Alibaba Sentinel DegradeRule 降级规则源码

降级方式

image.png

Alibaba Sentinel 支持多种降级方式:

  1. 根据响应时间:判断单位时间内平均响应时间是否达到阈值;
  2. 根据异常比例:判断单位时间内,异常数量和异常比例是否达到阈值;
  3. 根据异常数量:判断单位时间内异常数量是否达到阈值;

一旦触发熔断,熔断开关将会打开,这时将拒绝所有请求,拒绝时间为设置的降级时间间隔。通过源码我们可以发现,Sentinel直接使用的是ScheduledExecutorService开启的一个延迟任务来实现降级时间间隔。

如:响应时间达到阈值,并且熔断时间间隔配置为5S,这时熔断开关会打开,并且在5S内拒绝所有请求,当5S后熔断开关再次关闭,这时会放行请求,如果放行请求后又触发了熔断,那么又需要等5S钟。

熔断降级规则说明

熔断降级规则(DegradeRule)包含下面几个重要的属性:

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 引入)

最新参数请看官网

源码

public class DegradeRule extends AbstractRule {

    @SuppressWarnings("PMD.ThreadPoolCreationRule")
    private static ScheduledExecutorService pool = Executors.newScheduledThreadPool(
        Runtime.getRuntime().availableProcessors(), new NamedThreadFactory("sentinel-degrade-reset-task", true));

    public DegradeRule() {}

    public DegradeRule(String resourceName) {
        setResource(resourceName);
    }

    /**
     * RT threshold or exception ratio threshold count.
     */
    private double count;

    /**
     * Degrade recover timeout (in seconds) when degradation occurs.
     */
    private int timeWindow;

    /**
     * Degrade strategy (0: average RT, 1: exception ratio, 2: exception count).
     */
    private int grade = RuleConstant.DEGRADE_GRADE_RT;

    /**
     * Minimum number of consecutive slow requests that can trigger RT circuit breaking.
     *
     * @since 1.7.0
     */
    private int rtSlowRequestAmount = RuleConstant.DEGRADE_DEFAULT_SLOW_REQUEST_AMOUNT;

    /**
     * Minimum number of requests (in an active statistic time span) that can trigger circuit breaking.
     *
     * @since 1.7.0
     */
    private int minRequestAmount = RuleConstant.DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT;

    ...

    // Internal implementation (will be deprecated and moved outside).

    private AtomicLong passCount = new AtomicLong(0);
    private final AtomicBoolean cut = new AtomicBoolean(false);

    @Override
    public boolean passCheck(Context context, DefaultNode node, int acquireCount, Object... args) {
        // 判断熔断开关的状态
        if (cut.get()) {
            return false;
        }

        ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(this.getResource());
        if (clusterNode == null) {
            return true;
        }

        if (grade == RuleConstant.DEGRADE_GRADE_RT) {  // 基于RT的熔断模式
            // 获取平均RT
            double rt = clusterNode.avgRt();
            // 判断平均RT是否达到阈值,如果没有就放行
            if (rt < this.count) {
                passCount.set(0);
                return true;
            }

            // 如果RT达到阈值,还需要判断单位时间内请求量是否达到阈值(默认是5)
            if (passCount.incrementAndGet() < rtSlowRequestAmount) {
                return true;
            }
        } else if (grade == RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO) { // 基于异常比例的熔断策略
            double exception = clusterNode.exceptionQps();
            double success = clusterNode.successQps();
            double total = clusterNode.totalQps();
            // If total amount is less than minRequestAmount, the request will pass.
            // 判断单位时间内请求数量是否发到熔断阈值(默认5)
            if (total < minRequestAmount) {
                return true;
            }

            // In the same aligned statistic time window,
            // "success" (aka. completed count) = exception count + non-exception count (realSuccess)
            double realSuccess = success - exception;
            // 判断单位时间内异常请求数量是否达到阈值
            if (realSuccess <= 0 && exception < minRequestAmount) {
                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;
    }

    private static final class ResetTask implements Runnable {

        private DegradeRule rule;

        ResetTask(DegradeRule rule) {
            this.rule = rule;
        }

        @Override
        public void run() {
            rule.passCount.set(0);
            rule.cut.set(false);
        }
    }
}

基于RT的熔断流程图:

image.png

总结

  1. 有一个值得借鉴的地方,使用ScheduledExecutorService来实现延迟任务的执行。
  2. 基于RT的熔断,框架是基于所有请求的平均响应时间来实现的,这种方式不会产生上下文切换。还有一种简单的方式,这种方式采用FutureTask机制,但是会产生上下文切换,如:
ExecutorService executorService = Executors.newFixedThreadPool(10);
FutureTask<String> futureTask = new FutureTask<String>(() -> {
    // 业务逻辑
    return "处理结果";
});
executorService.submit(futureTask);
futureTask.get(5, TimeUnit.SECONDS);
Sentinel 是一款开源的分布式系统防护组件,主要用于服务的流量控制、熔断降级和系统负载保护等。Sentinel 提供了一种基于注解和 API 的自定义限流方式。 自定义限流的主要步骤如下: 1. 定义资源名:在 Sentinel 中,资源是需要受到限流保护的对象。可以使用 `@SentinelResource` 注解来定义资源名。 ```java @SentinelResource(value = "myResource", blockHandler = "handleBlock") public void myMethod() { // 方法逻辑 } // 定义限流规则 private void handleBlock(BlockException ex) { // 处理限流逻辑 } ``` 2. 配置限流规则:通过 Sentinel Dashboard 或者代码方式配置限流规则,即设置每个资源的 QPS 阈值、流控模式等。 3. 触发限流:当资源的访问量超过限流规则中的阈值时,Sentinel 会触发限流,执行 blockHandler 中定义的逻辑。 以上是基于注解的方式,如果希望基于 API 进行自定义限流,可以使用 Sentinel 提供的 `SphU` 和 `Tracer` 等类进行手动埋点和统计。例如: ```java // 定义资源名 String resourceName = "myResource"; // 手动埋点 Entry entry = null; try { entry = SphU.entry(resourceName); // 执行业务逻辑 } catch (BlockException ex) { // 处理限流逻辑 } finally { if (entry != null) { entry.exit(); } } // 手动统计指标 Tracer.traceCount(resourceName, 1); ``` 通过自定义限流,可以灵活地根据业务需要对不同的资源进行限流保护,提高系统的可靠性和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值