流控神器Sentinel指南:熔断降级源码分析

Sentinel 熔断降级(后面简称为降级)会在调用链路中某个资源出现不稳定状态时,例如调用超时或异常比例升高,对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都抛出 DegradeException。

Sentinel的降级检测在DegradeSlot中:

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

@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
    fireExit(context, resourceWrapper, count, args);
}

这里通过DegradeRuleManager来判断是否熔断。

 public static void checkDegrade(ResourceWrapper resource, Context context, DefaultNode node, int count)
    throws BlockException {
    // degradeRules保存了所有的熔断规则,key为ResourceName,value为 Set<DegradeRule> 
    Set<DegradeRule> rules = degradeRules.get(resource.getName());
    if (rules == null) {// 如果没有设置规则,那么直接返回
        return;
    }

    for (DegradeRule rule : rules) {
        if (!rule.passCheck(context, node, count)) {
            throw new DegradeException(rule.getLimitApp(), rule);
        }
    }
}

取出熔断规则对象后,逐个遍历,调用了DegradeRule的paseCheck方法,如果过不通过抛出DegradeException异常。

DegradeRule中重要的成员变量如下:

private int grade 
private double count;
private int timeWindow;
private final AtomicBoolean cut = new AtomicBoolean(false);
private AtomicLong passCount = new AtomicLong(0);
private static ScheduledExecutorService pool = Executors.newScheduledThreadPool(
        Runtime.getRuntime().availableProcessors(), new NamedThreadFactory("sentinel-degrade-reset-task", true));

  • grade为熔断的策略,分别可以为rt(一秒内平均响应时间)、异常比例(1秒内)、异常数。

  • count对应上面三种策略

  • timeWindow为时间窗口的长度。

  • cut表示该资源是否已经降级

  • passCount用于记录尝试次数,当每当降级后会在接下来的时间窗口内直接失败,过了该窗口后,后尝试一定次数,passCount就是用于统计这个值。

  • pool 计划任务线程池,发生降级是,启动计划任务,在时间窗口后关闭降级开关。

下面具体分析熔断的内部实现

 @Override
public boolean passCheck(Context context, DefaultNode node, int acquireCount, Object... args) {
    if (cut.get()) {// cut是AtomicBoolean类型,默认false
        return false;
    }
    // 获取到该资源对应的ClusterNode
    ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(this.getResource());
    if (clusterNode == null) {
        return true;
    }
    // 下面是三种降级策略
    if (grade == RuleConstant.DEGRADE_GRADE_RT) {
        double rt = clusterNode.avgRt();
        if (rt < this.count) {
            passCount.set(0);
            return true;
        }
        if (passCount.incrementAndGet() < RT_MAX_EXCEED_N) {
            return true;
        }
    } else if (grade == RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) {
        double exception = clusterNode.exceptionQps();
        double success = clusterNode.successQps();
        double total = clusterNode.totalQps();
        // if total qps less than RT_MAX_EXCEED_N, pass.
        if (total < RT_MAX_EXCEED_N) {
            return true;
        }
        double realSuccess = success - exception;
        if (realSuccess <= 0 && exception < RT_MAX_EXCEED_N) {
            return true;
        }
        if (exception / success < count) {
            return true;
        }
    } else if (grade == RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {
        double exception = clusterNode.totalException();
        if (exception < count) {
            return true;
        }
    }
    if (cut.compareAndSet(false, true)) {
        ResetTask resetTask = new ResetTask(this);
        pool.schedule(resetTask, timeWindow, TimeUnit.SECONDS);
    }
    return false;
}

我们知道ClusterNode保存的是一个资源的所有数据,不管它是哪个EntranceNode下的数据,都会汇总在ClusterNode中。

  • 降级策略为rt时,通过ClusterNode获取到上一秒内所有该资源请求的平均响应时间,如果小于设置的值,重置passCount,返回ture。
    如果过大于设置的值,增加尝试次数,若没超过5,则返回true,否则修改cut的值进行熔断。与此同时,启动定时任务,在timeWindow之后修改降级开关的值。

  • 如果降级策略为异常率,获取到上一秒总内请求数、异常数、成功数,若总请求小于尝试常数(5),返回true,否则计算真正成功次数,与success对应的block,即使在业务代码中跑出了异常
    success也会递增,因此realSuccess = success-exception,如果exception小于5,则通过,如果异常率小于设置的值,也通过,否则和上面一样,修改降级开关的值。并开启计划任务。

  • 如果策略为异常次数,则直接比较异常数和设定的值,如果小于则通过,否则降级。

总结:
本文对Sentinel的三种熔断降级策略从代码层面进行了分析,逻辑处理在DegradeSlot中,该slot处于ClusterBuilderSlot、StatisticSlot之后,因此所需要的success、exception等数据已经就绪。计划任务线程池作用是修改降级开关,相当于熔断自动恢复。这个任务启动之后只会执行一次(注意和fixRate的区别)并且在某一时刻最多有一个计划任务启动。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值