简介
分布式系统面临的问题
复杂分布式系统中的应用程序有数十个依赖关系,每个依赖关系在某些时候不可避免的失败。
多个微服务之间调用时,假设 A 调B和C,B和C又调其他微服务,就是所谓的扇出。当扇出的链路上某个微服务响应时间过长或不可用对A的调用就会占用越来越多的资源,进而引起系统崩溃 ,所谓的雪崩效应。
是什么
Hystrix 是处理分布式系统的延迟和容错的开源库,保证一个依赖出现问题时不会导致整体服务失败,避免级联故障,以提高分布式系统弹性。
断路器本身是一种开关装置,当某个服务单元发生故障后,通过断路器的故障监控,向调用方返回一个符合预期的可处理的备选响应,而不是长时间的等待或抛出调用方法无法处理的异常 。
官网资料
https://github.com/Netflix/Hystrix
停更
Hystrix重要概念
服务降级
- 服务器忙,请稍后重试,不让客户端等待并立即返回一个友好的提示。
- 哪些情况会导致服务降级
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量打满
服务熔断
- 类比保险丝达到最大服务访问时,直接拒绝访问,拉闸限电,然后调用服务降级的方法返回友好提示。
- 服务降级->进而熔断->恢复调用链路
服务限流
- 秒杀高并发等操作,严禁一窝蜂过来拥挤,一秒N个有序进行。
案例
准备cloud-provider-hystrix-payment8001
将 7001换成单机版
构建
- 新建
- pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- yml
server:
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
- 测试
- 正常情况下
- http://localhost:8001/ok/1
- http://localhost:8001/timeout/1
- http://localhost/ok/1
- 通过80 访问的非常快
- 高并发打到8001端口时,80端口也会非常慢
- 正常情况下
如何解决
- 超时导致服务器变慢(转圈)->超时不再等待
- 出错(宕机或程序运行时出错)->出错要有兜底
- 解决
- 8001 超时,调用者 80 不能一直等待,必须有服务降级
- 服务 8001 宕机,调用者 80 不能一直等待,必须有服务降级
- 服务 8001 OK ,调用者自己出故障或有自我要求(自己的等待时间小于服务提供的时间。),自己降级处理
服务降级
超时:8001
service
@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 "服务超时,调用服务降级成功";
}
}
main
@EnableCircuitBreaker
运行异常:8001
service
@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
feign:
hystrix:
enabled: true
- controller
改为同 8001 的 service 一样 - 主类
添加 @EnableHystrix 注解 - 注意:降级处理方法参数列表必须跟异常方法一样
全面服务降级
存在问题
- 每一个方法都需要配置一个降级方法
- 和业务代码在一起
解决
- 第一个问题
controller@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 对应的接口
- 再写一个类实现该接口,对降级方法进行处理
@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
// 服务熔断
@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
// 服务熔断
@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
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
- yml
只需要配置端口号就行 - 启动类
加注解@EnableHystrixDashboard - 测试
http://localhost:9001/hystrix有页面即为成功
使用
注意
- 注意:依赖于actuator,要监控哪个接口,哪个接口必须有这个依赖
- 业务模块需要添加bean
@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;
}
使用
![](imgs/dashboard.png)
- 进行8001 的访问查看对应页面变化
- 页面状态
- 七色
对应不同状态 - 一圈
对应访问量 - 一线
访问趋势
- 七色