在生活中,如果电路的负载过高,保险箱会自动跳闸,以保护家里的各种电器,这就是熔断器的一个活生生例子。在Hystrix中也存在这样一个熔断器,当所依赖的服务不稳定时,能够自动熔断,并提供有损服务,保护服务的稳定性。在运行过程中,Hystrix会根据接口的执行状态(成功、失败、超时和拒绝),收集并统计这些数据,根据这些信息来实时决策是否进行熔断。
相关参数
Hystrix的熔断器实现在HystrixCircuitBreaker
类中,比较重要的几个参数如下:
1、circuitBreaker.enabled
熔断器是否启用,默认是true
2、circuitBreaker.forceOpen
熔断器强制打开,始终保持打开状态,默认是false
3、circuitBreaker.forceClosed
熔断器强制关闭,始终保持关闭状态,默认是false
4、circuitBreaker.requestVolumeThreshold
滑动窗口内(10s)的请求数阈值,只有达到了这个阈值,才有可能熔断。默认是20
,如果这个时间段只有19个请求,就算全部失败了,也不会自动熔断。
5、circuitBreaker.errorThresholdPercentage
错误率阈值,默认50%
,比如(10s)内有100个请求,其中有60个发生异常,那么这段时间的错误率是60
,已经超过了错误率阈值,熔断器会自动打开。
6、circuitBreaker.sleepWindowInMilliseconds
熔断器打开之后,为了能够自动恢复,每隔默认5000ms
放一个请求过去,试探所依赖的服务是否恢复。
工作原理
详细过程如下:
在最新代码中,因为一些bug,已经弃用了allowRequest()
,取而代之的是attemptExecution()
方法。
和allowRequest()
方法相比,唯一改进的地方是通过compareAndSet
修改状态值。通过attemptExecution()
方法的返回值决定执行正常逻辑,还是降级逻辑。
1、如果circuitBreaker.forceOpen=true
,说明熔断器已经强制开启,所有请求都会被熔断。
2、如果circuitBreaker.forceClosed =true
,说明熔断器已经强制关闭,所有请求都会被放行。
3、circuitOpened
默认-1
,用以保存最近一次发生熔断的时间戳。
4、如果circuitOpened
不等于 -1
,说明已经发生熔断,通过isAfterSleepWindow()
判断当前是否需要进行试探。
这里就是熔断器自动恢复的逻辑,如果当前时间已经超过上次熔断的时间戳 + 试探窗口5000ms
,则进入if分支,通过compareAndSet修改变量status
,竞争试探的能力。其中status
代表当前熔断器的状态,包含CLOSED, OPEN, HALF_OPEN
,只有试探窗口之后的第一个请求可以执行正常逻辑,且修改当前状态为HALF_OPEN
,进入半熔断状态,其它请求执行compareAndSet(Status.OPEN, Status.HALF_OPEN)
时都返回false,执行降级逻辑。
5、如果试探请求发生异常,则执行markNonSuccess()
通过compareAndSet
修改status
为熔断开启状态,并更新当前熔断开启的时间戳。
6、如果试探请求返回成功,则执行markSuccess()
通过compareAndSet
修改status
为熔断关闭状态,并重置接口统计数据和circuitOpened
标识为-1
,后续请求开始执行正常逻辑。
说了这么多,如何实现自动熔断还没提到,在Hystrix内部有一个Metric模块,专门统计每个Command的执行状态,包括成功、失败、超时、线程池拒绝等,在熔断器的中subscribeToStream()
方法中,通过订阅数据流变化,实现函数回调,当有新的请求时,数据流发生变化,触发回调函数onNext
在onNext
方法中,参数hc
保存了当前接口在前10s之内的请求状态(请求总数、失败数和失败率),其主要逻辑是判断请求总数是否达到阈值requestVolumeThreshold
,失败率是否达到阈值errorThresholdPercentage
,如果都满足,说明接口的已经足够的不稳定,需要进行熔断,则设置status
为熔断开启状态,并更新circuitOpened
为当前时间戳,记录上次熔断开启的时间。