分布式微服务的 Hystrix断路器

2022-07-30,,,,

Hystrix断路器

服务雪崩
  • 多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他微服务,这就是所谓的 “扇出”,如果扇出的链路上某个微服务的调用时间过长或者不可用,对微服务A的调用就会占有越来越多的系统资源,进而引起系统奔溃,所谓的 “雪崩效应”。
  • 对于高流量的应用来说,单一的后端依赖坑会导致所有服务器上的所有资源都在几秒内饱和。更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发送更多的级联故障。这些表示需要对故障和延迟进行隔离和管理,以便单个依赖关系失败,不能导致整个应用程序奔溃
  • 所以 通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接收-流量,然后这个有问题的模块还调用了其他模块,这样就会发生级联故障,或者叫雪崩。

简介
  • Hystrix 是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能保证一个依赖出问题的情况下,不会导致整个服务失败,避免级联故障,以提高分布式系统的弹性。

  • “断路器 ” 本身是一种开关装置,当某个服务单元发生故障的时候,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期,可处理的备选响应(FallBack),而不是长时间的等待护着抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免故障在分布式系统中蔓延

  • 功能:服务降级,服务熔断,接近实时的监控,限流,隔离等

Hystrix重要概念
服务降级(fallback)

提供者和消费者都可以进行服务降级。(一般都是放在客户端(消费者))

服务熔断(break)

服务限流(flowlimit)


新建客户端 cloud-provider-hystrix-payment8001 模块

  1. 引入POM (在客户端(消费者使用))
<!-- hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> 
  1. yml配置
server: port: 8001 spring: application: name: cloud-provider-hystrix-payment
	

eureka: client: register-with-eureka: true fetch-registry: true service-url: #单机版
      defaultZone: http://localhost:7001/eureka 
  1. 主启动类
@EnableEurekaClient @SpringBootApplication public class PaymentHystrixMain8001 { public static void main(String[] args) { SpringApplication.run(PaymentHystrixMain8001.class, args); } } 
  1. service
@Service public class PaymentService { //正常访问方法 public String paymentInfo_OK(Integer id){ return "线程池:" + Thread.currentThread().getName() + "\tpaymentInfo_OK,id:" + id; } //超时访问方法 public String paymentInfo_TimeOut(Integer id){ int timeNumber = 3; try { TimeUnit.SECONDS.sleep(timeNumber); } catch (InterruptedException e) { e.printStackTrace(); } return "线程池:" + Thread.currentThread().getName() + "\tpaymentInfo_TimeOut,id:" + id + ",耗时:" + timeNumber + "秒"; } } 
  1. controller
@Slf4j @RestController public class PaymentController { @Resource PaymentService paymentService; @Value("${server.port}") //spring的@Value注解 private String ServerPort; @GetMapping("/payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id") Integer id){ String result = paymentService.paymentInfo_OK(id); log.info("******result:" + result); return result; } @GetMapping("/payment/hystrix/timeout/{id}") public String paymentInfo_TimeOut(Integer id){ String result = paymentService.paymentInfo_OK(id); log.info("******result:" + result); return result; } } 
  1. 启动7001和8001

http://localhost:8001/payment/hystrix/ok/1

== http://localhost:8001/payment/hystrix/timeout/1==

进行高并发测试:


然后去访问http://localhost:8001/payment/hystrix/ok/1,访问速度变慢了。


新建消费者

新建消费者模块 cloud-consumer-feign-hystrix-order80

  1. 引入POM
<dependencies> <!-- openfeign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- hystrix --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!--eureka client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- 引用自己定义的api通用包,可以使用Payment支付Entity --> <dependency> <groupId>com.angenin.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </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.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> 
  1. yml文件
server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://localhost:7001/eureka

#需要加上,否则会报错
ribbon: ReadTimeout: 4000 ConnectTimeout: 4000 
  1. 主启动类
@EnableEurekaClient @EnableFeignClients @SpringBootApplication public class OrderHystrixMain80 { public static void main(String[] args) { SpringApplication.run(OrderHystrixMain80.class, args); } } 
  1. service
@Component @FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT") public interface PaymentHystrixService { @GetMapping("/payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id") Integer id); @GetMapping("/payment/hystrix/timeout/{id}") public String paymentInfo_TimeOut(@PathVariable("id") Integer id); } 
  1. controller
@Slf4j @RestController public class OrderHystrixController { @Resource private PaymentHystrixService paymentHystrixService; @GetMapping("/consumer/payment/hystrix/ok/{id}") public String paymentInfo_OK(@PathVariable("id") Integer id){ String result = paymentHystrixService.paymentInfo_OK(id); return result; } @GetMapping("/consumer/payment/hystrix/timeout/{id}") public String paymentInfo_TimeOut(@PathVariable("id") Integer id){ String result = paymentHystrixService.paymentInfo_TimeOut(id); return result; } } 
  1. 启动jmeter,然后再进行测试

安装JMeter
JMeter下载地址:http://jmeter.apache.org/download_jmeter.cgi

下载tgz和zip都可以:


启动jmeter 测试


故障现象、导致原因以及解决

现象:

解决:

服务降级

提供者 (8001模块)
  1. 修改8001中PaymentService的paymentInfo_TimeOut方法,并添加paymentInfo_TimeOutHandler方法:
 @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = { //设置自身超时调用时间的峰值为3秒,峰值内可以正常运行,超过了需要有兜底的方法处理,服务降级fallback @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000") }) public String paymentInfo_TimeOut(Integer id){ int timeNumber = 5; //int i = 1 / 0; try { TimeUnit.SECONDS.sleep(timeNumber); } catch (InterruptedException e) { e.printStackTrace(); } return "线程池:" + Thread.currentThread().getName() + "\tpaymentInfo_TimeOut,id:" + id + ",耗时:" + timeNumber + "秒"; } public String paymentInfo_TimeOutHandler(Integer id){ return "8001提供者,线程池:" + Thread.currentThread().getName() + "\tpaymentInfo_TimeOutHandler系统繁忙,请稍后再试,id:" + id; } 
  1. 然后在8001的主启动类上添加@EnableCircuitBreaker注解,启用断路器。
消费者 (80模块)

  1. yml添加 :
feign: hystrix: enabled: true 
  1. 在主启动类添加@EnableHystrix注解。

  2. 修改OrderHystrixController的paymentInfo_TimeOut方法,并添加paymentTimeOutFallbackMethod方法:

 @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500") }) @GetMapping("/consumer/payment/hystrix/timeout/{id}") public String paymentInfo_TimeOut(@PathVariable("id") Integer id){ String result = paymentHystrixService.paymentInfo_TimeOut(id); return result; } public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){ return "消费者80,支付系统繁忙"; } 

启动7001,8001,80,http://localhost/consumer/payment/hystrix/timeout/1(如果是提供者那边出问题,并且消费者设置了fallback,会优先进入消费者的fallback)


代码膨胀的解决办法:


服务熔断

  1. 在8001的PaymentService中添加
在这里插入代码片 @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = { @HystrixProperty(name = "circuitBreaker.enabled", value = "true"), //开启断路器 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), //请求总数阈值(默认20) @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), //休眠时间窗口期(休眠多久进入半开模式(单位毫秒,默认5秒)) @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"), //请求次数的错误率达到多少跳闸(百分率%,默认50%) }) public String paymentCircuitBreaker(@PathVariable("id") Integer id) { if(id < 0){ throw new RuntimeException("****id 不能为负数"); } String serialNumber = IdUtil.simpleUUID(); return Thread.currentThread().getName() + "\t" + "调用成功,流水号:" + serialNumber; } public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){ return "id 不能为负数,请稍后再试, id: " + id; } 
  1. 在8001的PaymentController中添加
@GetMapping("/payment/circuit/{id}") public String paymentCircuitBreaker(@PathVariable("id") Integer id){ String result = paymentService.paymentCircuitBreaker(id); log.info("******result:" + result); return result; } 
  1. 启动7001和8001
    http://localhost:8001/payment/circuit/-1(输入超过6次进入熔断)

  • 熔断10秒内就算是正确的请求也返回错误信息。
  • 10秒后进入半开模式,对请求进行处理,此时如果是正确的请求,那么就关闭熔断,否则再次进入熔断,10秒后再次开启半开模式,对请求进行处理,直到半开模式处理到正确请求。

总结

https://martinfowler.com/bliki/CircuitBreaker.html

  • 我的总结:如果请求次数的错误率超过指定值,开启熔断,经过一段时间后,变为半开模式,然后放进一个请求进行处理,如果请求处理成功,关闭熔断;如果还是报错,继续进入熔断,再经过一段时间后,变为半开模式,再进行对下一个请求进行处理,一直在熔断,半开模式来回切换,直到请求成功,关闭熔断。
  • 官网步骤:
  • 断路器在什么情况下开始起作用:

  • 断路器开启或关闭的条件:

    断路器打开之后:

服务限流

会在后面高级篇alibaba的Sentinel讲解。

Hystrix工作流程:

服务监控HystrixDashboard

简介


仪表盘9001

  1. 新建模块 cloud-consumer-hystrix-dashboard9001

  2. pom

<dependencies> <!-- hystrix仪表盘图形化 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>com.angenin.springcloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </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.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> 
  1. http://localhost:9001/hystrix


断路器演示(服务监控hystrixDashboard)
注意:所有微服务提供者都需要在pom中引入监控依赖。

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> 

在8001的主启动类中添加:

 /**
    * 此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
    * ServletRegistrationBean因为SpringBoot的默认路径不是 “/hystrix.stream"
    * 只要在自己的项目里配置上下的servlet就可以了
    */ @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; } 

在浏览器输入http://localhost:8001/payment/circuit/1和http://localhost:8001/payment/circuit/-1

本文地址:https://blog.csdn.net/qq_41977838/article/details/108207975

《分布式微服务的 Hystrix断路器.doc》

下载本文的Word格式文档,以方便收藏与打印。