Sentinel的基本使用-熔断降级使用及源码分析

Posted _微风轻起

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Sentinel的基本使用-熔断降级使用及源码分析相关的知识,希望对你有一定的参考价值。

这一篇我们主要来分析下Sentinel的熔断降级,这个相关的官方文档

一、demo使用

public class DegradeFlowDemo 

    private static final String resourceKey = "degradeResource";

    private static int index = 0;

    public static void main(String[] args) throws InterruptedException 
        initRule();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 12; i++) 
            new Thread(() -> 
                    Entry entry = null;
                    try 
                        entry = SphU.entry(resourceKey);
                        doSomething();
                     catch (BlockException blockException) 
                        System.out.println("---------Blocked");
                     catch (Exception e) 
                        System.out.println("---------Exception");
                        Tracer.trace(e);
                     finally 
                        if (Objects.nonNull(entry)) 
                            entry.exit();
                        
                    
            ).start();
            Thread.sleep(50);
        
        System.out.println("End cost time - " + (System.currentTimeMillis() - startTime));
    

    private static void initRule()
        List<DegradeRule> degradeRuleList = new ArrayList<>();
        DegradeRule degradeRule = new DegradeRule(resourceKey);
        degradeRule.setTimeWindow(500);
        degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        degradeRule.setCount(3);
        degradeRule.setStatIntervalMs(1000);
        degradeRule.setMinRequestAmount(10);
        degradeRuleList.add(degradeRule);
        DegradeRuleManager.loadRules(degradeRuleList);
    

    private static void doSomething()
        if (index++%2 == 0) 
            throw new NullPointerException();
        
        System.out.println("----------doSomething");
    


​ 这个demo我们就不像上篇那样详细了,我们看下运行结果:

----------doSomething
---------Exception
---------Exception
----------doSomething
---------Exception
----------doSomething
---------Exception
----------doSomething
---------Exception
----------doSomething
---------Blocked
---------Blocked
End cost time - 762

Process finished with exit code 0

​ 这个demo就是我们开启12个线程运行,然后将熔断规则设置为异常错误数,也就是有多少错误请求的时候就返回,同时我们设置了最小的请求数,也就是在StatIntervalMs时间短内必须要达到MinRequestAmount请求的数量,才会熔断,不然即使有count个异常树也不会熔断。例如demo前10个其的异常数量是超过了3个,但并没有熔断,而是从第11个开始熔断的。

二、demo分析

1、参数

二、demo分析

1、参数

1)、setTimeWindow

​ 这个主要是用来设置从熔断到下次放开一个探测请求的时间,也就是半开放状态。

public interface CircuitBreaker 
		.........
    /**
     * Circuit breaker state.
     */
    enum State 
        
        OPEN,
        
        HALF_OPEN,
        
        CLOSED
    

​ 这个是熔断器的3种状态,OPEN表示目前熔断器是打开的,所有请求都不能通过,HALF_OPEN表示半开状态,也就是会放开请求去运行看这个请求是否正常,如果正常交可以设置为CLOSED,也就是关闭熔断器,让请求能通过,如果半开状态的请求不能正常访问,就将状态从HALF_OPEN变为OPEN,然后需要等待下一个窗口时间-TimeWindow

2)、setGrade

​ 这个就是设置对应的熔断策略的,其主要有3中,

public static final int DEGRADE_GRADE_EXCEPTION_COUNT = 2;

public static final int DEGRADE_DEFAULT_SLOW_REQUEST_AMOUNT = 5;
public static final int DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT = 5;
  • 慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。

  • 异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

  • 异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

    这里我们直接贴官方的说明,这个介绍的还是

3)、degradeRule.setStatIntervalMs

​ 然后degradeRule.setStatIntervalMs(1000)设置的就是统计的窗口时间这个时间段多长,例如QPS默认的就是1000ms,也就是这些数据的统计是统计哪个时间段,也就是我们上篇梳理的LeapArray

public ExceptionCircuitBreaker(DegradeRule rule) 
    this(rule, new SimpleErrorCounterLeapArray(1, rule.getStatIntervalMs()));

​ 我们上面的就是ExceptionCircuitBreaker就是对应的DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT策略对应的熔断器逻辑处理类。可以看到其的SimpleErrorCounterLeapArray传入的是1,也就是在getStatIntervalMs事件单位内就用一个桶来计算统计,我们上篇介绍的QPS其默认的是21000,也就是将1000分为两个桶来计算。

4)、setMinRequestAmount

​ 最小请求数,在上面demo介绍有说明其的作用了。

三、逻辑源码分析。

1、DegradeSlot

​ 与熔断降级相关的类是DegradeSlot

1)、entry方法

public class DegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> 

    @Override
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
                      boolean prioritized, Object... args) throws Throwable 
        performChecking(context, resourceWrapper);

        fireEntry(context, resourceWrapper, node, count, prioritized, args);
    

​ 可以看到其是先通过performChecking判断是否能通过,能通过再fireEntry继续调用链路的执行。

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());
        
    

​ 其判断就是获取规则的CircuitBreaker,也就是我们前面介绍的3种策略:

public ResponseTimeCircuitBreaker(DegradeRule rule) 
    this(rule, new SlowRequestLeapArray(1, rule.getStatIntervalMs()));

​ 我们可以看到ResponseTimeCircuitBreaker也是一个桶来计算。

public final class DegradeRuleManager 
    ...........
	private static CircuitBreaker newCircuitBreakerFrom(/*@Valid*/ DegradeRule rule) 
        switch (rule.getGrade()) 
            case RuleConstant.DEGRADE_GRADE_RT:
                return new ResponseTimeCircuitBreaker(rule);
            case RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO:
            case RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT:
                return new ExceptionCircuitBreaker(rule);
            default:
                return null;
        
    

​ 我们看到DEGRADE_GRADE_EXCEPTION_RATIODEGRADE_GRADE_EXCEPTION_COUNT这两种都是用的同一个类。

​ 也就是DegradeSlot通过CircuitBreaker去判断是否能通过,不能通过的话,就抛出DegradeException(其是BlockException的子类),

2)、exit方法

@Override
public void exit(Context context, ResourceWrapper r, int count, Object... args) 
    Entry curEntry = context.getCurEntry();
    if (curEntry.getBlockError() != null) 
        fireExit(context, r, count, args);
        return;
    
    List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());
    if (circuitBreakers == null || circuitBreakers.isEmpty()) 
        fireExit(context, r, count, args);
        return;
    

    if (curEntry.getBlockError() == null) 
        // passed request
        for (CircuitBreaker circuitBreaker : circuitBreakers) 
            circuitBreaker.onRequestComplete(context);
        
    

    fireExit(context, r, count, args);

​ 这个就是离开的,如果curEntry.getBlockError() == null,也就是并没有阻塞熔断,其用CircuitBreaker再来处理计算逻辑。

这里对应的官网的一句话:

注意异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要通过 Tracer.trace(ex) 记录业务异常

​ 也就是BlockException并不算一个异常,其计算的是你的业务异常。

3)、ExceptionCircuitBreaker

1)、exit相关的逻辑调用

我们以ExceptionCircuitBreaker来说明下CircuitBreaker一般的逻辑处理。剩下直接是上面的exit方法里面的CircuitBreaker调用:

@Override
public void onRequestComplete(Context context) 
    Entry entry = context.getCurEntry();
    if (entry == null) 
        return;
    
    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);
    

exit的逻辑主要是这两个方法,一个是对调用次数,异常次数计数。handleStateChangeWhenThresholdExceeded就计算熔断器的状态,也就是通过计算对应的值,是熔断器状态在OPENCLOSEHALF_OPEN切换。例如:

if (currentState.get() == State.HALF_OPEN) 
    // In detecting request
    if (error == null) 
        fromHalfOpenToClose();
     else 
        fromHalfOpenToOpen(1.0d);
    
    return;

​ 如果当前是HALF_OPEN,当这个请求没有error,就将状态由HALF_OPEN变为CLOSE,不然就将HALF_OPEN继续变为OPEN,不允许通过请求。还有:

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);

​ 也就是要达到的最小请求数,才会去下面计算处理熔断器的打开,例如curCount > threshold,请求数已经超过了我们的阈值,才下面去进行状态判断。

protected boolean fromHalfOpenToClose() 
    if (currentState.compareAndSet(State.HALF_OPEN, State.CLOSED)) 
        resetStat();
        notifyObservers(State.HALF_OPEN, State.CLOSED, null);
        return true;
    
    return false;

protected boolean fromHalfOpenToOpen(double snapshotValue) 
    if (currentState.compareAndSet(State.HALF_OPEN, State.OPEN)) 
        updateNextRetryTimestamp();
        notifyObservers(State.HALF_OPEN, State.OPEN, snapshotValue);
        return true;
    
    return false;

​ 当然这里面还有取代的观察者模式相关逻辑,就不分析了。

四、规则设置的参数

​ 这些参数的设置,我们再直接贴官网的吧。

Field说明默认值
resource资源名,即规则的作用对象
grade熔断策略,支持慢调用比例/异常比例/异常数策略慢调用比例
count慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值
timeWindow熔断时长,单位为 s
minRequestAmount熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入)5
statIntervalMs统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入)1000 ms
slowRatioThreshold慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)

​ 由于我们上面的demo是异常数量处理,所以是没有设置slowRatioThreshold,也就是慢遍历阈值的。

以上是关于Sentinel的基本使用-熔断降级使用及源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Sentinel的基本使用-熔断降级使用及源码分析

Sentinel.01.Sentinel简介及简单使用

Sentinel的基本使用-流量控制及其源码调用分析

Sentinel的基本使用-流量控制及其源码调用分析

微服务之间的通讯安全-Sentinel入门之注解及熔断降级

阿里sentinel源码解析