一.微服务入门理论
微服务架构概述
微服务架构是一种架构模式,他提倡将单体应用程序划分成一组小的服务,服务之间互相协调,互相配合,为用户提供最终价值.每个服务运行在其独立的进程中,服务与服务之间采用轻量级的通信机制互相协作(通常是基于HTTP协议的RESTful API) .每个服务都围绕着一个具体业务进行构建,并且被独立部署到生产环境, 类生产环境等.另外,应当尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言,工具对其进行构建
SpringCloud简介
SpringCloud = 分布式微服务架构的一站式解决方案,是多种微服务架构落地技术的集合体,俗称微服务全家桶
SpringCloud技术栈
二.环境版本
版本常用软件pom
<!--springboot 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring cloud Hoxton.SR1-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring cloud alibaba 2.1.0.RELEASE-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
三.服务架构编码构建
核心:约定 > 配置 > 编码
IDEA新建project工作空间
-
new Project
<groupId>com.atguigu.springcloud</groupId> <artifactId>cloud2020</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging>
-
配置 Maven 版本
-
字符编码
-
注解生效激活
-
java编译版本选8
Rest微服务工程搭建
微服务第一个构建模板(cloud-provider-payment8001)
-
建modul
我更愿意选择springboot模板
-
改pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <groupId>com.atguigu.springcloud</groupId> <artifactId>cloud2020</artifactId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-provider-payment8001</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
-
写YML
server: port: 8001 spring: application: name: cloud-payment-service
-
主启动
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) // 不检测数据库信息 public class CloudProviderPayment8001Application { public static void main(String[] args) { SpringApplication.run(CloudProviderPayment8001Application.class, args); } }
-
业务类
这里做简单模拟
@RestController public class PaymentController { @GetMapping("/payment/get/{id}") public String getPayment(@PathVariable Integer id){ return UUID.randomUUID().toString() + " 订单" + id; } }
-
测试
http://localhost:8001/payment/get/3
-
总结
建modle --> 改pom --> 写YML–> 主启动 --> 业务类
热部署Devtools
-
导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency>
-
配置父工程POM
<build> <fileName>你自己的工程名字<fileName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> <addResources>true</addResources> </configuration> </plugin> </plugins> </build>
-
配置Compile
-
配置Registry
快捷键 : Ctrl + Shift + Alt + /
打勾
- 重启idea
cloud-consumer-order80 微服务消费者订单Module模块
-
**建modle **
-
改pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <groupId>com.atguigu.springcloud</groupId> <artifactId>cloud2020</artifactId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-consumer-order80</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> </dependencies> </project>
-
写YML
server:
port: 80
spring:
application:
name: cloud-consumer-order
- 主启动
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class CloudConsumerOrder80Application {
public static void main(String[] args) {
SpringApplication.run(CloudConsumerOrder80Application.class, args);
}
}
- 业务类
controller
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/getPayment/{id}")
public String getPayment(@PathVariable Integer id){
// 远程调用
return restTemplate.getForObject("http://localhost:8001/payment/get/" + id, String.class);
}
}
配置RestTemplateConfig
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
-
测试
http://localhost/consumer/getPayment/3
runDashBorad运行仪表盘
配置: https://blog.csdn.net/m18633778874/article/details/82687389
四.Eureka服务注册与发现
Eureka基础知识
-
什么是服务治理
Spring Cloud 封装了Netflix公司开发的Eureka模块来实现服务治理
在传统的RPC远程服务调用,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要服务治理,管理服务与服务之间的关系,可以实现服务调用 负载均衡 容错 等,实现服务注册与发现
-
什么是服务发现
Eureka采用了CS的设计模式,Eureka Server作为服务注册功能的服务器,他是服务注册中心.而系统的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接.这样系统的维护人员就可以通过Eureka Server来监控系统各个微服务是否正常运行
-
Eureka两组件
Eureka包含两个组件:Eureka Server 和 Eureka Client
Eureka Server 提供服务注册服务
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表将会存储所有可用的服务节点的信息,服务节点的信息下可以在界面中直观看到
Eureka Client 通过服务注册中心进行访问
是一个java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的,使用轮询(round-robin)负载算法的负载均衡器.在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒).如果Eureka Server在多个心跳周期没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)
单机Eureka构建步骤
IDEA生成Eureka服务注册中心(cloud-eureka-server7001)
-
**建modle **
-
**改pom **
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <groupId>com.atguigu.springcloud</groupId> <artifactId>cloud2020</artifactId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-eureka-server7001</artifactId> <dependencies> <!-- eureka--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> </dependencies> </project>
-
写YML
server: port: 7001 spring: application: name: cloud-payment-service
-
主启动
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableEurekaServer
public class CloudEurekaServer7001Application {
public static void main(String[] args) {
SpringApplication.run(CloudEurekaServer7001Application.class, args);
}
}
-
业务类
-
启动测试
http://localhost:7001/
现在没有注册客户端
EurekaClient端cloud-provider-payment8001 将注册进EurekaServer成为服务提供者provider
cloud-eureka-server7001
修改yml
server:
port: 7001
spring:
application:
name: eureka-server7001
eureka:
instance:
hostname: localhost
client:
# 是否将自己注册进EurekaServer 默认为true
register-with-eureka: false
# 是否从EurekaServer抓取已有的注册信息,默认为true.单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
cloud-provider-payment8001
-
导入Eureka client依赖
<!-- Eureka Client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
-
改yml
server: port: 8001 spring: application: name: cloud-payment-service # 添加 eureka: client: # 是否将自己注册进EurekaServer 默认为true register-with-eureka: true # 是否从EurekaServer抓取已有的注册信息,默认为true.单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetch-registry: true service-url: defaultZone: http://localhost:7001/eureka
-
主启动
加上@EnableEurekaClient 注解
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableEurekaClient public class CloudProviderPayment8001Application { public static void main(String[] args) { SpringApplication.run(CloudProviderPayment8001Application.class, args); } }
-
测试
先启动服务端,在启动客户端
EurekaClient端cloud-consumer-order80 将注册进EurekaServer成为服务提供者provider
同样的方法改写 cloud-consumer-order80
-
cloud-eureka-server7001 修改yml,上一步已经做过
-
导入eureka-client依赖
-
修改yml
eureka: client: # 是否将自己注册进EurekaServer 默认为true register-with-eureka: true # 是否从EurekaServer抓取已有的注册信息,默认为true.单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetch-registry: true service-url: defaultZone: http://localhost:7001/eureka
-
主启动 @EnableEurekaClient
Eureka集群环境搭建
-
新建cloud-eureka-server7002
参考: cloud-eureka-server7001
-
修改映射配置:必须设置
前面单机的时候 eureka注册中心实例名称 是localhost,现在是集群,不能三个实例都是localhost,这里复杂的办法是搞三个虚拟机,麻烦,这里有简单办法,直接配置本机hosts,来实现本机域名映射;
找到 C:\Windows\System32\drivers\etc 打开hosts,加配置
127.0.0.1 eureka7001.com 127.0.0.1 eureka7002.com
刷新hosts 文件 : ipconfig /flushdns
-
以前cloud-eureka-server7001 , cloud-eureka-server7002的yml配置文件
cloud-eureka-server7001
server: port: 7001 spring: application: name: eureka-server eureka: instance: hostname: localhost client: # 是否将自己注册进EurekaServer 默认为true register-with-eureka: false # 是否从EurekaServer抓取已有的注册信息,默认为true.单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetch-registry: false service-url: defaultZone: http://localhost:7001/eureka
cloud-eureka-server7002
server: port: 7002 spring: application: name: eureka-server eureka: instance: hostname: localhost client: # 是否将自己注册进EurekaServer 默认为true register-with-eureka: false # 是否从EurekaServer抓取已有的注册信息,默认为true.单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetch-registry: false service-url: defaultZone: http://localhost:7002/eureka
-
将eureka环境配置替换
eureka: instance: hostname: localhost client: # 是否将自己注册进EurekaServer 默认为true register-with-eureka: false # 是否从EurekaServer抓取已有的注册信息,默认为true.单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetch-registry: false service-url: defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
-
将cloud-consumer-order80注册到eureka服务中心
-
导入eureka-client依赖
-
改yml
eureka配置如上
-
主启动
加注解 @EnableEurekaServer
-
-
测试
先启动eureka 7001,7002 --> 启动8001–> 启动80
-
负载均衡测试
-
新建cloud-provider-payment8002,具体内容copy cloud-provider-payment8001
-
将二者的controller 修改为
@RestController public class PaymentController { @Value("${server.port}") private String serverPort; @GetMapping("/payment/get/{id}") public String getPayment(@PathVariable Integer id){ return UUID.randomUUID().toString() + " 订单" + id + "applicationName --> "+serverPort; } }
-
负载均衡
修改 cloud-consumer-order80
-
服务名称不能写死
@RestController public class ConsumerController { // 使用eureka服务名称 public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE"; @Autowired private RestTemplate restTemplate; @GetMapping("/consumer/getPayment/{id}") public String getPayment(@PathVariable Integer id){ return restTemplate.getForObject(PAYMENT_URL+"/payment/get/" + id, String.class); } }
-
赋予RestTemplate负载均衡的能力
@Configuration public class RestTemplateConfig { @Bean @LoadBalanced //赋予负载均衡的能力 public RestTemplate restTemplate(){ return new RestTemplate(); } }
-
可以测试了
先开启eureka服务端7001,7002 --> provide两个提供服务的客户端8001,8002–> 开启consumer消费客户端 80
效果: 8001/8002端口交替出现
Ribbon和Eureka整合后Consumer可以直接调用服务而不用再关心地址和端口号,且该服务还有负载功能了.
-
-
actuator微服务信息完善
显示服务名称和显示IP地址
- 当前问题
-
修改yml
eureka: client: # 是否将自己注册进EurekaServer 默认为true register-with-eureka: true # 是否从EurekaServer抓取已有的注册信息,默认为true.单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetch-registry: true service-url: defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 添加 ---------------------- instance: # 显示服务名称 instance-id: payment8001 # 访问路径可以显示ip地址 prefer-ip-address: true
类似的修改 8002
-
效果
服务发现Discovery
对于注册eureka里面的微服务,可以通过服务发现来获得该服务信息
-
修改cloud-provider-payment8001的Controller
@RestController public class PaymentController { @Value("${server.port}") private String serverPort; @Autowired private DiscoveryClient discoveryClient; @GetMapping("/payment/get/{id}") public String getPayment(@PathVariable Integer id){ return UUID.randomUUID().toString() + " 订单" + id + "applicationName --> "+serverPort; } @GetMapping("/payment/discovery") public Object discovery(){ List<String> services = discoveryClient.getServices(); for (String service : services) { System.out.println("****element"+service); } // 一个微服务的全部实例 List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE"); for (ServiceInstance instance : instances) { System.out.println(instance.getServiceId() + "\t"+instance.getHost() + "\t "+ instance.getPort()+"\t"+instance.getUri()); } return services; } }
-
8001 启动类
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableEurekaClient @EnableDiscoveryClient public class CloudProviderPayment8001Application { public static void main(String[] args) { SpringApplication.run(CloudProviderPayment8001Application.class, args); } }
-
自测
先启动EurekaServer 再启动8001
访问 http://localhost:8001/payment/discovery
-
效果
自我保护
-
概述
保护模式主要用于客户端和EurekaServer之间存在的网络分区场景下的保护.一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不在删除,也就是不会注销微服务
-
导致原因
某一时刻,一个微服务不可用了,Eureka不会立刻清理,依旧会对该服务的信息进行保存
-
如何禁止自我保护
-
注册中心eurekaServer端7001
出产默认,自我保护是开启的
eureka.server.enable-self-preservation=true
若是禁用改为false 即可
关闭效果
-
-
生产者客户端eurekaClient端8001
默认
# Eureka 客户端向服务端发送心跳的时间间隔,单位为秒(默认30)
eureka.instance.lease-renewal-interval-in-seconds=30 # 1
# Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90),超时剔除服务
eureka.instance.lease-expiration-duration-in-seconds=90 # 2
测试
先启动 7001 在启动8001 ,关闭8001 ,在服务端中应该会看到 8001 服务被删除的效果
五.Ribbon负载均衡调用
概述
是什么
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端 负载均衡的工具
主要功能是提供客户端的软件负载均衡算法和服务调用.Ribbon客户端组件提供的一系列完善的配置如 超时 重试等.简单地说 ,就是在配置文件中列出Load Balance(简称LB)后面所有的机器,Ribbon会自动的帮您基于某种规则(如简单轮询,随机连接等)去连接这些机器.我们很容易使用Ribbon实现自定义负载均衡的算法
能干嘛
一句话 : 负载均衡 + RestTemplate调用
Ribbon负载均衡的演示
架构
总结: Ribbon其实就是一个软负载均衡的客户端组件,它可以和其他所需请求的客户端结合使用,和Eureka结合只是其中的一个实例
pom说明
之前没有引入spring-cloud-start-ribbon也可以使用ribbon
猜测spring-cloud-start-netflix-eureka-client自带了spring-cloud-start-ribbon
RestTemplat的说明
getForObject 可以直接获取到结果(实体类)
getForEntity 获取ResponseEntity之后 调用getBody 也可以获取(实体类)对象,其返回的信息更加详细
Ribbon核心组件IRule
如何替换
-
修改cloud-consumer-order80
-
配置细节
- 新建pakage
-
主启动类添加@RibbonClient
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableEurekaClient // 添加 @RibbonClient @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class) public class CloudConsumerOrder80Application { public static void main(String[] args) { SpringApplication.run(CloudConsumerOrder80Application.class, args); } }
-
测试http://localhost/consumer/payment/get/31
六.OpenFeign服务接口
概述
是什么
Feign是一个生命式的web服务客户端,让编写的web服务客户端变得非常容易, 只需要创建一个接口并在接口上添加注解即可
能干嘛
Feign 旨在使编写Java Http客户端变得更容易
前面在使用Ribbon + RestTemplat时,利用RestTemplat对http请求的封装处理,形成了一套模板化的调用方法.但在实际开发中,由于对服务的调用可能不止一处,往往一个接口被多处调用,所以通常都会对每个微服务自行封装一些客户端老包装这些依赖服务的调用.所以,Feign再次基础上做了一个封装,由他来帮助我们定义和实现服务接口的定义.在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置他(以前使用DAO接口上面标注Mapper注解,现在是一个微服务上面标注一个Feign注解即可),即可完成对服务提供方接口的绑定,简化了使用Spring Cloud Ribbon时,自动封装服务调用客户端的开发量,
Feign集成了Ribbon
利用Ribbon维护Payment的服务列表信息,并通过轮询实现了客户端的负载均衡.而Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而简明的实现了服务调用
Feign和OpenFeign两者区别
OpenFeign使用步骤
接口+注解
微服务调用接口 + @FeignClient
使用步骤
-
新建cloud-consumer-feign-order80
Feign在消费端使用
-
写pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud2020</artifactId> <groupId>com.atguigu.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-consumer-feign-order80</artifactId> <description>订单消费者之feign</description> <dependencies> <!--openfeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--eureka client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--监控--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--热部署--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
-
改yml
server: port: 80 eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
-
主启动
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableEurekaClient // 去服务中心注册 @EnableFeignClients // 取开启feign ***** public class CloudConsumerFeignOrder80Application { public static void main(String[] args) { SpringApplication.run(CloudConsumerFeignOrder80Application.class, args); } }
-
业务类
业务逻辑接口 + @FeignClient配置调用provide服务
新建PaymentFeignService接口并添加注解@FeignClient
@Component @FeignClient(value = "CLOUD-PAYMENT-SERVICE") public interface PaymentFeignService { // 直接去copy对应服务类的controller方法 @GetMapping("/payment/get/{id}") String getPayment( @PathVariable("id") Integer id); // 注意这里不用加 }
-
测试
先启动2个eureka集群7001/7002
再启动2个微服务8001/8002
启动OpenFeign
http://localhost/consumer/payment/get/31
Feign自带负载均衡配置项
-
总结
OpenFeign超时控制
超时设置
演示: 修改8001 和 8002的的controller方法
@RestController
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/payment/get/{id}")
String getPayment( @PathVariable("id") Integer id){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return UUID.randomUUID().toString() + " 订单" + id + "applicationName --> "+serverPort;
}
}
测试:
Openfeign默认等待1秒中,超过后报错
是什么–OpenFeign默认支持Ribbon
默认Feign客户端只等待一秒钟,但是服务端处理需要超过1秒钟,导致Feign客户端不想等待了,直接返回报错.为了避免这样的情况,有时候我们需要设置Feign客户端的超时时间
可以在yml中进行配置
YML文件里需要开启OpenFeign客户端超市控制
server:
port: 80
eureka:
client:
register-with-eureka: false
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
# 设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
# 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间
ReadTimeout: 8000
# 指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 8000
测试
http://localhost/paymentFeign/get/3 这次响应比较慢
OpenFeign日志打印功能
是什么
Feign提供了日志打印功能,我们可以通过配置来调整日志级别,来了解Feign中的Http请求的细节
说白了就是 对Feign接口的调用情况进行监控和输出
日志级别
配置日志Bean
@Configuration
public class FeignConfig {
/**
* feignClient配置日志级别
*
* @return
*/
@Bean
public Logger.Level feignLoggerLevel() {
// 请求和响应的头信息,请求和响应的正文及元数据
return Logger.Level.FULL;
}
}
YML文件里需要开启日志的Feign客户端
server:
port: 80
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
# 设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
# 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间
ReadTimeout: 8000
# 指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 8000
logging:
level:
# feign日志以什么级别监控哪个接口
com.atguigu.springcloud.service.PaymentFeignService: debug
后台日志查看
测试
七.Hystrix熔断器
概述
分布式系统面临的问题
分布式系统面临的问题
复杂的分布式体系机构的应用程序 有数10个依赖关系,每个依赖关系在某些时候不可避免的失败
服务雪崩:
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C有调用其他微服务,这就是所谓的"扇出".
如果删除的链路上某个微服务的调用的响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓 雪崩效应
对于高流量的应用来说,单一的后端依赖可能会导致所有的服务上的所有资源在几秒钟内饱和.比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他资紧张,导致整个系统发生更多的级联故障.这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖的失败,不能取消整个应用程序或系统.
所以,
通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接受流量,然后这个问题的模块还调用了其他模块,这样就会发生 级联故障 或者 雪崩
是什么
Hystirx 是一个能够用于处理分布式系统的 延迟 和 容错 的开源库,在分布式系统里,许多依赖不可避免地会调用失败,比如超时,异常等.
Hystirx 能够保证在某一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障 ,以提供分布式系统的弹性.
"断路器"本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监测(类似熔断保险丝),向调用方返回一个预期,可处理的备选响应(Fall Back),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间 不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩
能干嘛
- 服务降级
- 服务熔断
- 接近实时的监控
Hystrix官宣,停更进维护
HyStrix重要概念
服务降级
- 服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,FallBack
- 那些情况会发生降级:
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量也会导致服务降级
服务熔断
类比保险丝达到最大服务访问后,直接拒绝访问,拉闸断电,然后调用服务降级的方法并返回友好提示
就是保险丝: 服务的降级–>进而熔断–> 恢复调用链路
服务限流
秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
站在巨人肩膀上
https://www.techgrow.cn/posts/2ed0fea6.html
八.Gateway新一代网关
概述简介
是什么
Spring Cloud GateWay使用的是Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架
能干嘛
反向代理 鉴权 流量控制 熔断 日志监控
微服务架构中的网关在哪里
三大核心概念
Route(路由):路由是构建网关的基本模块,他由ID,目标URL,一系列的断言和过滤器组成,如断言为true则匹配该路由
Predicate(断言):开发人员可以匹配http请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
Filter(过滤):指的是Spring框架中的GateWayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改
Gateway工作流程
入门配置
-
新建工程 cloud-gateway-gateway9527
-
pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <groupId>com.atguigu.springcloud</groupId> <artifactId>cloud2020</artifactId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-gateway-gateway9527</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> </dependencies> </project>
-
yml
server: port: 9527 spring: application: name: cloud-gateway cloud: gateway: discovery: locator: enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由 routes: - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名 #匹配后提供服务的路由地址 uri: http://localhost:8001 predicates: - Path=/payment/get/** # 断言,路径相匹配的进行路由 eureka: instance: hostname: cloud-gateway-service client: fetch-registry: true register-with-eureka: true service-url: defaultZone: http://eureka7001.com:7001/eureka
-
业务类 无
-
主启动
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableEurekaClient public class CloudGatewayGateway9527Application { public static void main(String[] args) { SpringApplication.run(CloudGatewayGateway9527Application.class, args); } }
-
测试
通过服务名实现动态
默认情况下GateWay会根据注册中心注册到服务列表,以注册中心上的微服务名为路径创建 动态路由进行转发,从而实现动态路由的功能
启动: 一个Eureka7001 + 两个服务提供者8001 / 8002
yml:
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由
routes:
- id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
#匹配后提供服务的路由地址
# uri: http://localhost:8001
uri: lb://CLOUD-PAYMENT-SERVICE
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
注意: 需要注意的是uri的协议lb,表示启用Gateway的负载均衡功能.
测试
交替效果
Predicate
根据predicate 进行匹配筛选
九.SpringCloud Config 分布式配置中心
概述
是什么
SpringCloud Config为微服务架构中的的微服务提供集中化的外部配置支持,配置服务器为各个不同微服务应用的所有环境提供了一了一个中心化的外部配置
怎么玩
SpringCloud Config分为服务端与客户端两部分
服务端也称为分布式配置中心 ,他是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密/解密信息等访问接口
客户端则是通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息配置服务器默认时采用git来存储配置信息,这样就有助于对环境进行版本管理,并可以通过git客户端工具来方便的管理和访问配置内容
能干嘛
集中管理配置文件
不同环境不同配置,动态化的配置更新,分环境部署比如dev/test/prod/beta/release
运行期间动态调整配置,不在需要每个服务器的机器上写配置文件,服务会向配置中心同意拉去配置自己的信息
当配置发生变化时,服务不再需要重启既可感知到配置的变化并应用新的配置
将配置信息以Rest接口暴露----post curl访问刷新即可
Config服务配置与测试
-
在gitee新建springcloud-config库
-
并配置一个applicaition-opt.yml文件
-
新建Module模块cloud-config-center-3344它即为Cloud的配置中心模块cloudCofing Center
-
pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud2020</artifactId> <groupId>com.atguigu.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-config-center-3344</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> </dependencies> </project>
-
yml
server: port: 3344 eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka7001.com:7001/eureka spring: application: name: cloud-config-center cloud: config: server: git: uri: https://gitee.com/zhouhongjie123/springcloud-config.git search-paths: - springcloud-config label: master
-
主启动
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableConfigServer @EnableEurekaClient public class CloudConfigCenter3344Application { public static void main(String[] args) { SpringApplication.run(CloudConfigCenter3344Application.class, args); } }}
-
修改映射文件
windows下修改hosts文件,增加映射
127.0.0.1 config-3344.com
-
测试
启动微服务3344
http://localhost:3344/master/application-opt.yml
Config客户端配置与测试
-
新建cloud-config-center-3355
-
pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <groupId>com.atguigu.springcloud</groupId> <artifactId>cloud2020</artifactId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-config-client-3355</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> </dependencies> </project>
-
bootstarp.yml
server: port: 3355 spring: application: name: config-client cloud: config: label: master #分支名称 name: application #配置文件名称 profile: opt #读取后缀名称 上述三个综合http://localhost:3344/master/application-opt.yml uri: http://localhost:3344 #配置中心的地址 eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka7001.com:7001/eureka
-
主启动
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableEurekaClient public class CloudConfigCenter3355Application { public static void main(String[] args) { SpringApplication.run(CloudConfigCenter3355Application.class, args); } }
-
业务类
@RestController public class ConfigController { @Value("${name}") private String name; @GetMapping("/config/getName") public String getName(){ return "myName ------------->" + name; } }
-
测试
先启动 7001 --> 3344 --> 3355
Config客户端之动态刷新
避免每次更新配置都要重启客户端微服务3355
动态刷新
-
修改3355模块
-
pom引入actuator监控
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
-
修改YML,暴露监控接口
server: port: 3355 spring: application: name: config-client cloud: config: label: master #分支名称 name: application #配置文件名称 profile: opt #读取后缀名称 上述三个综合http://localhost:3344/master/application-opt.yml uri: http://localhost:3344 #配置中心的地址 eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka7001.com:7001/eureka # 修改YML,暴露监控接口 management: endpoints: web: exposure: include: "*"
-
@RefreshScope业务类Controller修改
@RestController @RefreshScope public class ConfigController { @Value("${name}") private String name; @GetMapping("/config/getName") public String getName(){ return "myName ------------->" + name; } }
-
此时修改Gitee
http://localhost:3355/getServerPort 访问测试, ? 答案 没有改变
-
HOW
需要运维人员发送Post请求刷新3355 : 必须时Post
curl -X POST “http://localhost:3355/actuator/refresh”
- 测试
问题
假如有多个微服务客户端3355/3366/3377
每次微服务都要执行一次post请求,手动刷新
可否广播?一次通知,处处生效 ------> bus消息总线,希望之光
我们想大范围的自动刷新,求方法
十.SpringCloud Bus消息总线
概述
分布式自动刷新配置功能
SpringCloud Bus配合Springcloud Config使用可以实现配置的动态刷新
Bus支持两种消息代理:RabbitMQ和kafka
什么是总线
在微服务架构的系统中,通常会使用轻量级的消息代理来构建一个公用的消息主题,并让系统中的所有微服务实例都连接上来。由于该主题中产生的消息会被所有实例监听和消费,所以称为消息总线。在总线上的各个实例,都可以方便地广播一些需要让其他链接在该主题行的实例都知道的消息。基本原理:
ConfigClient实例都监听MQ中的同一个topic(默认是SpringcloudBus)。当一个服务刷新数据的时候,它会把这个信息放入到topic中,这样其它监听同一个topic的服务就能得到通知,然后去更新自身的配置。
RabbitMQ环境配置
下载安装RabbitMQ
SpringCloud Bus动态刷新全局广播
必须先具备良好的RabbitMQ环境
演示广播效果增加复杂度,再以3355为模板制作一个3366
-
新建cloud-config-client-3366
-
pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.atguigu.springcloud</groupId> <artifactId>cloud2020</artifactId> <version>1.0-SNAPSHOT</version> </parent> <groupId>com.atguigu</groupId> <artifactId>cloud-config-client-3366</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>cloud-config-client-3366</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
-
bootstrap.yml
server: port: 3366 spring: application: name: config-client cloud: config: label: master #分支名称 name: application #配置文件名称 profile: opt #读取后缀名称 上述三个综合http://localhost:3344/master/application-opt.yml uri: http://localhost:3344 #配置中心的地址 eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://eureka7001.com:7001/eureka # 修改YML,暴露监控接口 management: endpoints: web: exposure: include: "*"
-
主启动
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) @EnableEurekaClient public class CloudConfigClient3366Application { public static void main(String[] args) { SpringApplication.run(CloudConfigClient3366Application.class, args); } }
-
controller
@RestController @RefreshScope public class ConfigController { @Value("${name}") private String name; @GetMapping("/config/getName") public String getName(){ return "myName ------------->" + name; } }
设计思想
方案一: 利用消息总线触发一个客户端/bus/refresh,而刷新所有客户端的配置
方案二:利用消息总线触发一个服务端ConfigServer的/bus/refresh端点,而刷新所有客户端的配置
方案二的架构显然更加合适,方案一不适合的原因如下:
- 打破了微服务的职责单一性,因为微服务本身是业务模块,它不应该承担配置刷新的职责
- 打破了微服务各个节点的对等性
- 有一定的局限性。例如:微服务在迁移时,它的网络地址常常会发生变化,此时如果想要做到自动刷新,那就回增加更多的的修改
给3344配置中心服务端添加消息总线支持
pom
添加
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
yml
server:
port: 3344
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
spring:
application:
name: cloud-config-center
cloud:
config:
server:
git:
uri: https://gitee.com/zhouhongjie123/springcloud-config.git
search-paths:
- springcloud-config
label: master
# 配置 rabbitMQ
rabbitmq:
host: 101.34.172.88
port: 5672
username: admin
password: 123
virtual-host: ojhost
#rabbitmq相关配置,暴露bus刷新配置的端点
management:
endpoints: #暴露bus刷新配置的端点
web:
exposure:
include: 'bus-refresh'
给3355配置中心服务端添加消息总线支持
pom
添加
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
yml
server:
port: 3355
spring:
application:
name: config-client
cloud:
config:
label: master #分支名称
name: application #配置文件名称
profile: opt #读取后缀名称 上述三个综合http://localhost:3344/master/application-opt.yml
uri: http://localhost:3344 #配置中心的地址
# 配置 rabbitMQ
rabbitmq:
host: localhost
port: 5672
username: admin
password: 123
virtual-host: ojhost
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
# 修改YML,暴露监控接口
management:
endpoints:
web:
exposure:
include: "*"
给3366配置中心服务端添加消息总线支持
同 3355 配置
测试
运维工程师
修改GitHub上的配置文件增加版本
发送POST请求 :
curl -X POST “http://localhost:3344/actuator/bus-refresh”
一次发送处处生效
配置中心
http://localhost:3344/master/application-opt.yml
客户端
http://localhost:3355/config/getName
http://localhost:3366/config/getName
获取配置信息。发现都已经刷新了
一次修改,广播通知,处处生效
SpringCloud Bus动态刷新定点通知
指定具体某一实例生效而不是全部
公式:http://lcoalhost:配置中心的端口号/actuator/bus-refresh/{destination}
我们只想刷新运行在3355端口上的config-client为例:
- 只通知3355 不通知3366
- curl -X POST “http://localhost:3344/actuator/bus-refresh/config-client:3355”
访问测试