1 前言

  • 我们知道,当请求失败,被拒绝,超时的时候,都会进入到降级方法中。但是进入降级方法并不意味着断路器已经被打开了。此时我们需要Hystrix的监控平台来查看断路器的状态。

2 Hystrix的监控平台

2.1 概述

  • 除了实现容错功能,Hystrix还提供了近乎实时的监控,HystrixCommand和HystrixObervableCommand在执行的时候,会生成执行结果和运行指标。比如每秒的请求数量、成功数量等待。这些状态会暴露在Actuator提供的/health端点中。
  • 导入相关jar包的Maven坐标:
  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-actuator</artifactId>
  4. </dependency>
  5. <!-- openfeign -->
  6. <dependency>
  7. <groupId>org.springframework.cloud</groupId>
  8. <artifactId>spring-cloud-starter-openfeign</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.cloud</groupId>
  12. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  13. </dependency>
  • 在启动类上开启Hystrix:
  1. package com.sunxiaping;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
  5. import org.springframework.cloud.openfeign.EnableFeignClients;
  6. @SpringBootApplication
  7. @EnableFeignClients //开启OpenFeign的支持
  8. @EnableCircuitBreaker //开启Hystrix
  9. public class Order9007Application {
  10. public static void main(String[] args) {
  11. SpringApplication.run(Order9007Application.class, args);
  12. }
  13. }
  • 在application.yml中将所有的端点打开:
  1. # 配置hystrix
  2. hystrix:
  3. command:
  4. default:
  5. execution:
  6. isolation:
  7. thread:
  8. timeoutInMilliseconds: 6000 # 默认的连接超时时间为1秒,如果1秒没有返回数据,就自动触发降级逻辑
  9. feign:
  10. hystrix: # 开启Feign中的Hystrix
  11. enabled: true
  12. # 暴露所有端点
  13. management:
  14. endpoints:
  15. web:
  16. exposure:
  17. include: '*'

实时监控数据.png

2.2 搭建Hystrix DashBoard监控

  • Hystrix官方提供了基于图形化的DashBoard(仪表盘)监控平台,用来直观的展示系统的运行状态。Hystrix仪表盘可以显示每个断路器的状态。
  • 导入依赖:
  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-actuator</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.cloud</groupId>
  7. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  8. </dependency>
  9. <dependency>
  10. <groupId>org.springframework.cloud</groupId>
  11. <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
  12. </dependency>
  • 在启动类上添加@EnableHystrixDashboard注解以激活仪表盘:
  1. package com.sunxiaping;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
  5. import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
  6. import org.springframework.cloud.openfeign.EnableFeignClients;
  7. @SpringBootApplication
  8. @EnableFeignClients //开启OpenFeign的支持
  9. @EnableCircuitBreaker //开启Hystrix
  10. @EnableHystrixDashboard // 激活仪表盘
  11. public class Order9007Application {
  12. public static void main(String[] args) {
  13. SpringApplication.run(Order9007Application.class, args);
  14. }
  15. }

搭建Hystrix DashBoard监控.png

2.3 断路器聚合监控Turbine

2.3.1 概述

  • 在微服务架构体系中,每个服务都需要配置Hystrix DashBoard监控。如果每次只能查看单个实例的监控数据,就需要不断切换监控地址,这显然很不方便。要想看整个系统的Hystrix DashBoard数据就需要用到Hystrix Turbine。Turbine是一个聚合Hystrix监控数据的工具,它可以将所有相关微服务的Hystrix监控数据聚合到一起,方便使用,引入Turbine后,整个监控系统架构如下:

Turbine.jpg

2.3.2 搭建Turbine Server

  • 服务熔断Hystrix高级(已过时) - 图4创建一个工程,并引入相关jar包的Maven坐标:
  • 修改部分:
  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.cloud</groupId>
  7. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  8. </dependency>
  9. <dependency>
  10. <groupId>org.springframework.cloud</groupId>
  11. <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
  12. </dependency>
  • 完整部分:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns="http://maven.apache.org/POM/4.0.0"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>spring_cloud_demo</artifactId>
  7. <groupId>org.sunxiaping</groupId>
  8. <version>1.0</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>hystrix_turbine7004</artifactId>
  12. <dependencies>
  13. <dependency>
  14. <groupId>org.springframework.cloud</groupId>
  15. <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.springframework.cloud</groupId>
  19. <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework.cloud</groupId>
  23. <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-web</artifactId>
  28. </dependency>
  29. </dependencies>
  30. </project>
  • 服务熔断Hystrix高级(已过时) - 图5配置多个微服务的hystrix监控。
  • 在application.yml的配置文件中开启turbine并进行相关配置:
  1. server:
  2. port: 7004
  3. spring:
  4. application:
  5. name: service-turbine # 微服务的名称
  6. # 配置 eureka
  7. eureka:
  8. instance:
  9. # 主机名称:服务名称修改,其实就是向eureka server中注册的实例id
  10. instance-id: service-turbine:${server.port}
  11. # 显示IP信息
  12. prefer-ip-address: true
  13. client:
  14. service-url: # 此处修改为 Eureka Server的集群地址
  15. defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
  16. turbine:
  17. # 要监控的微服务列表,多个用,隔开
  18. app-config: service-order
  19. cluster-name-expression: "'default'"
  • 服务熔断Hystrix高级(已过时) - 图6配置启动类:
  1. package com.sunxiaping.turbine;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
  5. import org.springframework.cloud.netflix.turbine.EnableTurbine;
  6. @SpringBootApplication
  7. @EnableHystrixDashboard
  8. @EnableTurbine
  9. public class Turbine7004Application {
  10. public static void main(String[] args) {
  11. SpringApplication.run(Turbine7004Application.class, args);
  12. }
  13. }

hystrix dashboard Unable to connect to Command Metric Stream解决办法.png

  • 需要在SpringBoot中配置HystrixMetricsStreamServlet:
  1. package com.sunxiaping.config;
  2. import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
  3. import org.springframework.boot.web.servlet.ServletRegistrationBean;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. @Configuration
  7. public class SpringConfig {
  8. @Bean
  9. public ServletRegistrationBean getServlet() {
  10. HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
  11. ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
  12. registrationBean.setLoadOnStartup(1);
  13. registrationBean.addUrlMappings("/hystrix.stream");
  14. registrationBean.setName("HystrixMetricsStreamServlet");
  15. return registrationBean;
  16. }
  17. }

3 断路器的状态

3.1 概述

  • 断路器的状态有三种:CLOSEDOPENHALF_OPEN。断路器默认CLOSED状态,当触发熔断后状态变更为OPEN状态,在等待指定的时间,Hystrix会放请求检测服务是否开启,这期间断路器的状态会由OPEN状态变为HALF_OPEN状态,一旦探测服务可用则将断路器的状态由HALF_OPEN变为CLOSED来关闭断路器。

断路器的状态.png

  • CLOSED:关闭状态,所有请求都能够正常访问。代理类维护了最近调用失败的次数,如果某次调用失败,则失败次数加1。如果最近失败次数超过了在给定时间内允许失败的阈值,则代理类切换到OPEN(打开)状态。此时代理开启了一个超时时钟,当该时钟超过了该时间,则切换到HALF_OPEN(半打开)状态。该超时是吉恩的设定给了系统一次机会来修正导致调用失败的错误。
  • OPEN:打开状态,所有请求都会被降级。Hystrix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器完全关闭。默认失败比例的阈值是50%,请求次数最少不低于20次。
  • HALF_OPEN:半打开状态,OPEN的状态不是永久的,打开后进行休眠时间(默认是5秒)。随后断路器会自动进入半打开状态,此时会释放一个请求通过,如果这个请求是健康的,则关闭断路器,否则继续保持打开,再次进行5秒休眠计时。

总结:

  • 服务熔断Hystrix高级(已过时) - 图10Hystrix正常启动的时候,断路器的状态是CLOSED状态(所有的请求都可以正常访问)。
  • 服务熔断Hystrix高级(已过时) - 图11默认情况下,当请求次数大于20次,且存在50%的失败概率的时候,将自动触发熔断,断路器的状态变为OPEN状态(所有的请求都会进入到降级方法中)。
  • 服务熔断Hystrix高级(已过时) - 图12默认情况下,会维持OPEN状态一段时间(5s),进入到半打开状态(尝试释放一个请求到远程微服务发起调用)。如果释放的请求可以正常访问,就会关闭断路器(断路器的状态由HALF_OPEN状态变为CLOSED状态)。如果释放的请求不能访问,则将状态由HALF_OPEN状态变为OPEN状态。

3.2 测试断路器的工作状态

3.2.1 环境准备

  • ①在订单系统中加入如下的逻辑:
    • 服务熔断Hystrix高级(已过时) - 图13判断请求的id,如果id=1,正常执行(正常调用微服务)。
    • 服务熔断Hystrix高级(已过时) - 图14判断请求的id,如果id!=1,抛出异常。
  • ②默认Hystrix有断路器状态转化的阈值。
    • 服务熔断Hystrix高级(已过时) - 图15触发熔断的最小请求次数为20次。
    • 服务熔断Hystrix高级(已过时) - 图16触发熔断的请求失败比例为50%。
    • 服务熔断Hystrix高级(已过时) - 图17断路器开启的时长为5秒。

3.2.2 修改订单系统的逻辑

  • OrderController.java
  1. package com.sunxiaping.order.controller;
  2. import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
  3. import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
  4. import com.sunxiaping.order.domain.Product;
  5. import com.sunxiaping.order.feign.ProductFeignClient;
  6. import lombok.var;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.web.bind.annotation.GetMapping;
  9. import org.springframework.web.bind.annotation.PathVariable;
  10. import org.springframework.web.bind.annotation.RequestMapping;
  11. import org.springframework.web.bind.annotation.RestController;
  12. import org.springframework.web.client.RestTemplate;
  13. @RestController
  14. @RequestMapping(value = "/order")
  15. public class OrderController {
  16. @Autowired
  17. private RestTemplate restTemplate;
  18. @Autowired
  19. private ProductFeignClient productFeignClient;
  20. @GetMapping(value = "/buy/{id}")
  21. @HystrixCommand(fallbackMethod = "orderFallback", commandProperties = {
  22. @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"), //默认的连接超时时间为1秒,如果1秒内没有返回数据,自动触发降级逻辑
  23. })
  24. public Product buy(@PathVariable(value = "id") Long id) {
  25. if (1 != id) {
  26. throw new RuntimeException("服务器异常");
  27. }
  28. return restTemplate.getForObject("http://SERVICE-PRODUCT/product/findById/" + id, Product.class);
  29. }
  30. public Product orderFallback(Long id) {
  31. var product = new Product();
  32. product.setId(Long.MAX_VALUE);
  33. product.setProductName("******测试断路器的状态******");
  34. return product;
  35. }
  36. }

3.2.3 修改Hystrix有断路器状态转化的阈值

  • 修改方式一:@HystrixCommand注解中配置
  1. package com.sunxiaping.order.controller;
  2. import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
  3. import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
  4. import com.sunxiaping.order.domain.Product;
  5. import com.sunxiaping.order.feign.ProductFeignClient;
  6. import lombok.var;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.web.bind.annotation.GetMapping;
  9. import org.springframework.web.bind.annotation.PathVariable;
  10. import org.springframework.web.bind.annotation.RequestMapping;
  11. import org.springframework.web.bind.annotation.RestController;
  12. import org.springframework.web.client.RestTemplate;
  13. @RestController
  14. @RequestMapping(value = "/order")
  15. public class OrderController {
  16. @Autowired
  17. private RestTemplate restTemplate;
  18. @Autowired
  19. private ProductFeignClient productFeignClient;
  20. @GetMapping(value = "/buy/{id}")
  21. @HystrixCommand(fallbackMethod = "orderFallback", commandProperties = {
  22. @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"), //默认的连接超时时间为1秒,如果1秒内没有返回数据,自动触发降级逻辑
  23. @HystrixProperty(name = "circuitBreaker.enabled", value = "true"), //是否开启断路器
  24. @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"), //触发熔断的最小请求次数,默认为20/10秒
  25. @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), //熔断多少秒后去尝试请求,默认为10秒
  26. @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"), //触发熔断的失败请求最小比例,默认为50%
  27. })
  28. public Product buy(@PathVariable(value = "id") Long id) {
  29. if (1 != id) {
  30. throw new RuntimeException("服务器异常");
  31. }
  32. return restTemplate.getForObject("http://SERVICE-PRODUCT/product/findById/" + id, Product.class);
  33. }
  34. public Product orderFallback(Long id) {
  35. var product = new Product();
  36. product.setId(Long.MAX_VALUE);
  37. product.setProductName("******测试断路器的状态******");
  38. return product;
  39. }
  40. }
  • 修改方式二:修改application.yml
  1. hystrix:
  2. command:
  3. default:
  4. execution:
  5. isolation:
  6. thread:
  7. timeoutInMilliseconds: 3000 # 默认的连接超时时间为1秒,如果1秒没有返回数据,就自动触发降级逻辑
  8. circuitBreaker:
  9. requestVolumeThreshold: 5 #触发熔断的最小请求次数,默认20/10秒
  10. sleepWindowInMilliseconds: 10000 #熔断多少秒后去尝试请求,默认为10秒
  11. errorThresholdPercentage: 50 #触发熔断的失败请求最小占比,默认50%

4 断路器的隔离策略

  • 微服务使用Hystrix熔断器实现了服务的自动降级,让微服务具备自我保护的能力,提升了系统的稳定性,也较好的解决了雪崩效应。其使用方式目前支持两种策略:
  • 服务熔断Hystrix高级(已过时) - 图18线程池隔离策略:使用一个线程池来存储当前的请求,线程池对请求进行处理,设置任务返回处理超时时间,堆积的请求入线程池队列。这种方式需要为每个依赖的服务申请线程池队列,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不玩可以将数据存储到线程池队列慢慢处理)。
  • 服务熔断Hystrix高级(已过时) - 图19信号量隔离策略:使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,请求过来先判断计数器的数值,如果超过设置的最大线程个数则丢弃该类型的新请求,如果不超过设置的最大线程个数则将计数器+1,请求返回计数器-1。这种方式是严格控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)。 | | 线程池隔离 | 信号量隔离 | | —- | —- | —- | | 线程 | 与调用线程非相同线程 | 与调用线程相同(jetty线程) | | 开销 | 排队、调度、上下文开销等 | 无线程切换,开销低 | | 异步 | 可以是异步,也可以是同步。看调用的方法 | 同步调用,不支持异步 | | 并发支持 | 支持(最大线程池大小hystrix.threadpool.default.maximumSize) | 支持(最大信号量上限maxConcurrentRequests) | | 是否超时 | 支持,可直接返回 | 不支持,如果阻塞,只能通过调用协议(如:socket超时才能返回) | | 是否支持熔断 | 支持,当线程池到达maxSize后,再请求会触发fallback接口进行熔断 | 支持,当信号量达到maxConcurrentRequests后。再请求会触发fallback | | 隔离原理 | 每个服务单独用线程池 | 通过信号量的计数器 | | 资源开销 | 大,大量线程的上下文切换,容易造成机器负载高 | 小,只是个计数器 |
  • 可以通过在application.yml中配置隔离策略:
  1. hystrix:
  2. command:
  3. default:
  4. execution:
  5. isolation:
  6. strategy: ExecutionIsolationStrategy.SEMAPHORE # 信号量隔离
  7. maxConcurrentRequests 5000 # 最大信号量上限
  8. # strategy: ExecutionIsolationStrategy.THREAD # 线程池隔离