源码模块
- sentinel-adapter:适配器模块,主要实现了对一些常用框架的适配
- sentinel-benchmark:基准测试模块,对核心代码的精确性提供基准测试
- sentinel-cluster:集群流控,主要是负责sentinel集群流控相关功能的
- sentinel-core:限流核心模块,限流、降级、熔断等功能都在这里实现
- sentinel-demo:样例模块,可以参考怎么使用sentinel进行限流、降级
- sentinel-extension扩展模块,主要对DataSource进行了部分拓展实现
- sentinel-loggin:拓展模块,主要是对DataSource进行了部分拓展实现
- sentinel-transport:传输模块,提供了基本的监控服务端和客户端的APi接口,以及一些基于不同库的实现(Http和Netty)
Node、Context、CtEntry讲解
在源码分析前还是讲一下Node、Context、CtEntry的逻辑,后面会大量的用到
当属于同一个应用下的请求过来后,会首先判断是否有Root,没有的话就会新建一个Root节点。 再往下就会出现EntranceNode、DefaultNode、CLusterNode三种node,那这三种节点又是什么关系呢?我们首先看一下这三个节点类的关系图:
其中StatsticNode是用作数据统计的,那根据继承关系其他三个Node也是进行数据统计的,那么它们分别的作用是什么那?在讲这三个Node前,大家要明白Sentinel的几个核心概念Context、资源操作符CtEntry:
- Context:对资源操作的上下文,里面包含EntranceNode(入口节点),如果代码中没有指定context,则会创建一个name为sentinel_default_context的默认context。一个Context生命周期可以包含多个资源操作CtEntry。Context生命周期中的最后一个资源在exit时会清理Context
- CtEntry:它会保存一次资源访问链路元数据和该资源所对应的实时信息,每个资源操作都必须属于一个Context
接下来就是三个节点:
- StatisticNode:统计节点,是Node接口的实现类,用于完成数据统计
- EntranceNode:入口节点,一个Context会有一个入口节点,用于统计当前Context的总体流量数据
- DefaultNode:默认节点,用于统计一个资源在当前Context中的流量数据
- ClusterNode:集群节点,用于统计一个资源在所有Context中的总体流量数据 而Sentinel也是通过滑动窗口算法高效的实现数据统计
这些逻辑后面代码都会体现,需要认真看
入口
这里选用demo模块下面的sentinel-demo-transport-spring-mvc来看sentinel是如何做限流的
entry = SphU.entry(“demo-hello-api”)
在获取sph这个静态属性的时候,会调用静态方法
Env.sph
InitExecutor.doInit():
这里一共做了两件事:
- 通过Spi机制 获取所有InitFunc的类,并且排序
- 调用它们的init去加载资源,其中不同的initFun分别有:
- CommandCenterInitFunc:用于初始化所有CommandCenter,对外提供客户端的数据处理功能
- DefaultClusterClientInitFunc:用于初始化集群限流客户端的所需资源
- DefaultClusterServerInitFunc:用于初始化集群限流服务端的所需资源
- ParamFlowStatisticSlotCallbackInit:用于初始化参数流控回调
CtSph.entry
总结一下这里做了哪些事:
- 获取上下文Context
- 使用责任链创建功能插槽
- 走责任链对资源对象进行限流等操作
ContextUtil.getContext()
先看ContextUtil的静态代码:
主要做了这些事:
- 创建了一个EntranceNode(入口节点)
- 通过Constants.Root创建一个root节点,然后将创建的node节点加入到root的子节点中
- 存入到contextNameNodeMap
这里并没有创建context,只是创建Node,并且把name和Node放到一个map中
InternalContextUtil.internalEnter
- 在获取上下文时,首先会从线程中获取到Context,或获取到就直接返回
- 如果是线程首次获取,则为空;然后会根据context的名称去缓存中去找表示入口的节点EntranceNode,如果Entrancenode为空则会通过一个双重检测锁的机制创建一个EntranceNode,然后会将Context对象中的入口节点设置为新创建的 EntranceNode。最后将context对象存入到ThreadLocal中。
这里才会真正的创建Context,并且设置Node、Origin,并且把它放到ThreadLocal
lookProcessChain(resourceWrapper)
�
如果chainMap没有chain,那就创建一个:
SlotChainProvider.newSlotChain():
slotChainBuilder.build():
这里会根据META-INF/services/com.alibaba.csp.sentinel.slotchain.ProcessorSlot读取到ProcessorSlot文件内存进行初始化,然后把它们放到一个责任链中
�构建出来的chain链:
new CtEntry(resourceWrapper, chain, context, count, args)
�创建资源操作对象
chain.entry(context, resourceWrapper, null, count, prioritized, args)
开始链式调用了
NodeSelectorSlot
这个 Slot 会选择或创建相应的 Node(节点),以便其他 Slot 可以针对这些节点进行统计和控制。
在执行完这段代码之后:
context里面的entranceNode(入口节点)会在它的childList新建一个Node节点:
ctEntry里面的curNode会设置为当前节点:
最终会形成这样:
ClusterBuilderSlot
这个node节点是上面创建的DefaultNode,会设置它的clusterNode
最终形成:
LogSlot
在执行保护策略或流量控制逻辑时记录相关的日志,便于监控和故障诊断。
�
StatisticSlot
收集和维护资源(如 API 调用)的运行时指标,如 QPS、线程数、响应时间等
会继续往后面调用,最终在去统计�
AuthoritySlot
检查请求是否有权访问某资源,基于黑名单或白名单进行访问控制。
�
SystemSlot
根据系统的负载情况(如 CPU 使用率、总体 QPS、平均响应时间等)动态调整流量入口,以防止系统过载。
�
FlowSlot
�实现流量控制规则,如 QPS 控制、并发线程数控制等,防止资源被过度使用
checkFlow()源码:
这里就是去判断指定资源的流控规则是否能够正常通过,若不能通过则抛出异常,被限流了。且后续不再应用。其中canPassCheck就是进行详细判断的方法。方法具体的获取实时流量数据并进行判断的相关源码请参考后续集群流控源码分析
DefaultCircuitBreakerSlot
当资源的运行状况低于预设的阈值时,自动切断对该资源的访问,经过一段时间后再尝试恢复,以防止系统雪崩�
�
�
DegradeSlot
根据资源的运行状态(如异常比例、响应时间)动态地进行降级操作,如返回一个默认值或抛出异常,以保护系统稳定运行�
�
总结
sentinel限流处理流程如下: