0 参考网站:
- 1. 概述 参数:
- 2. HystrixCircuitBreaker
- 3. HystrixCircuitBreaker.Factory
- 4. HystrixCircuitBreakerImpl
- 4.1 构造方法
- 4.2 #subscribeToStream()
- 4.3 #attemptExecution()
- 4.4 #markSuccess()
- 4.5 #markNonSuccess()
- 4.6 #allowRequest()
- 4.7 #isOpen()
- 5 NoOpCircuitBreaker
- 参考网站:
- https://www.jianshu.com/p/b9af028efebb
- 本文主要分享 断路器 HystrixCircuitBreaker。
-
// 统计滚动的时间窗口,默认:5000毫秒(取自circuitBreakerSleepWindowInMilliseconds) private final HystrixProperty metricsRollingStatisticalWindowInMilliseconds; // 统计窗口的Buckets的数量,默认:10个,每秒一个Buckets统计 private final HystrixProperty metricsRollingStatisticalWindowBuckets; // number of buckets in the statisticalWindow // 是否开启监控统计功能,默认:true private final HystrixProperty metricsRollingPercentileEnabled; /* --------------熔断器相关------------------*/ // 熔断器在整个统计时间内是否开启的阀值,默认20。也就是在metricsRollingStatisticalWindowInMilliseconds(默认10s)内至少请求20次,熔断器才发挥起作用 private final HystrixProperty circuitBreakerRequestVolumeThreshold; // 熔断时间窗口,默认:5秒.熔断器中断请求5秒后会进入半打开状态,放下一个请求进来重试,如果该请求成功就关闭熔断器,否则继续等待一个熔断时间窗口 private final HystrixProperty circuitBreakerSleepWindowInMilliseconds; //是否启用熔断器,默认true. 启动 private final HystrixProperty circuitBreakerEnabled; //默认:50%。当出错率超过50%后熔断器启动 private final HystrixProperty circuitBreakerErrorThresholdPercentage; //是否强制开启熔断器阻断所有请求,默认:false,不开启。置为true时,所有请求都将被拒绝,直接到fallback private final HystrixProperty circuitBreakerForceOpen; //是否允许熔断器忽略错误,默认false, 不开启 private final HystrixProperty circuitBreakerForceClosed; /* --------------信号量相关------------------*/ //使用信号量隔离时,命令调用最大的并发数,默认:10 private final HystrixProperty executionIsolationSemaphoreMaxConcurrentRequests; //使用信号量隔离时,命令fallback(降级)调用最大的并发数,默认:10 private final HystrixProperty fallbackIsolationSemaphoreMaxConcurrentRequests; /* --------------其他------------------*/ //使用命令调用隔离方式,默认:采用线程隔离,ExecutionIsolationStrategy.THREAD private final HystrixProperty executionIsolationStrategy; //使用线程隔离时,调用超时时间,默认:1秒 private final HystrixProperty executionIsolationThreadTimeoutInMilliseconds; //线程池的key,用于决定命令在哪个线程池执行 private final HystrixProperty executionIsolationThreadPoolKeyOverride; //是否开启fallback降级策略 默认:true private final HystrixProperty fallbackEnabled; // 使用线程隔离时,是否对命令执行超时的线程调用中断(Thread.interrupt())操作.默认:true private final HystrixProperty executionIsolationThreadInterruptOnTimeout; // 是否开启请求日志,默认:true private final HystrixProperty requestLogEnabled; //是否开启请求缓存,默认:true private final HystrixProperty requestCacheEnabled; // Whether request caching is enabled.
- HystrixCircuitBreaker 有三种状态
CLOSED
:关闭OPEN
:打开HALF_OPEN
:半开-
其中,断路器处于
OPEN
状态时,链路处于非健康状态,命令执行时,直接调用回退逻辑,跳过正常逻辑。HystrixCircuitBreaker 状态变迁如下图 :
红线 :初始时,断路器处于 CLOSED
状态,链路处于健康状态。当满足如下条件,断路器从 CLOSED
变成 OPEN
状态:
- 周期( 可配,
HystrixCommandProperties.default_metricsRollingStatisticalWindow=10000ms
)内,总请求数超过一定量( 可配,HystrixCommandProperties.circuitBreakerRequestVolumeThreshold=20
) 。 - 错误请求占总请求数超过一定比例( 可配,
HystrixCommandProperties.circuitBreakerErrorThresholdPercentage=50%
绿线 :断路器处于 OPEN
状态,命令执行时,若当前时间超过断路器开启时间一定时间( HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds=5000ms
),断路器变成 HALF_OPEN
状态,尝试调用正常逻辑,(只让一个请求通过)根据执行是否成功,打开或关闭熔断器【蓝线】。
om.netflix.hystrix.HystrixCircuitBreaker
,Hystrix 断路器接口。定义接口如下代码
public interface HystrixCircuitBreaker { /** *每个{@link HystrixCommand}请求都会询问是否允许继续。 它是幂等的 *不修改任何内部状态,并考虑允许一些请求通过在半开逻辑电路打开后 * * @return boolean是否应允许请求、 **/ boolean allowRequest(); //电路器是否打开 boolean isOpen(); /** * Invoked on successful executions from {@link HystrixCommand} as part of feedback mechanism when in a half-open state. 在半开状态下,如果成功调用了HystrixCommand 那么,就会反馈这个结果。 */ void markSuccess(); /** * Invoked on unsuccessful executions from {@link HystrixCommand} as part of feedback mechanism when in a half-open state. 作为处于半开状态的反馈机制的一部分,从{@link HystrixCommand}执行失败时调用。 */ void markNonSuccess(); /** * Invoked at start of command execution to attempt an execution. This is non-idempotent - it may modify internal * state. *在命令执行开始时调用以尝试执行。 这是非幂等的 - 它可能会修改内部 */ boolean attemptExecution(); }
#allowRequest()
和#attemptExecution()
方法,方法目的基本类似,差别在于当断路器满足尝试关闭条件时,前者不会将断路器不会修改状态( oepn=>HALF-OPEN
),而后者会。
HystrixCircuitBreaker 有两个子类实现 :
- NoOpCircuitBreaker :空的断路器实现,用于不开启断路器功能的情况。
- HystrixCircuitBreakerImpl :完整的断路器实现。
在 AbstractCommand 创建时,初始化 HystrixCircuitBreaker ,代码如下 :
/* package */abstract class AbstractCommand<R> implements HystrixInvokableInfo<R>, HystrixObservable<R> { /** * 断路器 */ protected final HystrixCircuitBreaker circuitBreaker; protected AbstractCommand(HystrixCommandGroupKey group, HystrixCommandKey key, HystrixThreadPoolKey threadPoolKey, HystrixCircuitBreaker circuitBreaker, HystrixThreadPool threadPool, HystrixCommandProperties.Setter commandPropertiesDefaults, HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults, HystrixCommandMetrics metrics, TryableSemaphore fallbackSemaphore, TryableSemaphore executionSemaphore, HystrixPropertiesStrategy propertiesStrategy, HystrixCommandExecutionHook executionHook) { // ... 省略无关代码 // 初始化 断路器 this.circuitBreaker = initCircuitBreaker(this.properties.circuitBreakerEnabled().get(), circuitBreaker, this.commandGroup, this.commandKey, this.properties, this.metrics); // ... 省略无关代码 } private static HystrixCircuitBreaker initCircuitBreaker(boolean enabled, HystrixCircuitBreaker fromConstructor, HystrixCommandGroupKey groupKey, HystrixCommandKey commandKey, HystrixCommandProperties properties, HystrixCommandMetrics metrics) { if (enabled) { if (fromConstructor == null) { // get the default implementation of HystrixCircuitBreaker return HystrixCircuitBreaker.Factory.getInstance(commandKey, groupKey, properties, metrics); } else { return fromConstructor; } } else { return new NoOpCircuitBreaker(); } } }
- 当
HystrixCommandProperties.circuitBreakerEnabled=true
时,即断路器功能开启,使用 Factory 获得 HystrixCircuitBreakerImpl 对象。在 「3. HystrixCircuitBreaker.Factory」 详细解析。 - 当
HystrixCommandProperties.circuitBreakerEnabled=false
时,即断路器功能关闭,创建 NoOpCircuitBreaker 对象。另外,NoOpCircuitBreaker 代码简单到脑残,点击 链接 查看实现。
3. HystrixCircuitBreaker.Factory
com.netflix.hystrix.HystrixCircuitBreaker.Factory
,HystrixCircuitBreaker 工厂,主要用于:
- 创建 HystrixCircuitBreaker 对象,目前只创建 HystrixCircuitBreakerImpl 。
- HystrixCircuitBreaker 容器,基于 HystrixCommandKey 维护了 HystrixCircuitBreaker 单例对象 的映射。代码如下 :
private
static
ConcurrentHashMap<String,
HystrixCircuitBreaker> circuitBreakersByCommand =
new
ConcurrentHashMap<String,
HystrixCircuitBreaker>();
4. HystrixCircuitBreakerImpl
持有的属性
构造方法,代码如下 :
/* package */class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker { private final HystrixCommandProperties properties;//代表通过构造函数进入的 private final HystrixCommandMetrics metrics; /* track whether this circuit is open/closed at any given point in time (default to false==closed) */ private AtomicBoolean circuitOpen = new AtomicBoolean(false); /* when the circuit was marked open or was last allowed to try a 'singleTest' */ 电路被标记为打开或最后被允许尝试'singleTest时 //电路被标记为打开的时间,或者是过了滑动窗口设计的一个尝试时间。 private AtomicLong circuitOpenedOrLastTestedTime = new AtomicLong(); protected HystrixCircuitBreakerImpl(HystrixCommandKey key, HystrixCommandGroupKey commandGroup, HystrixCommandProperties properties, HystrixCommandMetrics metrics) { this.properties = properties; this.metrics = metrics;
status 属性,断路器的状态。
circuitOpen 属性,判断断路器是否打开,默认没打开
circuitOpenedOrLastTestedTime :,记录上一次断路器被打开的时间戳,如果上一次断路器是关闭状态,
2、构造方法
直到这里,我们就知道,要创建一个HystrixCircuitBreakerImpl实例,必须要知道上述提到的四个概念。因为断路器主要是围绕HystrixCommand而生。
HystrixCommandKey
HystrixCommandGroupKey
HystrixCommandProperties
HystrixCommandMetrics
@Override
public boolean allowRequest() {
//HystrixCommandProperties.circuitBreakerForceOpen = true ( 默认值 :false) 时,即断路器强制打开,返回 false
当该配置接入配置中心后,可以动态实现打开熔断。为什么会有该配置?当 HystrixCircuitBreaker 创建完成后,无法动态切换 NoOpCircuitBreaker 和 HystrixCircuitBreakerImpl ,通过该配置以实现类似效果。
if (properties.circuitBreakerForceOpen().get()) {
// properties have asked us to force the circuit open so we will allow NO requests
return false;
}
HystrixCommandProperties.circuitBreakerForceClose = true ( 默认值 :false) 时,即断路器强制关闭,
//我们仍然希望允许isOpen()执行它的计算,以便我们模拟正常行为
if (properties.circuitBreakerForceClosed().get()) {
// we still want to allow isOpen() to perform it's calculations so we simulate normal behavior
isOpen();
/ properties已要求我们忽略错误,因此我们将忽略isOpen的结果并允许所有流量通过
// properties have asked us to ignore errors so we will ignore the results of isOpen and just allow all traffic through
return true;
}
return !isOpen() || allowSingleTest();
}
public boolean allowSingleTest() {
long timeCircuitOpenedOrWasLastTested = circuitOpenedOrLastTestedTime.get();
// 1) if the circuit is open
// 2) and it's been longer than 'sleepWindow' since we opened the circuit
//如果断路器打开了,并且超过了滑动窗口的休眠期,circuitBreakerSleepWindowInMilliseconds 这配置。
if (circuitOpen.get() && System.currentTimeMillis() > timeCircuitOpenedOrWasLastTested + properties.circuitBreakerSleepWindowInMilliseconds().get()) {
// We push the 'circuitOpenedTime' ahead by 'sleepWindow' since we have allowed one request to try.
// If it succeeds the circuit will be closed, otherwise another singleTest will be allowed at the end of the 'sleepWindow'
//由于我们允许一个请求尝试,我们将'circuitOpenedTime'推向'sleepWindow',允许一个请求通过cas拿到这个测试如果能拿到,就让这个请求通过。
if (circuitOpenedOrLastTestedTime.compareAndSet(timeCircuitOpenedOrWasLastTested, System.currentTimeMillis())) {
// if this returns true that means we set the time so we'll return true to allow the singleTest
// if it returned false it means another thread raced us and allowed the singleTest before we did
return true;
}
}
return false;
}
//判断断路器是否打开
@Override public boolean isOpen() { if (circuitOpen.get()) { // if we're open we immediately return true and don't bother attempting to 'close' ourself as that is left to allowSingleTest and a subsequent successful test to close 如果我们打开,我们立即返回true并且不打算尝试“关闭”我们自己,因为留下来允许单个测试和随后的成功测试关闭 return true; } // we're closed, so let's see if errors have made us so we should trip the circuit open //我们已经关闭,所以让我们看看错误是否让我们如此,我们应该打开电路 HealthCounts health = metrics.getHealthCounts(); // check if we are past the statisticalWindowVolumeThreshold //检查周期类的总共请求是否小于总共的阀值,小于的话,就返回false,没打开。 if (health.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) { // we are not past the minimum volume threshold for the statisticalWindow so we'll return false immediately and not calculate anything return false; } //是否出错的阀值也小于百分比, if (health.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) { return false; } else { // our failure rate is too high, trip the circuit //前面的2项都不满足,我们就让一个线程设置为true,并且记录最新的断路打开的时间。 if (circuitOpen.compareAndSet(false, true)) { // if the previousValue was false then we want to set the currentTime circuitOpenedOrLastTestedTime.set(System.currentTimeMillis()); return true; } else { // How could previousValue be true? If another thread was going through this code at the same time a race-condition could have // caused another thread to set it to true already even though we were in the process of doing the same // In this case, we know the circuit is open, so let the other thread set the currentTime and report back that the circuit is open return true; } } } }