![图片[1]-Sentinel核心概念与核心类详解:一文搞懂所有关键组件 - 速优课-速优课](http://www.suyouke.com/wp-content/uploads/2026/08/doubao_img_2304x1728_20260803_094414-1024x768.png)
Sentinel核心概念与核心类详解:一文搞懂所有关键组件
前言
上一篇我们讲了Sentinel的滑动窗口原理,了解了它是怎么统计QPS的。但要真正理解Sentinel的工作流程,光知道滑动窗口还不够。
Sentinel里有很多核心概念和类,比如Resource、Node、Context、Entry、ProcessorSlot……刚开始接触的时候,很容易被这些概念绕晕。
这篇文章,我就带大家把Sentinel的核心概念和核心类一次性梳理清楚。搞懂了这些,再看Sentinel的源码就会轻松很多。
一、三个基础概念
先从最基础的三个概念说起:资源、规则、降级。
| 概念 | 说明 | 举例 |
|---|---|---|
| 资源 | Sentinel保护的对象,可以是一个方法、一段代码、一个接口 | 比如GET:/user/info接口 |
| 规则 | 围绕资源设定的规则,包括限流、熔断、系统保护等 | 比如QPS不超过100 |
| 降级 | 流量过大时,根据规则有策略地拒绝部分请求 | 超过100 QPS的请求直接拒绝 |
可以这样理解:资源是保护对象,规则是保护策略,降级是执行结果。
二、资源指标统计相关的类
Sentinel的指标统计是以资源为维度的。先看几个核心类的继承关系:
![图片[2]-Sentinel核心概念与核心类详解:一文搞懂所有关键组件 - 速优课-速优课](https://www.suyouke.com/wp-content/uploads/2026/08/image-10-1024x571.png)
2.1 ResourceWrapper:资源的抽象表示
ResourceWrapper是对资源的封装,我们可以把它理解为”资源ID”。如果资源是一个接口,那资源名称通常就是接口URL,比如GET:/v1/demo。
public abstract class ResourceWrapper {
protected final String name; // 资源名称
protected final EntryType entryType; // 流量类型
protected final int resourceType; // 资源类型
}
三个字段的含义:
| 字段 | 含义 | 说明 |
|---|---|---|
| name | 资源名称 | 比如”GET:/v1/demo” |
| entryType | 流量类型 | IN=流入(接收请求),OUT=流出(发起请求) |
| resourceType | 资源类型 | Web、RPC、网关、数据库等 |
EntryType:流入还是流出?
EntryType是个枚举,只有两个值:
- IN:流入流量,也就是接收处理请求
- OUT:流出流量,也就是向外发起请求
怎么区分?举个例子:
- 你的服务收到前端的请求 → IN
- 你的服务调用其他服务的接口 → OUT
为什么要区分?因为熔断降级通常是在消费端统计OUT类型的流量——比如调用第三方接口失败率太高,就熔断不再调用。
资源类型有哪些?
Sentinel支持的资源类型:
public final class ResourceTypeConstants {
public static final int COMMON = 0; // 默认
public static final int COMMON_WEB = 1; // Web应用接口
public static final int COMMON_RPC = 2; // Dubbo等RPC接口
public static final int COMMON_API_GATEWAY = 3; // API网关
public static final int COMMON_DB_SQL = 4; // 数据库SQL
}
2.2 Node:统计节点接口
Node是Sentinel中非常核心的接口,它负责持有实时统计的指标数据,对外屏蔽滑动窗口的存在。
简单说就是:Node封装了所有指标统计的读写方法。
Node接口的方法很多,大致可以分为几类:
| 方法分类 | 代表方法 | 说明 |
|---|---|---|
| 查询总数 | totalRequest()、totalPass()、totalSuccess() | 总请求数、通过数、成功数 |
| 查询QPS | passQps()、blockQps()、successQps() | 各种QPS指标 |
| 查询耗时 | avgRt()、minRt() | 平均耗时、最小耗时 |
| 查询并发 | curThreadNum() | 当前占用线程数 |
| 记录数据 | addPassRequest()、addRtAndSuccess() | 记录通过、成功、耗时 |
| 记录异常/拒绝 | increaseBlockQps()、increaseExceptionQps() | 记录拒绝和异常 |
| 线程计数 | increaseThreadNum()、decreaseThreadNum() | 并发线程增减 |
Node有几个重要的实现类:StatisticNode、DefaultNode、ClusterNode、EntranceNode。我们一个个来讲。
2.3 StatisticNode:最基础的统计节点
StatisticNode,顾名思义就是”统计节点”,是Node接口的基础实现类,真正实现了实时指标数据统计。
public class StatisticNode implements Node {
// 秒级滑动窗口:2个Bucket,每个500毫秒,总共1秒
private transient volatile Metric rollingCounterInSecond = new ArrayMetric(2, 1000);
// 分钟级滑动窗口:60个Bucket,每个1秒,总共1分钟
private transient Metric rollingCounterInMinute = new ArrayMetric(60, 60 * 1000, false);
// 当前并发线程数
private LongAdder curThreadNum = new LongAdder();
}
一个StatisticNode里包含了:
| 组件 | 作用 |
|---|---|
| 秒级滑动窗口 | 统计实时QPS(默认2个500ms的窗口) |
| 分钟级滑动窗口 | 保存最近1分钟的历史数据(60个1秒的窗口) |
| 并发线程计数器 | 统计当前占用的线程数 |
为什么要两个滑动窗口?因为用途不一样:
- 秒级:用于实时限流判断,对精度要求高
- 分钟级:用于Dashboard展示、熔断判断等,需要更长时间的历史数据
比如:
- 获取当前秒的拒绝QPS → 用秒级窗口
- 获取前一秒的拒绝QPS → 用分钟级窗口
- 获取最小耗时 → 用秒级窗口
记录数据的时候,两个窗口都会同时更新:
@Override
public void addRtAndSuccess(long rt, int successCount) {
// 秒级滑动窗口
rollingCounterInSecond.addSuccess(successCount);
rollingCounterInSecond.addRT(rt);
// 分钟级滑动窗口
rollingCounterInMinute.addSuccess(successCount);
rollingCounterInMinute.addRT(rt);
}
并发线程数有什么用?
StatisticNode里的curThreadNum用于统计当前并发占用的线程数,用来实现信号量隔离。
- 接收到请求 → curThreadNum自增1
- 请求处理完 → curThreadNum自减1
比如Tomcat线程池有200个线程,如果不控制,一个慢接口可能把200个线程都占满,导致其他接口没法处理。通过控制并发线程数(比如最多20个线程处理这个接口),就能避免一个接口拖垮整个应用。
2.4 DefaultNode:资源维度的统计节点
DefaultNode是StatisticNode的子类,它的作用是:将资源ID和StatisticNode绑定在一起,实现以资源为维度的统计。
public class DefaultNode extends StatisticNode {
private ResourceWrapper id; // 资源ID
private volatile Set<Node> childList = new HashSet<>(); // 子节点集合
private ClusterNode clusterNode; // 对应的ClusterNode
}
三个字段的含义:
| 字段 | 含义 | 说明 |
|---|---|---|
| id | 资源ID | 这个Node统计的是哪个资源 |
| childList | 子节点集合 | 用于构建调用链树 |
| clusterNode | 集群节点 | 该资源对应的全局统计节点 |
DefaultNode继承了StatisticNode,所以它本身就具备统计能力。加上资源ID,就变成了”某个资源的统计节点”。
一个资源理论上可能有多个DefaultNode,这取决于是否有多个不同的Context(调用上下文)。不过通常情况下,一个资源在同一个Context下只有一个DefaultNode。
2.5 ClusterNode:全局维度的统计节点
ClusterNode用于统计每个资源全局的指标数据——不区分调用链路,一个资源ID只对应一个ClusterNode。
public class ClusterNode extends StatisticNode {
private final String name; // 资源名称
private final int resourceType; // 资源类型
// 按调用来源区分的统计数据
private Map<String, StatisticNode> originCountMap = new HashMap<>();
private final ReentrantLock lock = new ReentrantLock();
}
重点是originCountMap这个字段:它维护了每个调用来源的统计数据。
什么是调用来源?比如服务A调用服务B,那服务A就是来源。通过originCountMap,Sentinel可以实现按调用来源限流——比如只限制服务A的调用QPS,不限制服务C的。
2.6 EntranceNode:入口节点
EntranceNode是一个特殊的Node,它继承自DefaultNode,用于维护调用树。
public class EntranceNode extends DefaultNode {
public EntranceNode(ResourceWrapper id, ClusterNode clusterNode) {
super(id, clusterNode);
}
}
什么是调用树?从根节点到每个叶子节点,代表不同的请求调用链路。调用链路上经过的每个节点,对应该链路上被Sentinel保护的资源。
举个例子:在Web MVC应用中,Sentinel会统一创建一个名为sentinel_spring_web_context的Context,对应的EntranceNode就是入口。每个接口对应一个DefaultNode,都挂在这个EntranceNode的childList下面。
EntranceNode有个很实用的功能:统计整个应用的总QPS。
@Override
public double totalQps() {
double r = 0;
// 遍历所有子节点,把QPS加起来
for (Node node : getChildList()) {
r += node.totalQps();
}
return r;
}
2.7 各类Node的关系总结
用一张图来梳理一下:
![图片[3]-Sentinel核心概念与核心类详解:一文搞懂所有关键组件 - 速优课-速优课](https://www.suyouke.com/wp-content/uploads/2026/08/image-11-1024x407.png)
- StatisticNode:基础统计能力,包含秒级和分钟级滑动窗口
- DefaultNode:资源维度的统计,绑定了资源ID,有子节点列表
- ClusterNode:全局维度的统计,还支持按来源统计
- EntranceNode:入口节点,维护调用树,可以统计总QPS
三、Context与Entry:调用链路的上下文
理解了各种Node,接下来要理解Context和Entry。这两个是理解Sentinel工作流程的关键。
3.1 Context:调用链路上下文
Context代表调用链路的上下文,贯穿一次调用链路中的所有Entry。
Context里存了什么?
public class Context {
private final String name; // Context名称(入口名称)
private DefaultNode entranceNode; // 入口节点
private Entry curEntry; // 当前Entry
private String origin = ""; // 调用来源
}
| 字段 | 含义 | 说明 |
|---|---|---|
| name | Context名称 | 也就是调用链路入口名称 |
| entranceNode | 入口节点 | 当前调用树的入口节点(EntranceNode) |
| curEntry | 当前Entry | 当前正在处理的资源对应的Entry |
| origin | 调用来源 | 服务消费者的名称或IP |
Context是通过ThreadLocal传递的,只在调用链路的入口处创建。也就是说,同一个线程里的多次Sentinel调用,共用同一个Context。
举个实际的例子:
- 服务B是一个MVC应用,同时用了Sentinel的MVC适配和OpenFeign适配
- 服务B收到服务A的请求时,MVC拦截器创建了名为
sentinel_spring_web_context的Context - 服务B调用服务C时,OpenFeign适配器发现当前线程已经有Context了,就直接复用
- 整个调用链路用的是同一个Context
再打个比方:
- 路径一:A.a() → B.b() → C.c(),入口是A.a(),Context名称是”a_context”
- 路径二:D.d() → B.b() → C.c(),入口是D.d(),Context名称是”d_context”
虽然都调用了B.b()和C.c(),但因为入口不同,Context不同,对应的调用链树也是分开的。
3.2 Entry:资源调用的表示
Entry代表一次资源调用。每次调用SphU.entry()都会创建一个Entry。
Entry的抽象类定义:
public abstract class Entry implements AutoCloseable {
private long createTime; // 创建时间
private Node curNode; // 当前节点(DefaultNode)
private Node originNode; // 来源节点
private Throwable error; // 异常
protected ResourceWrapper resourceWrapper; // 资源
}
Entry的具体实现类是CtEntry:
class CtEntry extends Entry {
protected Entry parent = null; // 父Entry
protected Entry child = null; // 子Entry
protected ProcessorSlot<Object> chain; // 当前资源的处理器链
protected Context context; // 当前上下文
}
这里有个很重要的设计:父子Entry构成双向链表。
什么意思?比如在一次请求中,多次调用SphU.entry(),每次都会创建一个CtEntry,这些CtEntry通过parent和child指针连起来。
举个例子:
- 服务B收到服务A的请求,创建ctEntry1(parent为空)
- 服务B调用服务C,创建ctEntry2(parent=ctEntry1,ctEntry1.child=ctEntry2)
![图片[4]-Sentinel核心概念与核心类详解:一文搞懂所有关键组件 - 速优课-速优课](https://www.suyouke.com/wp-content/uploads/2026/08/image-12-1024x419.png)
为什么要搞双向链表?因为调用entry.exit()的时候,需要把Context的curEntry还原为上一个Entry。就像栈一样,后进先出。
3.3 ROOT与调用树
Sentinel里有一个全局的根节点:Constants.ROOT,类型是EntranceNode。
整个调用树的结构是这样的:
- 最顶层是ROOT(全局唯一)
- ROOT的子节点是各个入口的EntranceNode
- 每个EntranceNode的子节点是该入口下各个资源的DefaultNode
- 以此类推,形成一棵树
用一张图来表示:
![图片[5]-Sentinel核心概念与核心类详解:一文搞懂所有关键组件 - 速优课-速优课](https://www.suyouke.com/wp-content/uploads/2026/08/image-13-1024x583.png)
三者的关系:
- Constants.ROOT:全局根节点,所有EntranceNode都挂在它下面
- Context.entranceNode:当前调用链路的入口节点
- Entry.curNode:当前访问的资源节点
四、ProcessorSlot:功能插槽
ProcessorSlot直译是”处理器插槽”,是Sentinel实现各种降级功能的切入点。
Sentinel用了责任链模式,每个ProcessorSlot负责一项功能,多个Slot串成一条链,请求依次经过每个Slot的处理。
ProcessorSlot大致可以分为两类:
4.1 辅助统计的ProcessorSlot
这类Slot主要负责构建节点、统计数据:
| Slot名称 | 作用 |
|---|---|
| NodeSelectorSlot | 为当前资源创建DefaultNode,构建调用链树 |
| ClusterBuilderSlot | 创建ClusterNode,以及按调用来源的StatisticNode |
| StatisticSlot | 最重要的统计Slot,调用后续Slot后根据结果更新统计数据 |
4.2 实现降级功能的ProcessorSlot
这类Slot是真正实现降级功能的:
| Slot名称 | 功能 |
|---|---|
| AuthoritySlot | 黑白名单限流 |
| SystemSlot | 系统自适应限流 |
| FlowSlot | 限流降级 |
| DegradeSlot | 熔断降级 |
关于每个ProcessorSlot的具体实现,后面的文章会详细分析。
总结与思考
这篇文章内容比较多,我们来梳理一下整体脉络:
资源相关:
- ResourceWrapper:资源的抽象表示(名称、类型、流入流出)
统计节点相关(Node体系):
- StatisticNode:基础统计,秒级+分钟级滑动窗口
- DefaultNode:资源维度的统计,有子节点
- ClusterNode:全局统计,支持按来源统计
- EntranceNode:入口节点,可以统计总QPS
调用上下文相关:
- Context:调用链路上下文,ThreadLocal传递
- Entry/CtEntry:资源调用表示,父子Entry构成双向链表
- ROOT:全局根节点,维护调用树
功能插槽相关:
- NodeSelectorSlot、ClusterBuilderSlot、StatisticSlot:辅助统计
- AuthoritySlot、SystemSlot、FlowSlot、DegradeSlot:实现降级功能
几个关键理解点:
- 为什么需要这么多种Node? 因为统计的维度不一样。有资源维度、全局维度、调用链路维度、按来源维度。不同维度满足不同的需求。
- Context和Entry的关系? Context是整个调用链路的上下文,Entry是链路上每个资源的表示。一个Context里可以有多个Entry,形成双向链表。
- 调用树有什么用? 调用树清晰地展示了资源之间的调用关系,可以实现按链路限流、统计入口总QPS等功能。
理解了这些核心概念,下一篇我们就可以来看Sentinel的责任链模式和整体工作流程了。










请登录后查看评论内容