SpringCloud微服务系列 - Hystrix的使用和原理
SpringCloud微服务系列 - Hystrix的使用和原理
概要
Hystrix是Netflix开源的一款容错框架。集成到微服务体系里面的一个组件,是微服务体系里面的熔断器。
主要用于处理微服务架构中的故障,提供了一种机制来防止级联故障。在高并发访问下,系统所依赖的服务的稳定性对系统的影响非常大,依赖有很多不可控的因素,比如网络连接变慢,资源突然繁忙,暂时不可用,服务脱机等。我们要构建稳定、可靠的分布式系统,就必须要有这样一套容错方法。包含常用的容错方法:线程池隔离、信号量隔离、熔断、降级回退。
一、熔断器
Hystrix 采用了熔断器模式来保护系统免受持续失败的服务的影响。
1. 熔断器的状态
熔断器的状态分为三种:关闭、打开、半打开
如下图:
1)闭合(Closed)
在这个状态下,熔断器允许所有请求通过,可以继续后续处理;
2)打开(Open)
当服务的失败率超过设定的阈值时,熔断器会进入打开状态。该状态下,该接口请求会被拦截,直接进行失败处理;在设定的休眠时间(默认是 5 秒)后,Hystrix 会进入半打开状态。
3) 半打开(Half-Open)
当熔断器打开一段时间后(默认是5秒),Hystrix会允许进行一次接口请求,当请求成功后,关闭熔断器,若请求失败,熔断器会再次进入打开状态,继续拒绝后续请求,并重新开始计时,直到达到下一个休眠时间。
2. 熔断器的实现流程
当熔断器的开关为关闭时,每次请求进来都是成功的,当后端服务出现问题,请求出现的错误数达到一定的阈值,则会触发断路器为打开状态,在断路器为打开状态时,进来的所有请求都会被拒绝,当然也不是一直会拒绝请求,而是弹性的,过了特定的时间后,断路器会进入半打开状态,这是会让一部分请求通过进行尝试,如果尝试还是有问题,则继续进入打开状态,并重新开始计时,直到达到下一个休眠时间。如果尝试没有问题了,则会进入关闭状态。
3. 熔断器的打开条件
1)请求总量达到阈值(默认是10秒内20个请求)
在默认配置下,Hystrix 需要在一定时间窗口内收集到足够数量的请求。只有当请求数量达到这个阈值,后续的错误率才会被统计和评估。
2)错误率达到阈值(默认是10秒内错误率50%)
在请求总量达到阈值的情况下,Hystrix 会计算错误率。如果在这个时间窗口内,错误请求的比例超过设定的阈值,那么熔断器将会打开。
说明:以上这两个条件是协同工作的,只有当请求总量达到一定数目,且错误率超过设定阈值时,熔断器才会打开。因此,熔断器不会因为偶尔的错误或请求量过小而过早打开,这样可以提高系统的稳定性和可靠性。
二、容错限流的原理
对于基本的容错限流模式,主要有以下几点需要考量:
- 主动超时:在调用依赖时尽快的超时,可以设置比较短的超时时间,比如2s,防止长时间的等待。
- 限流:限制最大并发数。
- 熔断:错误数达到阈值时,类似于保险丝熔断。
- 隔离:隔离不同的依赖调用。
- 服务降级:资源不足时进行服务降级。
1. 服务降级
当一个服务不可用时,Hystrix 允许定义降级方法。降级方法可以返回默认值或执行其他逻辑,以保证系统的可用性。
触发场景:程序运行异常、超时、服务熔断触发服务降级、线程池/信号量满。
2. 服务熔断
依赖的下游服务多次在一定时间内故障达到了熔断阈值,为避免引发系统崩溃从而进行熔断(不再调用下游故障服务),熔断一定时间会自动尝试恢复。
触发场景:n分钟内下游出现了m次故障
3. 服务限流
当集群处于高并发场景下为保证服务的可靠而进行限流操作
4. 资源隔离
Hystrix 通过线程池(threadpool)和信号量(semaphore)来隔离资源,确保关键任务的执行不会因为其他任务的故障而受到影响。
1)线程池隔离
Hystrix 为每个命令提供独立的线程池,确保某个命令的延迟或失败不会影响到其他命令的执行。这种方式适合于较重的操作,例如调用外部 API 或数据库。
在Hystrix中,线程池隔离可以实现舱壁模式,确保不同服务调用的资源不会相互干扰,从而提高系统的容错能力和弹性。
@HystrixCommand(commandKey = "example", threadPoolKey = "exampleThreadPool") public String remoteCall() { // 执行远程服务调用 }
适用场景:
- 高延迟的远程调用:当调用外部服务(如远程 HTTP、数据库查询等)可能会有较长的响应时间,且这些请求会占用较多的资源时。
- 高并发场景:当服务需要处理大量并发请求时,线程池能够有效地限制并发量,避免过多的请求耗尽系统资源(如线程、CPU等)。
2)信号量隔离
对于轻量级操作(例如简单的数据库查询、缓存访问等),Hystrix 允许使用信号量来限制并发请求的数量,从而有效防止资源耗尽。
@HystrixCommand(commandKey = "example", semaphoreIsolation=true) public String remoteCall() { // 执行快速操作 }
信号量相当于一个计数器。初始化信号量currentCount=0,每进来一个请求需要先将currentCount自增,再判断currentCount的值是否小于系统最大信号量,小于则继续执行,大于则直接返回,拒绝请求。
适用场景:
- 轻量级、低延迟的操作:对于不需要独立线程池的轻量级操作(例如简单的数据库查询、缓存访问等),信号量可以通过限制请求数量来有效隔离。
- 短时间内的高并发请求:如果某些请求非常短小且不会占用太多资源,使用信号量可以节省系统开销,避免不必要的线程创建。
说明:默认情况下,Hystrix 使用线程池隔离 (THREAD),即每个命令都会使用一个独立的线程池来执行。
三、Hystrix 使用
1. 添加依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
2. 主启动类
1 @SpringBootApplication 2 @EnableHystrix 3 public class PaymentApplication8007 { 4 5 public static void main(String[] args) { 6 SpringApplication.run(PaymentApplication8007.class); 7 } 8 }
3. 如果服务消费端使用需要再application.properties进行配置
feign:
hystrix:
enabled: true
这将确保 Hystrix 功能应用于所有 Feign 调用。
4. 定义熔断策略
使用@HystrixCommand注解定义服务调用的熔断策略。通过配置Hystrix的参数,可以详细控制断路器的行为,如超时时间、请求阈值等
1 /**服务提供者模拟请求处理超时,服务消费者通过dystr1x控制*/ 2 //使用HystrixCommand注解进行熔断控制 3 @HystrixCommand( 4 //将相关的 Hystrix 命令进行分组,以便于管理和监控 5 groupKey = "ProductGroup", 6 7 //线程池标识配置,默认情况下所有的请求共同维护一个线程池 8 threadPoolKey="findProductServerPort", 9 10 //线程池细节属性配置 11 threadPoolProperties = { 12 //核心线程为2 13 @HystrixProperty(name="coresize",value="20"), 14 //等待队列的最大长度 15 @HystrixProperty(name="maxQueueSize",value="20") 16 }, 17 18 //配置Hystrix熔断器的属性信息 19 commandProperties = { 20 //设置请求的超时时间,一旦超过配置的时间会触发Hystrix的处理机制 21 @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="2000") 22 //Hystrix高级配置,定制工作过程细节 23 //统计时间窗口定义 24 @HystrixProperty(name="metrics.rollingstats.timeInMilliseconds",value="10000"), 25 //统计时间窗口内的最小请求数 26 @HystrixProperty(name="circuitBreaker.requestvolumeThreshold",value="20"), 27 //统计时间窗口内的错误数量百分比阈值。 28 @HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value="50"), 29 //自我修复时的活动窗口长度 30 @HystrixProperty(name="circuitBreaker.sleepwindowInMilliseconds",value="5000") 31 } 32 33 //表示回退方法。如果对应的服务提供者出现问题,则会调用FallbackMethod属性所指向的方法,将该方法的返回值作为最终响应给客户端的数据 34 fallbackMethod="getProductServerPortFallbackMethod" 35 36 ) 37 @GetMapping("timeout") 38 public CompletableFuture<String> timeout() {
//... 39 } 40 41 public String getProductServerPortFallbackMethod(){ 43 return "超时啦"; 44 }
@HystrixCommand 注解是 Hystrix 提供的一个非常重要的功能,用于简化对外部服务调用的管理和容错处理。
注意:
1. @HystrixCommand是加在服务消费者对应的方法上的。
2. HystrixCommand 和 HystrixObservableCommand
HystrixCommand 和 HystrixObservableCommand这两个类是 Hystrix 命令模式的核心实现。HystrixCommand 用于同步执行命令,而 HystrixObservableCommand 用于异步执行。
3. HystrixPropertiesManager
Hystrix参数不用我们刻意去记下,可以根据HystrixPropertiesManager这个类去查看。
四、版本支持
Spring Cloud 2021.0.x 版本之后,Netflix Hystrix 已经被完全移除,不再被官方维护和支持。这意味着在 Spring Cloud 2021 及更高版本中,Hystrix 相关的依赖将无法自动解析。
在这种情况下,建议的方案是迁移到 Spring Cloud 提供的替代方案 Resilience4j。Resilience4j 提供了与 Hystrix 类似的断路器、限流、重试等功能,并且更适合与 Spring Cloud 2021 及以后的版本搭配使用。
如果确实需要使用 Hystrix,可以尝试以下方式:
1)降级到 Spring Cloud 2020.x 版本:如果可以,降级到支持 Hystrix 的最后一个稳定版本 Spring Cloud Hoxton(2020.x 系列)。
2)手动引入 Hystrix:如果必须在 2021 版本中使用 Hystrix,可以尝试手动指定 Hystrix 的版本,但需要注意兼容性问题。Hystrix 的最后一个版本是 1.5.18:
<dependency> <groupId>com.netflix.hystrix</groupId> <artifactId>hystrix-core</artifactId> <version>1.5.18</version> </dependency>
更推荐的做法是迁移到 Resilience4j,以便获得更好的支持和兼容性。
五、总结
1. Hystrix 通过这些核心类和接口实现了断路器模式,提供了线程池隔离、请求缓存、服务降级等功能。
2. 每个命令在执行时都会被封装为一个 HystrixCommand 实例,并在一个独立的线程池中执行。CircuitBreaker 根据 HystrixCommandMetrics 提供的度量数据来决定是否跳闸。这些组件协同工作,确保了分布式系统在面对服务故障和延迟时的健壮性和弹性。
3. 熔断器是加在调用者端的,目的是保护系统免受被调用服务出现问题而引发的连锁故障。它可以检测服务健康状况并控制请求流量,以确保系统能够保持弹性,避免因某个服务的故障而导致整个系统崩溃。
参考链接:
https://juejin.cn/post/7359464203357437978