sentinel 随笔 2-降级处理

news2024/12/30 22:10:45

0. 像喝点东西,但不知道喝什么


先来段源码,看一下 我们在dashboard 录入的降级规则,都映射到哪些字段上

package com.alibaba.csp.sentinel.slots.block.degrade;

public class DegradeRule extends AbstractRule {

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

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

    /**
     * Threshold count.
     */
    private double count;

    /**
     * Recovery timeout (in seconds) when circuit breaker opens. After the timeout, the circuit breaker will
     * transform to half-open state for trying a few requests.
     */
    private int timeWindow;

    /**
     * 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;

    /**
     * The threshold of slow request ratio in RT mode.
     */
    private double slowRatioThreshold = 1.0d;

    private int statIntervalMs = 1000;
}

1. sentinel 的断路器实现

  • 效果跟 netflix.hystrix 差不离
package com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker;

public abstract class AbstractCircuitBreaker implements CircuitBreaker {

    protected final DegradeRule rule;
    protected final int recoveryTimeoutMs;

    private final EventObserverRegistry observerRegistry;

    protected final AtomicReference<State> currentState = new AtomicReference<>(State.CLOSED);
    protected volatile long nextRetryTimestamp;

    public AbstractCircuitBreaker(DegradeRule rule) {
        this(rule, EventObserverRegistry.getInstance());
    }

    AbstractCircuitBreaker(DegradeRule rule, EventObserverRegistry observerRegistry) {
        AssertUtil.notNull(observerRegistry, "observerRegistry cannot be null");
        if (!DegradeRuleManager.isValidRule(rule)) {
            throw new IllegalArgumentException("Invalid DegradeRule: " + rule);
        }
        this.observerRegistry = observerRegistry;
        this.rule = rule;
        this.recoveryTimeoutMs = rule.getTimeWindow() * 1000;
    }

	// true, 成功获得令牌
    @Override
    public boolean tryPass(Context context) {
		// 断路器关闭
        // Template implementation.
        if (currentState.get() == State.CLOSED) {
            return true;
        }
		// 断路器开启
        if (currentState.get() == State.OPEN) {
			// 半开状态,允许通过1个请求来尝试,可行的话,即 true
            // For half-open state we allow a request for probing.
            return retryTimeoutArrived() && fromOpenToHalfOpen(context);
        }
        return false;
    }

	// 当前系统时间 >= 下一次重试时间
    protected boolean retryTimeoutArrived() {
        return TimeUtil.currentTimeMillis() >= nextRetryTimestamp;
    }

    protected boolean fromOpenToHalfOpen(Context context) {
        if (currentState.compareAndSet(State.OPEN, State.HALF_OPEN)) {
			// 通知订阅者: 状态的变化 开 -> 半开
            notifyObservers(State.OPEN, State.HALF_OPEN, null);
            Entry entry = context.getCurEntry();
			
			// 过程中断时的回调,回滚状态
            entry.whenTerminate(new BiConsumer<Context, Entry>() {
                @Override
                public void accept(Context context, Entry entry) {
                    // Note: This works as a temporary workaround for https://github.com/alibaba/Sentinel/issues/1638
                    // Without the hook, the circuit breaker won't recover from half-open state in some circumstances
                    // when the request is actually blocked by upcoming rules (not only degrade rules).
                    if (entry.getBlockError() != null) {
                        // Fallback to OPEN due to detecting request is blocked
                        currentState.compareAndSet(State.HALF_OPEN, State.OPEN);
                        notifyObservers(State.HALF_OPEN, State.OPEN, 1.0d);
                    }
                }
            });
            return true;
        }
        return false;
    }
    
    private void notifyObservers(CircuitBreaker.State prevState, CircuitBreaker.State newState, Double snapshotValue) {
        for (CircuitBreakerStateChangeObserver observer : observerRegistry.getStateChangeObservers()) {
            observer.onStateChange(prevState, newState, rule, snapshotValue);
        }
    }
}

从 DegradeRule.grade 可知道:

  • 默认的降级策略,即 响应时长
  • 除此之外,还支持 异常 的触发方式

下面分别借助两个 AbstractCircuitBreaker 的实现类来说明实现细节

1.1 ResponseTimeCircuitBreaker(RT) 请添加图片描述

package com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker;

// 根据请求的响应时间(慢调用比例)
public class ResponseTimeCircuitBreaker extends AbstractCircuitBreaker {

    private static final double SLOW_REQUEST_RATIO_MAX_VALUE = 1.0d;

    private final long maxAllowedRt;
    private final double maxSlowRequestRatio;
    private final int minRequestAmount;

    private final LeapArray<SlowRequestCounter> slidingCounter;

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

    ResponseTimeCircuitBreaker(DegradeRule rule, LeapArray<SlowRequestCounter> stat) {
        super(rule);
        AssertUtil.isTrue(rule.getGrade() == RuleConstant.DEGRADE_GRADE_RT, "rule metric type should be RT");
        AssertUtil.notNull(stat, "stat cannot be null");
        this.maxAllowedRt = Math.round(rule.getCount());
        this.maxSlowRequestRatio = rule.getSlowRatioThreshold();
        this.minRequestAmount = rule.getMinRequestAmount();
        this.slidingCounter = stat;
    }

    @Override
    public void onRequestComplete(Context context) {
        SlowRequestCounter counter = slidingCounter.currentWindow().value();
        Entry entry = context.getCurEntry();
        if (entry == null) {
            return;
        }
        long completeTime = entry.getCompleteTimestamp();
        if (completeTime <= 0) {
            completeTime = TimeUtil.currentTimeMillis();
        }
        long rt = completeTime - entry.getCreateTimestamp();
        if (rt > maxAllowedRt) {
            counter.slowCount.add(1);
        }
        counter.totalCount.add(1);

        handleStateChangeWhenThresholdExceeded(rt);
    }

    private void handleStateChangeWhenThresholdExceeded(long rt) {
        if (currentState.get() == State.OPEN) {
            return;
        }
        
        if (currentState.get() == State.HALF_OPEN) {
            // In detecting request
            // TODO: improve logic for half-open recovery
            if (rt > maxAllowedRt) {
                fromHalfOpenToOpen(1.0d);
            } else {
                fromHalfOpenToClose();
            }
            return;
        }

        List<SlowRequestCounter> counters = slidingCounter.values();
        long slowCount = 0;
        long totalCount = 0;
        for (SlowRequestCounter counter : counters) {
            slowCount += counter.slowCount.sum();
            totalCount += counter.totalCount.sum();
        }
        if (totalCount < minRequestAmount) {
            return;
        }
        double currentRatio = slowCount * 1.0d / totalCount;
        if (currentRatio > maxSlowRequestRatio) {
            transformToOpen(currentRatio);
        }
        if (Double.compare(currentRatio, maxSlowRequestRatio) == 0 &&
                Double.compare(maxSlowRequestRatio, SLOW_REQUEST_RATIO_MAX_VALUE) == 0) {
            transformToOpen(currentRatio);
        }
    }

    static class SlowRequestCounter {
        private LongAdder slowCount;
        private LongAdder totalCount;
    }
}

1.2 ExceptionCircuitBreaker

请添加图片描述

package com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker;

// 策略:异常比例、异常数
public class ExceptionCircuitBreaker extends AbstractCircuitBreaker {

    private final int strategy;	// 策略的枚举值
    private final int minRequestAmount;	// 最小请求数
    private final double threshold;	// 设置的阈值

    private final LeapArray<SimpleErrorCounter> stat;

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

    ExceptionCircuitBreaker(DegradeRule rule, LeapArray<SimpleErrorCounter> stat) {
        super(rule);
        this.strategy = rule.getGrade();
        boolean modeOk = strategy == DEGRADE_GRADE_EXCEPTION_RATIO || strategy == DEGRADE_GRADE_EXCEPTION_COUNT;
        AssertUtil.isTrue(modeOk, "rule strategy should be error-ratio or error-count");
        AssertUtil.notNull(stat, "stat cannot be null");
        this.minRequestAmount = rule.getMinRequestAmount();
        this.threshold = rule.getCount();
        this.stat = stat;
    }

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

		// step into ...
        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;
        }
        
		// 把这个异常计数传播到整个时间窗(LeapArray)的计数器中
        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);
        }
    }

    static class SimpleErrorCounter {
        private LongAdder errorCount;
        private LongAdder totalCount;

        public SimpleErrorCounter() {
            this.errorCount = new LongAdder();
            this.totalCount = new LongAdder();
        }

        public LongAdder getErrorCount() {
            return errorCount;
        }

        public LongAdder getTotalCount() {
            return totalCount;
        }

        public SimpleErrorCounter reset() {
            errorCount.reset();
            totalCount.reset();
            return this;
        }

        @Override
        public String toString() {
            return "SimpleErrorCounter{" +
                "errorCount=" + errorCount +
                ", totalCount=" + totalCount +
                '}';
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/499623.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring的第十二阶段(03):Spring实现AOP的简单使用

1、获取拦截方法的返回值和抛的异常信息 获取方法返回的值分为两个步骤&#xff1a; 1、在返回值通知的方法中&#xff0c;追加一个参数 Object result 2、然后在AfterReturning注解中添加参数returning“参数名” 获取方法抛出的异常分为两个步骤&#xff1a; 1、在异常通…

Python入门(五)列表(二)

列表&#xff08;二&#xff09; 1.组织列表1.1 使用方法sort()对列表永久排序1.2 使用函数sorted()对列表临时排序1.3 倒着打印列表1.4 确定列表的长度1.5 使用列表时避免索引错误 2.操作列表2.1 遍历整个列表2.1.1 for循环2.1.2 在for循环中执行更多操作2.1.3 在for循环结束后…

用C语言编写,计算并输出下列多项式值 s=1+1/(1+2)+1/(1+2+3)+ ..1/(1+2+3...+50)

C/C语言程序设计题目 用C语言编写&#xff0c;计算并输出下列多项式值 s11/(12)1/(123) …1/(123…50) C语言设计编程代码 #include <stdio.h>int main() {double s 0; // 初始化s的值为0int n 0; // 初始化n的值为0for (int i 1; i < 50; i) {n i; // 累加n的…

scatterlist

在内核驱动程序的很多代码中&#xff0c;都能够看到类似sgdma的内容&#xff0c;sgdma全称为Scatter/Gather DMA(散列/收集 DMA)&#xff0c;内核中抽象了scatterlist和sg table用来描述和管理这种需要做散列和收集的DMA缓冲区。在内核中设计scatterlist&#xff0c;主要出于两…

BDCC - Lambda VS Kappa

文章目录 概述区别Lambda架构:Kappa架构:总结 适用场景:案例:小结 概述 Lambda架构和Kappa架构都是用于处理大数据的架构模式。 Lambda架构使用了批处理和流处理两种不同的处理方式来处理数据。数据首先通过流处理层进行实时处理&#xff0c;然后再通过批处理层进行离线处理&…

开源C#代码生成器,SmartSoftHelp 开发辅助工具

开源代码生成器&#xff0c;支持自定义生成代码&#xff0c;开源生成代码&#xff0c;自己编写生成格式&#xff0c;最方便的代码生成器 下载地址&#xff1a; 百度网盘 请输入提取码

《时间从来不语,却回答了所有问题》笔记三

目录 感悟 经典摘录 假若我再上一次大学 不完满才是人生 走运与倒霉 毁誉 我的座右铭 二月兰 观天池 火车上观日出 感悟 人这个万物之灵却偏偏有了感情&#xff0c;有了感情就有了悲欢。自古及今&#xff0c;海内海外&#xff0c;一个百分之百完满的人生是没有的&…

《Java虚拟机学习》JVM虚拟机是如何执行方法

1. 重载与重写方法 重载 重载 &#xff1a;同一个类中&#xff0c;方法名相同&#xff0c;形参列表不用&#xff0c;与返回值无关 对于重载方法的执行&#xff0c;JVM遵循下面的三条规则&#xff1a; 在不考虑对基本类型自动装拆箱&#xff08;auto-boxing&#xff0c;auto-…

图嵌入表示学习—Graph Embeddings

Embedding Entire Graphs 一、图嵌入向量基本概念 与Node Embeddings不同&#xff0c;Graph Embeddings是对整个图或子图进行编码而忽略其中的节点。应用场景包括异常检测或分子有毒检测。 二、对Node Embeddings求和或求和后平均 如图&#xff0c;首先对图/子图中的节点进行…

shell与shell script 学习总结

文章目录 shell与shell script 学习总结vivim可视化区块多文件编辑多窗口功能vim环境设置与记录中文编码问题语系编码转换 Shell的变量功能变量的使用与设置echo(变量的使用)变量设置的规则 环境变量的功能env(观察环境变量)set(观察所有变量)unset(取消变量) locale(语系变量)…

【Hello Network】TCP协议相关理解

作者&#xff1a;小萌新 专栏&#xff1a;网络 作者简介&#xff1a;大二学生 希望能和大家一起进步 本篇博客简介&#xff1a;补充下对于TCP协议的各种理解 TCP协议相关实验 TCP相关试验理解CLOSE_WAIT状态理解TIME_WAIT状态解决TIME_WAIT状态引起的bind失败的方法理解listen的…

分布式 03 富文本处理插件和图片文件上传

01.使用富文本编辑器来上传图片文件。 02.最开始在html文件中去使用相关富文本的插件。 引入相关文件 <link href"/js/kindeditor-4.1.10/themes/default/default.css" type"text/css" rel"stylesheet"> <script type"text/java…

Redis面试题(上)

1.什么是 Redis&#xff1f; Redis 是一种基于内存的数据库&#xff0c;对数据的读写操作都是在内存中完成&#xff0c;因此读写速度非常快&#xff0c;常用于缓存&#xff0c;消息队列、分布式锁等场景。 Redis 提供了多种数据类型来支持不同的业务场景&#xff0c;比如 Strin…

Python机器学习入门 -- 支持向量机学习笔记

文章目录 前言一、支持向量机简介二、支持向量机的数学原理1. 距离解算2. 目标函数3. 约束下的优化求解4. 软间隔优化5. 核函数变换 三、Python实现支持向量机1. 惩罚力度对比2. 高斯核函数3. 非线性SVM 总结 前言 大部分传统的机器学习算法都可以实现分类任务&#xff0c;但这…

干货丨你真的了解反应持续时间吗?

Hello&#xff0c;大家好&#xff01; 这里是壹脑云科研圈&#xff0c;我是喵君姐姐~ 在今天的推文里&#xff0c;要给大家分享的是一种灵活、免费的心理科学工具——反应持续时间&#xff0c;快来一起看看哦~ 01 导读 简单按键的反应持续时间是一种容易获得但未被充分利用…

C++相比于C语言增加的8个小特性(详解)

C相比于C语言增加的8个小特性&#xff08;详解&#xff09; 文章目录 C相比于C语言增加的8个小特性&#xff08;详解&#xff09;一、命名空间二、C输入和输出三、缺省参数四、函数重载五、引用六、内联函数七、auto关键字八、指针空值nullptr总结 一、命名空间 c的命名空间是…

从一到无穷大 #8 Arrow,Parquet and ORC

文章目录 引言ArrowParquetNested EncodingRepetition LevelsDefinition Levels 列化压缩 ORC 引言 以我的机器为例来做一个简单的计算&#xff1a; 执行cat /proc/cpuinfo |grep MHz|uniq可以看到目前机器中CPU频率&#xff0c;得到值 2494.140MHZ&#xff5e;2494140000HZ&…

【算法】——全排列算法讲解

前言&#xff1a; 今天&#xff0c;我给大家讲解的是关于全排列算。我会从三个方面去进行展开&#xff1a; 首先&#xff0c;我会给大家分析关于全排列算法的思想和定义&#xff1b;紧接着通过手动实现出一个全排列代码来带大家见见是怎么实现的&#xff1b;最后我会给出两道题…

ESP32单片机入门篇

目录 一、ESP32单片机的基本概念 1.双核架构 2. Wi-Fi和蓝牙功能 3. 集成多种外设 4. 支持多种操作系统 二、开发环境 1. Arduino IDE 2. ESP-IDF 三、开发语言 四、注意事项 五、代码例程 &#xff08;1&#xff09;点亮LED灯 1. 电路图 2. 代码 3. 代码注释 …

【精品】Java-Stream流详解

Java-Stream流详解 如何学会JDK8中的Stream流&#xff0c;用它来提高开发效率&#xff1f;创建不可变的集合&#xff08;Immutable 不可变的&#xff09;场景方法 初试 Stream 流Stream 流的思想Stream 流的作用Stream 流的使用步骤Stream 流的中间方法Stream 流的终结方法 如何…