sentinel 随笔 3-降级处理

news2024/12/23 10:53:49

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/500592.html

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

相关文章

redis(1)---redis的安装以及五大基本类型的使用

一)认识NoSQL 格式化数据VS格式化 1)SQL关系型数据库&#xff0c;在关系型数据库里面表中的字段是有限制的&#xff0c;况且最好不要随意删除修改表结构&#xff0c;存入到关系型数据库中的数据&#xff0c;都是结构化的数据&#xff0c;对于这些数据都是必须要有格式化的要求&…

sentinel 随笔 2-流控

0. 想要个半个月的旅游 最近发现算法比较有意思一些&#xff0c;什么企业框架都是看不完的… 书接 FlowSlot 1. FlowRuleChecker.checkFlow() : 配置的规则校验类 sentinel 并没有对这个Checker进行抽象的设计&#xff0c;第一次看有些别扭… package com.alibaba.csp.sent…

01- 综述 (目标检测)

要点&#xff1a; 区分One-stage detector 和 Two-stage detector 参考链接&#xff1a;深度学习目标检测最全综述 - 爱码网 详细模型解读参考&#xff1a;目标检测简介 - 知乎 一 目标检测分类 1.1 发展历程 检测网络发布历程&#xff1a; 1.2 检测模型分类 2014年后目标…

C嘎嘎~~ [类 下篇之运算符重载]

类 下篇 之运算符重载 5.赋值运算符重载5.1运算符重载5.1.1 运算符的概念5..1.2 重载运费符的位置5.1.3运算符重载的实质 5.2 赋值运算符重载5.2.1深刻理解---编译器生成的默认赋值运算符重载5.2.2深刻理解---拷贝构造和赋值运算符重载5.2.3深刻理解---传参和返回值用引用修饰…

CUDA error: device-side assert triggered CUDA kernel errors might be asynchronously reported at some

问题描述&#xff1a; 在修改代码时&#xff0c;出现入下报错。 发生异常: RuntimeError CUDA error: device-side assert triggered CUDA kernel errors might be asynchronously reported at some other API call,so the stacktrace below might be incorrect. For debuggi…

Android Switch开关按钮使用和自定义样式(系列教程五)

Switch开关按钮简介 Switch开关按钮是Android中的基本控件之一&#xff0c;其本质上也是一个按钮&#xff0c;具有开和关两种展示状态。 Switch开关按钮基本使用 在布局文件中定义开关按钮&#xff1a; <LinearLayoutandroid:layout_width"300dp"android:layo…

Vivado安装后添加器件库

1.前言 通常安装Vivado时&#xff0c;由于软件完整安装的空间需求过于庞大&#xff0c;一般只会选择一部分器件进行安装。而随着学习和工作的进展&#xff0c;遇到新的赛灵思朋友是成长的里程碑&#xff0c;也是综合不能通过的绊脚石。 今天有幸认识了一位新的赛灵思朋友——…

云原生: istio+dapr构建多运行时服务网格

2020 年&#xff0c;Bilgin Ibryam 提出了 Multi-Runtime&#xff08;多运行时&#xff09;的理念&#xff0c;对基于 Sidecar 模式的各种产品形态进行了实践总结和理论升华。那到底什么是多运行时呢&#xff1f;首先还是得从分布式应用的四大类基本需求讲起。简单来讲任何分布…

刷题练习3

文章目录 题目一分析题解 题目二分析第一种第二种 题解第一种方法代码第二种方法代码 题目一 题目链接 描述 读入一个字符串str&#xff0c;输出字符串str中的连续最长的数字串 输入描述&#xff1a; 个测试输入包含1个测试用例&#xff0c;一个字符串str&#xff0c;长度不超…

牛顿迭代法解超越方程

牛顿迭代法解超越方程 L g T 2 2 π t a n h ( 2 π L d ) L\frac{gT^2}{2\pi}tanh(\frac{2\pi}{L}d) L2πgT2​tanh(L2π​d) 方程&#xff1a; f ( L ) L − g T 2 2 π t a n h ( 2 π L d ) 0 f(L)L-\frac{gT^2}{2\pi}tanh(\frac{2\pi}{L}d)0 f(L)L−2πgT2​tanh(L2π…

~项目启动~

rmq是什么&#xff1f; "rmq" 可能指的是 "RabbitMQ"&#xff0c;它是一种开源消息队列软件&#xff0c;采用 AMQP&#xff08;Advanced Message Queuing Protocol&#xff09;协议&#xff0c;可以用于支持异步处理、任务分发、解耦合等应用场景。Rabbit…

仪表检测与读数(一):仪表检测

基于YOLOv4的仪表检测 前言YOLOv4源码下载数据集处理与模型训练模型性能测试 前言 本系列是想记录一下自己实现的一种用于仪表检测与读数的方法&#xff0c;首先方法仅针对于单指针仪表和单行显示的数字仪表进行了检测与读数方法的设计。方法的整体思路是&#xff1a;第一步对拍…

[ 云计算 | Azure ] Chapter 06 | 计算服务之虚拟机、虚拟机规模集、Azure 容器、Azure App 与 Azure Functions

本系列已经更新文章列表&#xff08;已更新&#xff09;&#xff1a; [ Azure 云计算从业者 ] Chapter 03 | 描述云计算运营中的 CapEx 与 OpEx&#xff0c;如何区分 CapEx 与 OpEx[ Azure 云计算从业者 ] Chapter 04 | Azure核心体系结构组件之数据中心、区域与区域对、可用区…

【Qt】插件Plugin入门之Q_PLUGIN_METADATA()宏【2023.05.07】

摘要 分析Q_PLUGIN_METADATA宏的设计意图&#xff0c;站在设计者的意图进行插件的高屋建瓴式学习。 Meta-Object Compiler 简称MOC Qt 的 Meta-Object Compiler&#xff08;MOC&#xff09;是一个预处理器&#xff0c;用于处理带有特殊关键字的 C 文件&#xff0c;并生成用于…

Linux命令·netstat

netstat命令用于显示与IP、TCP、UDP和ICMP协议相关的统计数据&#xff0c;一般用于检验本机各端口的网络连接情况。netstat是在内核中访问网络及相关信息的程序&#xff0c;它能提供TCP连接&#xff0c;TCP和UDP监听&#xff0c;进程内存管理的相关报告。 如果你的计算机有时候…

详细版简单易学版TypeScript各类型声明

假如本地新建了一个b.ts文件 安装TypeScript&#xff1a;npm install -g typescript 编译代码&#xff1a;tsc b.ts 运行js&#xff1a;node b.js 在终端输入 tsc -init 生成 tsconfig.json 文件 类型注解&#xff1a;TypeScript里的类型注解是一种轻量级的为函数或变量添加约束…

Python中模块和包基础学习

目录 模块 引入模块 使用from...import语句引入模块中的指定变量或函数 使用import...as语句给模块起别名 使用dir()函数查看模块中的所有变量和函数 使用__name__变量判断模块是被导入还是直接执行 包 注意 示例 模块 Python中的模块是指一个文件&#xff0c;可以包…

2.1 掌握NumPy数组对象ndarray

2.1 掌握NumPy数组对象ndarray 2.2.1 创建数组对象1&#xff0e;数组创建2&#xff0e;数组属性&#xff1a;ndarray&#xff08;数组&#xff09;是存储单一数据类型的多维数组。3&#xff0e;数组数据类型 2.1.2 生成随机数random模块常用随机数生成函数 2.1.3 通过索引访问数…

Python中异常处理的学习

目录 异常的基本介绍 异常处理语句 抛出异常 异常的基本介绍 在Python中&#xff0c;如果程序出现错误&#xff0c;会抛出异常。异常是一种Python对象&#xff0c;它封装了错误的信息&#xff0c;并提供了一种处理错误的机制。Python中内置了很多异常类型&#xff0c;包括但…

C语言-学习之路-07

C语言-学习之路-07 内存管理作用域局部变量静态&#xff08;static&#xff09;局部变量全局变量extern全局变量声明全局函数和静态函数 内存分布内存分区 内存管理 作用域 C语言中变量的作用域可分为&#xff1a;代码作用域、函数作用域、文件作用域 局部变量 局部变量也叫…