08-Hystrix
本笔记来源于:尚硅谷SpringCloud框架开发教程(SpringCloudAlibaba微服务分布式架构丨Spring Cloud)
b站视频
文章来自:
https://github.com/OT-mt/cloud2020/tree/master/springcloud-2%E5%B0%9A%E7%A1%85%E8%B0%B7%E5%91%A8%E9%98%B3-2020
脑图
简介
分布式系统面临的问题
复杂分布式系统中的应用程序有数十个依赖关系,每个依赖关系在某些时候不可避免的失败。
多个微服务之间调用时,假设 A 调B和C,B和C又调其他微服务,就是所谓的扇出。当扇出的链路上某个微服务响应时间过长或不可用对A的调用就会占用越来越多的资源,进而引起系统崩溃 ,所谓的雪崩效应。
是什么
Hystrix 是处理分布式系统的延迟和容错的开源库,保证一个依赖出现问题时不会导致整体服务失败,避免级联故障,以提高分布式系统弹性。
断路器本身是一种开关装置,当某个服务单元发生故障后,通过断路器的故障监控,向调用方返回一个符合预期的可处理的备选响应,而不是长时间的等待或抛出调用方法无法处理的异常 。
官网资料
https://github.com/Netflix/Hystrix
停更
Hystrix重要概念
服务降级
- 服务器忙,请稍后重试,不让客户端等待并立即返回一个友好的提示。
- 哪些情况会导致服务降级
- 类比保险丝达到最大服务访问时,直接拒绝访问,拉闸限电,然后调用服务降级的方法返回友好提示。
- 服务降级->进而熔断->恢复调用链路
服务限流
- 秒杀高并发等操作,严禁一窝蜂过来拥挤,一秒N个有序进行。
案例
准备cloud-provider-hystrix-payment8001
将 7001换成单机版构建
- 新建
- pom
1
2
3
4<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency> - yml
1
2
3
4
5
6
7
8
9
10
11
12server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defauleZone: http://eureka7001.com:7001/eureka - 主启动
- 业务类
- 测试
先启动7001,在启动8001测试两个方法,全部正常使用Jmeter模拟高并发
51集
高并发打到http://localhost:8001/timeout/1上使用后
http://localhost:8001/ok/1 也有延迟
上述还是8001单独测试,如果外部消费者80也来访问,那么消费者只能干等,最终导致消费端80不满意,服务端8001直接被拖死。加入80
- 建moudle cloud-consumer-feign-hystrix-order80
- pom
- yml
- 主启动类
- service
- controller
- 测试
- 超时导致服务器变慢(转圈)->超时不再等待
- 出错(宕机或程序运行时出错)->出错要有兜底
- 解决
- 8001 超时,调用者 80 不能一直等待,必须有服务降级
- 服务 8001 宕机,调用者 80 不能一直等待,必须有服务降级
- 服务 8001 OK ,调用者自己出故障或有自我要求(自己的等待时间小于服务提供的时间。),自己降级处理
服务降级
超时:8001
servicemain1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23@Service
public class PaymentService {
public String paymentInfo_OK(Integer id){
return "线程池:"+Thread.currentThread().getName()+"ok"+id;
}
// 设置超过 3 秒采用服务降级
@HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public String paymentInfo_Timeout(Integer id){
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:"+Thread.currentThread().getName()+"Timeout"+id;
}
// 异常后调用的方法
public String paymentInfo_TimeoutHandler(Integer id){
return "服务超时,调用服务降级成功";
}
}1
@EnableCircuitBreaker
运行异常:8001
service无论是运行异常还是超时都有兜底策略1
2
3
4
5
6
7
8
9
10
11
12@HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public String paymentInfo_Timeout(Integer id){
int a = 10/0;
// try {
// TimeUnit.SECONDS.sleep(5);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
return "线程池:"+Thread.currentThread().getName()+"Timeout"+id;
}订单侧:80,先去掉8001的超时和异常
==既可以配在客户端也可以配在服务端,一般建议放在客户端==
- yml
1
2
3feign:
hystrix:
enabled: true - controller
改为同 8001 的 service 一样 - 主类
添加 @EnableHystrix 注解 - 注意:降级处理方法参数列表必须跟异常方法一样
全面服务降级
存在问题
- 每一个方法都需要配置一个降级方法
- 和业务代码在一起
解决
- 第一个问题
controller1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27@RestController
@Slf4j
// 1. 添加注解,标注全局服务降级方法
@DefaultProperties(defaultFallback = "paymentGlobalFallBack")
public class OrderController {
@Resource
private PaymentHystrixService service;
// 3. 写 @HystrixCommand单不指定具体方法
@GetMapping("/ok/{id}")
@HystrixCommand
public String paymentInfo_OK(@PathVariable Integer id) {
int a = 10/0;
return service.paymentInfo_OK(id);
}
public String paymentInfo_TimeoutHandler(Integer id){
return "80异常,降级处理";
}
// 2. 定义全局服务降级方法
// 下面是全局 fallback
public String paymentGlobalFallBack(){
return "80:获取异常,调用方法为全局fallback";
}
} - 第二个问题
- 找到注解 @FeignClient 对应的接口
- 再写一个类实现该接口,对降级方法进行处理
1
2
3
4
5
6@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallBackService.class)
public interface PaymentHystrixService {}
@Component
public class PaymentFallBackService implements PaymentHystrixService {} - 测试在 8001 内加异常,或使 8001 宕机 ,返回异常处理
服务熔断
简介
类比保险丝,达到最大访问后直接拒绝访问,拉闸限电,然后调用服务降级。当检测==到该节点微服务调用正常后,恢复调用链路。==
当失败的调用达到一定阈值,缺省是5s内20次调用失败,就会启动熔断机制。熔断机制的注解是,@HystrixCommand是什么
https://martinfowler.com/bliki/CircuitBreaker.html实践:8001
- service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// 服务熔断
@HystrixCommand(fallbackMethod = "paymentInfo_Circuit",commandProperties = {
@HystrixProperty(name="circuitBreaker.enabled",value = "true"),//是否开启断路器
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "10"),// 请求次数
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value = "10000"),// 时间窗口期
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "60")// 失败率
// 加起来就是在10s内的10次请求中如果失败超过6次进入服务熔断
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
if (id<0){
throw new RuntimeException("id 不能为负数");
}
String serialNumber = IdUtil.simpleUUID();
return "调用成功:"+serialNumber;
}
public String paymentInfo_Circuit(Integer id){
return "id不能为负数:"+id;
} - controller
1
2
3
4
5
6
7// 服务熔断
@GetMapping("/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
String result = paymentService.paymentCircuitBreaker(id);
log.info("************"+result);
return result;
} - 结果
一直输入id为负数,达到失败率后即使输入id为正数也进入错误页面。总结
熔断类型
- 熔断打开
请求不再进行调用当前服务,内部设有时钟一般为 MTTR,当打开时长达时钟则进入半熔断状态 - 熔断关闭
熔断关闭不会对服务进行熔断 - 熔断半开
根据规则调用当前服务,符合规则恢复正常,关闭熔断什么时候打开
设计三个参数:时间窗,请求总阈值,错误百分比阈值 - 快照时间窗:默认为最近的10s
- 请求总数阈值:必须满足请求总阈值才有资格熔断。默认为20。意味着在10s内,如果命令调用次数不足20次,即使所有请求都超时或其他原因失败断路器都不会打开
- 错误百分比阈值:在快照时间窗内请求总数超过阈值,且错误次数占总请求次数的比值大于阈值,断路器将会打开
web界面图形化展示Dashboard
搭建
- 建 moudle
cloud-consumer-hystrix-dashboard9001 - pom
1
2
3
4<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency> - yml
只需要配置端口号就行 - 启动类
加注解@EnableHystrixDashboard - 测试
http://localhost:9001/hystrix有页面即为成功
使用
注意
- 注意:依赖于actuator,要监控哪个接口,哪个接口必须有这个依赖
- 业务模块需要添加bean
1
2
3
4
5
6
7
8
9@Bean
public ServletRegistrationBean getServlet(){
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}使用
- 进行8001 的访问查看对应页面变化
- 页面状态
- 七色
对应不同状态 - 一圈
对应访问量 - 一线
访问趋势
- 七色
08-Hystrix
http://yuanql.top/2023/06/27/13_SpringCloud/springcloud-2尚硅谷周阳-2020/08-Hystrix/