断路器HystrixCircuitBreaker有三种状态:
- CLOSE:关闭
- OPEN:打开
- HALF_OPEN:半开
下图展示了HystrixCommand或HystrixObservableCommand如何与HystrixCircuitBreaker进行交互,以及HystrixCircuitBreaker的决策逻辑过程,包括熔断器内部计数器如何工作。

线路的开路闭路详细逻辑如下:
- 假设线路内的容量(请求QPS)达到一定阈值(通过
HystrixCommandProperties.circuitBreakerRequestVolumeThreshold()配置) - 同时,假设线路内的错误率达到一定阈值(通过
HystrixCommandProperties.circuitBreakerErrorThresholdPercentage()配置) - 熔断器将从
闭路转换成开路 - 若此时是
开路状态,熔断器将短路后续所有经过该熔断器的请求,这些请求直接走失败回退逻辑 - 经过一定时间(即
休眠窗口,通过HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()配置),后续第一个请求将会被允许通过熔断器(此时熔断器处于半开状态),若该请求失败,熔断器将又进入开路状态,且在休眠窗口内保持此状态;若该请求成功,熔断器将进入闭路状态,回到逻辑1循环往复
HystrixCircuitBreaker
com.netflix.hystrix.HystrixCircuitBreaker,Hystrix断路器接口。定义接口如下代码:
1 | public interface HystrixCircuitBreaker { |
allowRequest()和attemptExecution()方法,方法目的基本类似,差别在于当断路器满足尝试关闭条件时,前者不会修改断路器的状态(CLOSE => HALF-OPEN),而后者会。
HystrixCircuitBreaker有两个子类实现:
- NoOpCircuitBreaker:空的断路器实现,用于不开启断路器功能的情况
- HystrixCircuitBreakerImpl:完整的断路器实现
在AbstractCommand创建时,初始化HystrixCircuitBreaker,代码如下:
1 | abstract class AbstractCommand<R> implements HystrixInvokableInfo<R>, HystrixObservable<R> { |
- 当
HystrixCommandProperties.circuitBreakerEnabled = true时,即断路器功能开启,使用Factory获得HystrixCircuitBreakerImpl对象 - 当
HystrixCommandProperties.circuitBreakerEnabled = false时,即断路器功能关闭,创建NoOpCircuitBreaker对象。
HystrixCircuitBreaker.Factory
com.netflix.hystrix.HystrixCircuitBreaker.Factory,HystrixCircuitBreaker工厂,主要用于:
- 创建
HystrixCircuitBreaker对象,目前只创建HystrixCircuitBreakerImpl HystrixCircuitBreaker容器,基于HystrixCommandKey维护了HystrixCircuitBreaker单例对象的映射。代码如下:
1 | private static ConcurrentHashMap<String, HystrixCircuitBreaker> circuitBreakersByCommand = new ConcurrentHashMap<String, HystrixCircuitBreaker>(); |
HystrixCircuitBreakerImpl
com.netflix.hystrix.HystrixCircuitBreaker.HystrixCircuitBreakerImpl,完整的断路器实现
构造方法
1 | class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker { |
subscribeToStream
subscribeToStream方法向Hystrix Metrics对请求量统计Observable发起订阅。代码如下:
1 | private Subscription subscribeToStream() { |
- 向Hystrix Metrics对请求量统计Observable发起订阅。这里的Observable基于RxJava Window操作符。简单来说,固定间隔,
onNext()方法将不断被调用,每次计算断路器的状态。 onNext()方法- 判断周期内(可配,
HystrixCommandProperties.default_metricsRollingStatisticalWindow = 10000ms),总请求数超过一定量(可配,HystrixCommandProperties.circuitBreakerRequestVolumeThreshold = 20) - 错误请求占总请求数超过一定比例(可配,
HystrixCommandProperties.circuitBreakerErrorThresholdPercentage = 50%) - 满足断路器打开条件,CAS将状态从
CLOSED修改为OPEN,并设置打开时间(circuitOpened)
- 判断周期内(可配,
Hystrix Metrics对请求量统计Observable使用了两种RxJava Window操作符:
Observable.window(timespan, unit)方法,固定周期(可配,HystrixCommandProperties.metricsHealthSnapshotIntervalInMilliseconds = 500ms),发射Observable窗口。Observable.window(count, skip)方法,每发射一次(skip)Observable忽略count(可配,HystrixCommandProperties.circuitBreakerRequestVolumeThreshold = 20)个数据项
目前该方法有两处调用:
- 构造方法,在创建
HystrixCircuitBreakerImpl时,向Hystrix Metrics对请求量统计Observable发起订阅。固定间隔计算断路器是否要关闭 markSuccess,清空Hystrix Metrics对请求量统计Observable的统计信息,取消原有订阅,并发起新的订阅
attemptExecution
如下是AbstractCommand.applyHystrixSemantics方法,对HystrixCircuitBreakerImpl.attemptExecution方法的调用的代码:
1 | private Observable<R> applyHystrixSemantics(final AbstractCommand<R> _cmd) { |
使用HystrixCircuitBreakerImpl.attemptExecution方法,判断是否可以执行正常逻辑。代码如下:
1 | public boolean attemptExecution() { |
- 当
HystrixCommandProperties.circuitBreakerForceOpen = true(默认值:false),即断路器强制打开,返回false。当该配置接入配置中心后,可以动态实现打开熔断。为什么会有该配置?当HystrixCircuitBreaker创建完成后,无法动态切换NoOpCircuitBreaker和HystrixCircuitBreakerImpl,通过该配置以实现类似效果。 - 当
HystrixCommandProperties.circuitBreakerForceClose = true(默认值:false),即断路器强制关闭,返回true。当该配置接入配置中心后,可以动态实现关闭熔断。为什么会有该配置?当HystrixCircuitBreaker创建完成后,无法动态切换NoOpCircuitBreaker和HystrixCircuitBreakerImpl,通过该配置以实现类似效果。 - 断路器打开时间(
circuitOpened)为空,返回true - 调用
isAfterSleepWindow方法,判断是否满足尝试调用正常逻辑的间隔时间。当满足时,使用CAS方法修改断路器状态(OPEN=>HALF_OPEN),从而保证有且仅有一个线程能够尝试调用正常逻辑
isAfterSleepWindow方法代码如下:
1 | private boolean isAfterSleepWindow() { |
在当前时间超过断路器打开时间HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds(默认值:5000ms),返回true。
markSuccess
当尝试调用正常逻辑成功时,调用markSuccess方法,关闭断路器。
1 | public void markSuccess() { |
- 使用CAS方式,修改断路器状态(
HALF_OPEN=>CLOSED) - 清空Hystrix Metrics对请求量统计Observable的统计信息
- 取消原有订阅,发起新的订阅
- 设置断路器打开时间为空
以下两处调用markSuccess方法:
markEmitsmarkOnCompleted
markNonSuccess
当尝试调用正常逻辑失败时,调用markNonSuccess方法,重新打开断路器。
1 | public void markNonSuccess() { |
- 使用CAS的方式,修改断路器的状态(
HALF_OPEN=>OPEN) - 设置断路器打开时间为当前时间。这样
attemptExecution()过一段时间,可以再次尝试执行正常逻辑
以下两处调用了markNonSuccess方法:
handleFallbackunsubscribeCommandCleanup
allowRequest
allowRequest方法和attemptExecution方法,目的基本类似,差别在于当断路器满足尝试关闭条件时,前者不会修改断路器的状态(CLOSE => HALF-OPEN),而后者会。
isOpen
isOpen方法比较简单:
1 | public boolean isOpen() { |
断路器测试
对断路器的测试,我们选择比较简单的方式,直接调用Hystrix的命令:
1 | public class CircuitBreakerCommand extends HystrixCommand<String> { |
执行结果如下:
1 | call times:1 result: running: isCircuitBreakerOpen: false |
我们看到,前10此命令执行有两次失败,于是熔断器被打开,11到20次执行全部快速失败。5s后熔断器关闭,命令可以再次尝试执行。
总结
总体来说,Hystrix的断路器是一个防止重复并发请求失败服务的机制,它的执行流程如下:
- 在一定时间内(HystrixCommandProperties.metricsRollingStatisticalWindowInMilliseconds,默认为10000ms),请求次数达到一定阈值(HystrixCommandProperties.circuitBreakerRequestVolumeThreshold,默认为20),且错误率达到一定阈值(HystrixCommandProperties.circuitBreakerErrorThresholdPercentage,默认为50%),熔断器将从闭路转换成开路。开路状态下,所有请求直接走失败回退逻辑。
- 经过一定的时间(休眠窗口,HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds,默认为5000ms),后续第一个请求将会被允许通过熔断器(半开状态),若该请求失败,熔断器又进入开路状态,且在休眠窗口内保持此状态;若该请求成功,熔断器将进入闭路状态。
http://youdang.github.io/2016/02/05/translate-hystrix-wiki-how-it-works/#%E8%AF%B7%E6%B1%82%E5%90%88%E5%B9%B6
https://github.com/YunaiV/Blog/blob/master/Hystrix/2018_11_08_Hystrix%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%20%E2%80%94%E2%80%94%20%E6%96%AD%E8%B7%AF%E5%99%A8%20HystrixCircuitBreaker.md
https://www.jianshu.com/p/14958039fd15