熔断器模式

2018-06-19  本文已影响0人  邝健强

产生原因

在分布式应用中,容易出现由某个基础服务故障引发整个集群了崩溃,称之为雪崩。

熔断器模式

熔断器模式可以有效防止对故障服务的不断重试,可以使服务调用者继续执行不用不用等待错误的修正,或者浪费cpu等待超时发生。熔断器模式也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。
熔断器模式为远程服务提供的关键能力:

  1. 快速失败-当远程服务处于降级状态时,应用程序将会快速失败,并防止通常会拖垮整个应用程序的资源耗尽问题的出现。在大多数中断情况下,最好是部分服务关闭而不是完全关闭。
  2. 优雅地失败-通过超时和快速失败,熔断器模式使应用程序开发人员有能力优雅地失败或提供替代方案。
  3. 无缝恢复-熔断器模式可以定期检查所请求的资源是否重新上线,并在没有认为干预的情况下重新允许对该资源进行访问。

实现

熔断器模式其实就是对服务的调用做了一层代理,对最近服务固定时间段调用错误次数进行统计,如果达到指定次数,则直接返回失败,并允许再接下的时间段内允许个别调用者真实调用服务,如果成功则认为服务已正常,允许后面服务的正常调用,否则继续返回失败。

熔断器模式可以通过状态模式实现: 状态转换.png

需要考虑的因素

Hystrix熔断器实现

简介

Hystrix是Netflix公司开源的防雪崩的利器,是一个帮助解决分布式系统交互时超时处理和容错的类库, 拥有保护系统的能力。

实现

/**
 * Circuit-breaker logic that is hooked into {@link HystrixCommand} execution and will stop allowing executions if failures have gone past the defined threshold.
 * <p>
 * The default (and only) implementation  will then allow a single retry after a defined sleepWindow until the execution
 * succeeds at which point it will again close the circuit and allow executions again.
 */
public interface HystrixCircuitBreaker {

    /**
     * Every {@link HystrixCommand} requests asks this if it is allowed to proceed or not.  It is idempotent and does
     * not modify any internal state, and takes into account the half-open logic which allows some requests through
     * after the circuit has been opened
     * 
     * @return boolean whether a request should be permitted
     */
    boolean allowRequest();

    /**
     * Whether the circuit is currently open (tripped).
     * 
     * @return boolean state of circuit breaker
     */
    boolean isOpen();

    /**
     * Invoked on successful executions from {@link HystrixCommand} as part of feedback mechanism when in a half-open state.
     */
    void markSuccess();

    /**
     * Invoked on unsuccessful executions from {@link HystrixCommand} as part of feedback mechanism when in a half-open state.
     */
    void markNonSuccess();

    /**
     * Invoked at start of command execution to attempt an execution.  This is non-idempotent - it may modify internal
     * state.
     */
    boolean attemptExecution();

这是熔断器的具体接口,下面我们分析下具体实现:

        @Override
        public boolean allowRequest() {
          //熔断器开关强制打开,则降级处理    
        if (properties.circuitBreakerForceOpen().get()) {
                return false;
            }  
            //如果熔断器强制关闭,则正常执行
          if (properties.circuitBreakerForceClosed().get()) {
                         isOpen();

                return true;
            }
              //判断熔断器是否打开,或者熔断器是否允许一个时间窗口进行单次访问  
            return !isOpen() || allowSingleTest();      
  }
  @Override
        public boolean isOpen() {
            //如果熔断器已打开,立刻返回true
            if (circuitOpen.get()) {
                           return true;
            }

           //如果当前开关处于闭合状态,根据采样判断当前是否需要熔断 
            HealthCounts health = metrics.getHealthCounts();

          //如果当前采样的总请求数小于circuitBreakerRequestVolumeThreshold阈值,不进行熔断
            if (health.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) {
                            return false;
            }

            //如果采样的错误率小于circuitBreakerErrorThresholdPercentage阈值,则不进行熔断
            if (health.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) {
                return false;
            } else {
                  //失败率超过阈值,进行熔断
                if (circuitOpen.compareAndSet(false, true)) {
                    circuitOpenedOrLastTestedTime.set(System.currentTimeMillis());
                    return true;
                } else {
                    return false;
                }
            }
        }
    }
        public boolean allowSingleTest() {
            //熔断器打开后的最后一次测试时间
            long timeCircuitOpenedOrWasLastTested = circuitOpenedOrLastTestedTime.get();
                //熔断器处于打开状态,允许在一个circuitBreakerSleepWindowInMilliseconds时间窗口内,进行访问测试
                 if (circuitOpen.get() && System.currentTimeMillis() > timeCircuitOpenedOrWasLastTested + properties.circuitBreakerSleepWindowInMilliseconds().get()) {
                if (circuitOpenedOrLastTestedTime.compareAndSet(timeCircuitOpenedOrWasLastTested, System.currentTimeMillis())) {
                                     return true;
                }
            }
            return false;
        }
        public void markSuccess() {
            if (circuitOpen.get()) {
               //重置采样数据,如错误次数,最后测试时间等
                metrics.resetCounter();
                //熔断器设置成关闭
                circuitOpen.set(false);
            }
        }

总结

熔断器是微服务的弹性化的其中一步,它能很好的保护我们的应用程序,除了熔断器外,客户端弹性模式还有后备模式、客户端负载均衡、后备模式和舱壁模式。

参考:
https://www.cnblogs.com/shanyou/p/CircuitBreaker.html
https://segmentfault.com/a/1190000005988895

上一篇 下一篇

猜你喜欢

热点阅读