Sentinel 学习笔记3-责任链与工作流程
本文属于sentinel学习笔记系列。网上看到吴就业老师的专栏,原文地址如下:
https://blog.csdn.net/baidu_28523317/category_10400605.html
上一篇梳理了概念与核心类:Sentinel 学习笔记2- 概念与核心类介绍-CSDN博客
补一个点:
Sph: 定义了获取资源访问权的接口, 代表性接口: Entry entry = sph.entry(String name);
CtSph: Sph接口的默认实现, 负责校验流控规则, 当校验不通过时抛出BlockException异常
SphU: 工具类, 封装了CtSph的调用, 未做特殊处理, 当校验不通过时抛出BlockException异常
SphO: 工具类, 封装了CtSph的调用, 与SphU不同的是, 校验不通过时不会抛出异常, 而是return false;
责任链
当执行到 SphU.entry 接口时,就到了 Sentinel 的核心骨架--ProcessorSlotChain,将不同的 Slot 按照顺序串在一起(责任链模式),从而将不同的功能(限流、降级、系统保护)组合在一起。slot chain 其实可以分为两部分:统计数据构建部分(statistic)和判断部分(rule checking)。
其中:辅助资源指标数据统计的 ProcessorSlot
NodeSelectorSlot、ClusterBuilderSlot、StatisticSlot
判断部分:
- AuthoritySlot:实现黑白名单降级
- SystemSlot:实现系统自适应降级
- FlowSlot:实现限流降级
- DegradeSlot:实现熔断降级
核心结构如下图所示

- NodeSelectorSlot:给当前请求(context)绑定一个DefaultNode,如果不存在则根据 context 创建 DefaultNode,然后维护整个调用树的父子关系,然后将相关数据保存在 Context 中。
- ClusterBuilderSlot:给当前请求(context)绑定一个ClusterNode, 如果不存在首先根据 resourceName 创建 ClusterNode,并将其引用保存在 defaultNode中,然后再根据 origin 创建来源节点(类型为 StatisticNode),并将源节点保存在当前的调用点 Entry 中。一个资源(resource)的所有请求共享同一个ClusterNode。
- StatisticSlot:
StatisticSlot是 Sentinel 最为重要的类之一,用于根据规则判断结果进行相应的统计操作,先调用后续 Slot 的检查过程,如果检查通过增加各个维度的计数器。- entry 过程:依次执行后面的判断 slot。每个 slot 触发流控的话会抛出异常(BlockException 的子类)。若有 BlockException 抛出,则记录 block 数据;若无异常抛出则算作可通过(pass),记录 pass 数据。
- exit 过程:若无 error(无论是业务异常还是流控异常),记录 complete(success)以及 RT,线程数-1。
Sentinel 的整体工具流程就是使用责任链模式将所有的 ProcessorSlot 按照一定的顺序串成一个单向链表。比如辅助完成资源指标数据统计的 ProcessorSlot 的排序顺序为:
NodeSelectorSlot->ClusterBuilderSlot->StatisticSlot
责任链构建
实现将 ProcessorSlot 串成一个单向链表的是 ProcessorSlotChain,这个 ProcessorSlotChain 是由 SlotChainBuilder 构造的,代码如下:
@Spi(isDefault = true)
public class DefaultSlotChainBuilder implements SlotChainBuilder {@Overridepublic ProcessorSlotChain build() {ProcessorSlotChain chain = new DefaultProcessorSlotChain();//sortedSlotList获取所有的处理器对象List<ProcessorSlot> sortedSlotList = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();for (ProcessorSlot slot : sortedSlotList) {if (!(slot instanceof AbstractLinkedProcessorSlot)) {RecordLog.warn("The ProcessorSlot(" + slot.getClass().getCanonicalName() + ") is not an instance of AbstractLinkedProcessorSlot, can't be added into ProcessorSlotChain");continue;}//通过尾添法将职责slot添加到DefaultProcessorSlotChain当中chain.addLast((AbstractLinkedProcessorSlot<?>) slot);}return chain;}
}
- ProcessorSlotChain作为Slot的责任链,负责责任链的构建和执行。
- 责任链上的处理器对象 AbstractLinkedProcessorSlot 通过保存指向下一个处理器的对象的进行关联,整体以链表的形式进行串联。
处理器
ProcessorSlot 比较抽象,前面概念说了Entry表示一个资源的访问权, 通过Sph接口获取,这里的
fireEntry是获取访问权成功后的处理,具体的接口的定义如下:
public interface ProcessorSlot<T> {/*** Entrance of this slot.* 入口方法* @param context current {@link Context}* @param resourceWrapper current resource* @param param generics parameter, usually is a {@link com.alibaba.csp.sentinel.node.Node}* @param count tokens needed* @param prioritized whether the entry is prioritized* @param args parameters of the original call* @throws Throwable blocked exception or unexpected error*/void entry(Context context, ResourceWrapper resourceWrapper, T param, int count, boolean prioritized,Object... args) throws Throwable;/*** Means finish of {@link #entry(Context, ResourceWrapper, Object, int, boolean, Object...)}.* 调用下一个 ProcessorSlot#entry 方法* @param context current {@link Context}* @param resourceWrapper current resource* @param obj relevant object (e.g. Node)* @param count tokens needed* @param prioritized whether the entry is prioritized* @param args parameters of the original call* @throws Throwable blocked exception or unexpected error*/void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized,Object... args) throws Throwable;/*** Exit of this slot.* 出口方法* @param context current {@link Context}* @param resourceWrapper current resource* @param count tokens needed* @param args parameters of the original call*/void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);/*** Means finish of {@link #exit(Context, ResourceWrapper, int, Object...)}.* 调用下一个 ProcessorSlot#exit 方法* @param context current {@link Context}* @param resourceWrapper current resource* @param count tokens needed* @param args parameters of the original call*/void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
}
方法参数解析:
- context:当前调用链路上下文。
- resourceWrapper:资源 ID。
- param:泛型参数,一般用于传递 DefaultNode。
- count:表示申请占用共享资源的数量,只有申请到足够的共享资源才能继续执行。
- prioritized:表示是否对请求进行优先级排序,SphU#entry 传递过来的值是 false。
- args:调用方法传递的参数,用于实现热点参数限流。
之所以能够将所有的 ProcessorSlot 构造成一个 ProcessorSlotChain,还是依赖这些 ProcessorSlot 继承了 AbstractLinkedProcessorSlot 类。
public abstract class AbstractLinkedProcessorSlot<T> implements ProcessorSlot<T> {//当前节点的下一个节点private AbstractLinkedProcessorSlot<?> next = null;@Overridepublic void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)throws Throwable {if (next != null) { // 触发下一个处理器对象的处理next.transformEntry(context, resourceWrapper, obj, count, prioritized, args);}}@SuppressWarnings("unchecked")void transformEntry(Context context, ResourceWrapper resourceWrapper, Object o, int count, boolean prioritized, Object... args)throws Throwable {T t = (T)o;// 执行具体处理器的逻辑,由具体的处理器自行实现entry(context, resourceWrapper, t, count, prioritized, args);}@Overridepublic void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {if (next != null) {//调用下一个 ProcessorSlot 的 exit 方法next.exit(context, resourceWrapper, count, args);}}public AbstractLinkedProcessorSlot<?> getNext() {return next;}public void setNext(AbstractLinkedProcessorSlot<?> next) {this.next = next; // 绑定下一个处理器的逻辑}}
ProcessorSlotChain 也继承 AbstractLinkedProcessorSlot,多了2个方法
public abstract class ProcessorSlotChain extends AbstractLinkedProcessorSlot<Object> {/*** Add a processor to the head of this slot chain.* 添加到链表的头节点* @param protocolProcessor processor to be added.*/public abstract void addFirst(AbstractLinkedProcessorSlot<?> protocolProcessor);/*** Add a processor to the tail of this slot chain.* 添加到链表末尾* @param protocolProcessor processor to be added.*/public abstract void addLast(AbstractLinkedProcessorSlot<?> protocolProcessor);
}
ProcessorSlotChain 的默认实现类是 DefaultProcessorSlotChain,DefaultProcessorSlotChain 有一个指向链表头节点的 first 字段和一个指向链表尾节点的 end 字段。
public class DefaultProcessorSlotChain extends ProcessorSlotChain {// first,指向链表头节点AbstractLinkedProcessorSlot<?> first = new AbstractLinkedProcessorSlot<Object>() {@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)throws Throwable {super.fireEntry(context, resourceWrapper, t, count, prioritized, args);}@Overridepublic void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {super.fireExit(context, resourceWrapper, count, args);}};//尾节点AbstractLinkedProcessorSlot<?> end = first;@Overridepublic void addFirst(AbstractLinkedProcessorSlot<?> protocolProcessor) {protocolProcessor.setNext(first.getNext());first.setNext(protocolProcessor);if (end == first) {end = protocolProcessor;}}@Overridepublic void addLast(AbstractLinkedProcessorSlot<?> protocolProcessor) {end.setNext(protocolProcessor);end = protocolProcessor;}
说明:
- Sentinel 中的 Slot 需要实现 com.alibaba.csp.sentinel.slotchain.ProcessorSlot 的通用接口。
- 自定义 Slot 一般继承抽象类 AbstractLinkedProcessorSlot 且只要改写 entry/exit 方法实现自定义逻辑。
- Slot 通过 next 变量保存下一个处理器Slot对象。
- 在自定义实现的 entry 方法中需要通过 fireEntry 触发下一个处理器的执行,在 exit 方法中通过fireExit 触发下一个处理器的执行。
责任链执行
com.alibaba.csp.sentinel.CtSph#entryWithPriority()
private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)throws BlockException {Context context = ContextUtil.getContext();if (context instanceof NullContext) {// The {@link NullContext} indicates that the amount of context has exceeded the threshold,// so here init the entry only. No rule checking will be done.return new CtEntry(resourceWrapper, null, context);}if (context == null) {// Using default context.context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);}// Global switch is close, no rule checking will do.if (!Constants.ON) {return new CtEntry(resourceWrapper, null, context);}//开始构造chainProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);/** Means amount of resources (slot chain) exceeds {@link Constants.MAX_SLOT_CHAIN_SIZE},* so no rule checking will be done.*/if (chain == null) {return new CtEntry(resourceWrapper, null, context);}Entry e = new CtEntry(resourceWrapper, chain, context);try {// 驱动责任链上的第一个处理器,进而由处理器自驱动执行下一个处理器chain.entry(context, resourceWrapper, null, count, prioritized, args);} catch (BlockException e1) {e.exit(count, args);throw e1;} catch (Throwable e1) {// This should not happen, unless there are errors existing in Sentinel internal.RecordLog.info("Sentinel unexpected exception", e1);}return e;}
说明:
- 整个责任链上处理器的执行通过Invoker对象的驱动,而非责任链对象的驱动。
- DefaultProcessorSlotChain的entry首先头部对象first,进而触发处理器的自驱实现处理器的执行。
- 整体按照entry → fireEntry → transformEntry → entry 的循环顺序依次触发处理器的自驱。
相关文章:
Sentinel 学习笔记3-责任链与工作流程
本文属于sentinel学习笔记系列。网上看到吴就业老师的专栏,原文地址如下: https://blog.csdn.net/baidu_28523317/category_10400605.html 上一篇梳理了概念与核心类:Sentinel 学习笔记2- 概念与核心类介绍-CSDN博客 补一个点:…...
Latex 转换为 Word(使用GrindEQ )(英文转中文,毕业论文)
效果预览 第一步: 告诉chatgpt: 将latex格式中的英文翻译为中文(符号和公式不要动),给出latex格式第二步: Latex 转换为 Word(使用GrindEQ ) 视频 https://www.bilibili.com/video/BV1f242…...
使用Chat-LangChain模块创建一个与用户交流的机器人
当然!要使用Chat-LangChain模块创建一个与用户交流的机器人,你需要安装并配置一些Python库。以下是一个基本的步骤指南和示例代码,帮助你快速上手。 安装依赖库 首先,你需要安装langchain库,它是一个高级框架&#x…...
国家认可的人工智能从业人员证书如何报考?
一、证书出台背景 为进一步贯彻落实中共中央印发《关于深化人才发展体制机制改革的意见》和国务院印发《关于“十四五”数字经济发展规划》等有关工作的部署要求,深入实施人才强国战略和创新驱动发展战略,加强全国数字化人才队伍建设,持续推…...
【网络云计算】2024第51周-每日【2024/12/17】小测-理论-解析
文章目录 1. 计算机网络有哪些分类2. 计算机网络中协议与标准的区别3. 计算机网络拓扑有哪些结构4. 常用的网络设备有哪些,分属于OSI的哪一层5. IEEE802局域网标准有哪些 【网络云计算】2024第51周-每日【2024/12/17】小测-理论-解析 1. 计算机网络有哪些分类 计算…...
每日十题八股-2024年12月19日
1.Bean注入和xml注入最终得到了相同的效果,它们在底层是怎样做的? 2.Spring给我们提供了很多扩展点,这些有了解吗? 3.MVC分层介绍一下? 4.了解SpringMVC的处理流程吗? 5.Handlermapping 和 handleradapter有…...
网络方案设计
一、网络方案设计目标 企业网络系统的构成 应用软件 计算平台 物理网络及拓扑结构 网络软件及工具软件 网络互连设备 广域网连接 无论是复杂的,还是简单的计算机网络,都包括了以下几个基本元素 : 应用软件----支持用户完成专门操作的软件。…...
学习记录:electron主进程与渲染进程直接的通信示例【开箱即用】
electron主进程与渲染进程直接的通信示例 1. 背景: electronvue实现桌面应用开发 2.异步模式 2.1使用.send 和.on的方式 preload.js中代码示例: const { contextBridge, ipcRenderer} require(electron);// 暴露通信接口 contextBridge.exposeInMa…...
【Java数据结构】ArrayList类
List接口 List是一个接口,它继承Collection接口,Collection接口中的一些常用方法 List也有一些常用的方法。List是一个接口,它并不能直接实例化,ArrayList和LinkedList都实现了List接口,它们的常用方法都很相似。 Ar…...
HDR视频技术之十:MPEG 及 VCEG 的 HDR 编码优化
与传统标准动态范围( SDR)视频相比,高动态范围( HDR)视频由于比特深度的增加提供了更加丰富的亮区细节和暗区细节。最新的显示技术通过清晰地再现 HDR 视频内容使得为用户提供身临其境的观看体验成为可能。面对目前日益…...
71 mysql 中 insert into ... on duplicate key update ... 的实现
前言 这个也是我们经常可能会使用到的相关的特殊语句 当插入数据存在 唯一索引 或者 主键索引 相关约束的时候, 如果存在 约束冲突, 则更新目标记录 这个处理是类似于 逻辑上的 save 操作 insert into tz_test_02 (field1, field2) values (field11, 11) on duplicate …...
计算机网络-GRE Over IPSec实验
一、概述 前情回顾:上次基于IPsec VPN的主模式进行了基础实验,但是很多高级特性没有涉及,如ike v2、不同传输模式、DPD检测、路由方式引入路由、野蛮模式等等,以后继续学习吧。 前面我们已经学习了GRE可以基于隧道口实现分支互联&…...
你的第一个博客-第一弹
使用 Flask 开发博客 Flask 是一个轻量级的 Web 框架,适合小型应用和学习项目。我们将通过 Flask 开发一个简单的博客系统,支持用户注册、登录、发布文章等功能。 步骤: 安装 Flask 和其他必要库: 在开发博客之前,首…...
若依启动项目时配置为 HTTPS 协议
文章目录 1、需求提出2、应用场景3、解决思路4、注意事项5、完整代码第一步:修改 vue.config.js 文件第二步:运行项目第三步:处理浏览器警告 6、运行结果 1、需求提出 在开发本地项目时,默认启动使用的是 HTTP 协议。但在某些测试…...
学习思考:一日三问(学习篇)之匹配VLAN
学习思考:一日三问(学习篇)之匹配VLAN 一、学了什么(是什么)1.1 理解LAN与"V"的LAN1.2 理解"V"的LAN怎么还原成LAN1.3 理解二层交换机眼中的"V"的LAN 二、为何会产生需求(为…...
[WiFi] WiFi 802.1x介绍及EAP认证流程整理
802.1X Wi-Fi 802.1X 是一种网络访问控制协议,常用于保护无线网络。它提供了一种基于端口的网络访问控制机制,主要用于在用户和网络之间建立安全的连接。以下是 802.1X 的一些关键特点: 认证框架 802.1X 使用 EAP(可扩展认证协议…...
用C#(.NET8)开发一个NTP(SNTP)服务
完整源码,附工程下载,工程其实也就下面两个代码。 想在不能上网的服务器局域网中部署一个时间服务NTP,当然系统自带该服务,可以开启,本文只是分享一下该协议报文和能跑的源码。网上作为服务的源码不太常见,…...
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
MyBatis 是一个用于简化数据库操作的框架,它可以帮助开发人员通过映射语句轻松执行 SQL 查询,并且能够方便地实现对象与数据库表之间的映射。MyBatis 支持一对一、一对多和多对多等关联查询。下面我们来探讨一下 MyBatis 如何实现一对一、一对多的关联查…...
ABAP SQL 取日期+时间最新的一条数据
我们在系统对接的时候,外部系统可能会推送多个数据给到我们。 我们 SAP 系统的表数据中日期和时间是作为主键的,那么如果通过 ABAP SQL 取到最新日期的最新时间呢。 解决方案: 方式 1:SELECT MAX 可以通过两个 SELECT MAX 来取…...
【Rust自学】4.3. 所有权与函数
4.3.0 写在正文之前 在学习了Rust的通用编程概念后,就来到了整个Rust的重中之重——所有权,它跟其他语言都不太一样,很多初学者觉得学起来很难。这个章节就旨在让初学者能够完全掌握这个特性。 本章有三小节: 所有权࿱…...
Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
