断路器 Hystrix 源码解析 —— 断路器 HystrixCircuitBreaker

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 单例对象 的映射。代码如下 :
    1. 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;
            }
        }
    }

}

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值