Sentinel-DegradeSlot

概述

DegradeSlot是用于服务降级熔断。

在执行entry的过程中,对于处于熔断open状态的情况则判断是否已经过了熔断期且设置半开成功,那么就通过.否则不通过报DegradeException

对于处于降级状态即half-open的时候,则直接抛出DegradeException.

 

熔断器

Sentinel的熔断器一共有两种ExceptionCircuitBreaker 和  ResponseTimeCircuitBreaker 都 extends  AbstractCircuitBreaker  implements CircuitBreaker

在上一个之前slot执行过程中,如果发生了非BlockException即一些未知的throw,那么在exit内会判断error是否达到配置的erro数量或者错误比例。

如果整个调用过程超过了配置的超时时间 则也会触发熔断。

熔断的目的是将熔断器的状态设置到半开或者全开,这样在tryPass校验的时候就可以返回通过或者异常了。

配置面板如下:

Sentinel-DegradeSlot

根据配置项,可以具体看一下熔断器的接口 CircuitBreaker

public interface CircuitBreaker {

    /**
     *  降级熔断规则
     */
    DegradeRule getRule();

    /**
     * true  判断需要降级
     */
    boolean tryPass(Context context);

    /**
     * 当前熔断器的状态
     */
    State currentState();

    /**
     * 回调方法   当请求pass通过后触发
     */
    void onRequestComplete(Context context);

    /**
     * Circuit breaker state.
     */
    enum State {
      
        OPEN,
        
        HALF_OPEN,
       
        CLOSED
    }
}

了解了熔断规则以后,下面将具体阐述熔断流程。

 

熔断器校验

在DegradeSlot#entry#performChecking中,先根据资源名称获取到所有的熔断器列表,然后逐个校验。

void performChecking(Context context, ResourceWrapper r) throws BlockException {
        // 根据资源名  获取该资源的熔断器列表
        List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());
        if (circuitBreakers == null || circuitBreakers.isEmpty()) {
            return;
        }
        for (CircuitBreaker cb : circuitBreakers) {
            //逐次校验
            if (!cb.tryPass(context)) {
                throw new DegradeException(cb.getRule().getLimitApp(), cb.getRule());
            }
        }
    }

这里面怎么获取到熔断配置,后续在控制台这章会详细讲解,关于页面上的配置如何在服务中生效。接下来重点看一下tryPass

@Override
    public boolean tryPass(Context context) {
        // Template implementation.
        if (currentState.get() == State.CLOSED) {
            return true;
        }
        // 若下次时间窗到达,且状态由open-> halfopen  则返回true
        if (currentState.get() == State.OPEN) {
            // For half-open state we allow a request for probing.
            // 校验是否通过的时候  有可能放开
            return retryTimeoutArrived() && fromOpenToHalfOpen(context);
        }
        return false;
    }

很好理解,如果熔断没开,直接return,如果发现正处于熔断状态,这个时候回试图去判断熔断当前时间是否已经过了熔断期,如果过了熔断器,那么尝试性的将open状态设置成half_open,如果设置成功,那么就通过。

 

熔断器状态调整

在statisticSlot中对于一些非BlockException会设置error,然后在调用exit的时候,传递到DegradeSlot#exit的时候,根据情况将会对熔断器的状态设置成open。

这边以ExceptionCircuitBreaker举例,

1 如果当前资源的熔断器已经是open,则不做调整。

2 如果是half-open状态,根据本地调用情况,是否有error来决定设置成close还是升级到熔断open状态

3 如果是close状态,则开始检验降级配置,计算异常数或者异常比例,如果超过阈值则将熔断器状态设置到open

注意:对异常数或者慢调用时间都是以滑动时间窗的数据结构来统计的,可参考滑动时间窗算法

private final LeapArray<SimpleErrorCounter> stat;
private final LeapArray<SlowRequestCounter> slidingCounter;

 

实现代码如下:

 @Override
    public void (Context context) {
        Entry entry = context.getCurEntry();
        if (entry == null) {
            return;
        }
        // 非blockException
        Throwable error = entry.getError();
        // 这边也是使用滑动窗口统计
        SimpleErrorCounter counter = stat.currentWindow().value();
        if (error != null) {
            counter.getErrorCount().add(1);
        }
        counter.getTotalCount().add(1);

        handleStateChangeWhenThresholdExceeded(error);
    }

    private void handleStateChangeWhenThresholdExceeded(Throwable error) {
        if (currentState.get() == State.OPEN) {
            return;
        }
        
        if (currentState.get() == State.HALF_OPEN) {
            // In detecting request
            if (error == null) {
                fromHalfOpenToClose();
            } else {
                fromHalfOpenToOpen(1.0d);
            }
            return;
        }
        
        List<SimpleErrorCounter> counters = stat.values();
        long errCount = 0;
        long totalCount = 0;
        for (SimpleErrorCounter counter : counters) {
            errCount += counter.errorCount.sum();
            totalCount += counter.totalCount.sum();
        }
        if (totalCount < minRequestAmount) {
            return;
        }
        double curCount = errCount;
        if (strategy == DEGRADE_GRADE_EXCEPTION_RATIO) {
            // Use errorRatio
            curCount = errCount * 1.0d / totalCount;
        }
        if (curCount > threshold) {
            transformToOpen(curCount);
        }
    }

 

上一篇:Lync Server外部访问系列PART5:模拟公网DNS


下一篇:三、Sentinel限流熔断