try {
// Do some checking.
fireEntry(context, resourceWrapper, node, count, prioritized, args); // @1
// Request passed, add thread count and pass count.
node.increaseThreadNum(); // @2
node.addPassRequest(count);
if (context.getCurEntry().getOriginNode() != null) { // @3
// Add count for origin node.
context.getCurEntry().getOriginNode().increaseThreadNum();
context.getCurEntry().getOriginNode().addPassRequest(count);
}
if (resourceWrapper.getEntryType() == EntryType.IN) { // @4
// Add count for global inbound entry node for global statistics.
Constants.ENTRY_NODE.increaseThreadNum();
Constants.ENTRY_NODE.addPassRequest(count);
}
// Handle pass event with registered entry callback handlers.
for (ProcessorSlotEntryCallback handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) { // @5
handler.onPass(context, resourceWrapper, node, count, args);
}
} catch (PriorityWaitException ex) { // @6
node.increaseThreadNum();
if (context.getCurEntry().getOriginNode() != null) {
// Add count for origin node.
context.getCurEntry().getOriginNode().increaseThreadNum();
}
if (resourceWrapper.getEntryType() == EntryType.IN) {
// Add count for global inbound entry node for global statistics.
Constants.ENTRY_NODE.increaseThreadNum();
}
// Handle pass event with registered entry callback handlers.
for (ProcessorSlotEntryCallback handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
handler.onPass(context, resourceWrapper, node, count, args);
}
} catch (BlockException e) { // @7
// Blocked, set block exception to current entry.
context.getCurEntry().setError(e);
// Add block count.
node.increaseBlockQps(count);
if (context.getCurEntry().getOriginNode() != null) {
context.getCurEntry().getOriginNode().increaseBlockQps(count);
}
if (resourceWrapper.getEntryType() == EntryType.IN) {
// Add count for global inbound entry node for global statistics.
Constants.ENTRY_NODE.increaseBlockQps(count);
}
// Handle block event with registered entry callback handlers.
for (ProcessorSlotEntryCallback handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
handler.onBlocked(e, context, resourceWrapper, node, count, args);
}
throw e;
} catch (Throwable e) { // @8
// Unexpected error, set error to current entry.
context.getCurEntry().setError(e);
// Java开源项目【ali1024.coding.net/public/P7/Java/git】 This should not happen.
node.increaseExceptionQps(count);
if (context.getCurEntry().getOriginNode() != null) {
context.getCurEntry().getOriginNode().increaseExceptionQps(count);
}
if (resourceWrapper.getEntryType() == EntryType.IN) {
Constants.ENTRY_NODE.increaseExceptionQps(count);
}
throw e;
}
}
代码@1:首先调用 fireEntry,先调用 Sentinel Slot Chain 中其他的处理器,执行完其他处理器的逻辑,例如 FlowSlot、DegradeSlot,因为 StatisticSlot 的职责是收集统计信息。
代码@2:如果后续处理器成功执行,则将正在执行线程数统计指标加一,并将通过的请求数量指标增加对应的值。下文会对 Sentinel Node 体系进行详细的介绍,在 Sentinel 中使用 Node 来表示调用链中的某一个节点,每个节点关联一个资源,资源的实时统计信息就存储在 Node 中,故该部分也是调用 DefaultNode 的相关方法来改变线程数等,将在下文会向详细介绍。
代码@3:如果上下文环境中保存了调用的源头(调用方)的节点信息不为空,则更新该节点的统计数据:线程数与通过数量。
代码@4:如果资源的进入类型为 EntryType.IN,表示入站流量,更新入站全局统计数据(集群范围 ClusterNode)。
代码@5:执行注册的进入Handler,可以通过 StatisticSlotCallbackRegistry 的 addEntryCallback 注册相关监听器。
代码@6:如果捕获到 PriorityWaitException ,则认为是等待过一定时间,但最终还是算通过,只需增加线程的个数,但无需增加节点通过的数量,具体原因我们在详细分析限流部分时会重点讨论,也会再次阐述 PriorityWaitException 的含义。
代码@7:如果捕获到 BlockException,则主要增加阻塞的数量。
代码@8:如果是系统异常,则增加异常数量。
我想上面的代码应该不难理解,但涉及到统计指标数据的变化,都是调用 DefaultNode node 相关的方法,从这里也可以看出,Node 将是实时统计数据的直接持有者,那毋容置疑接下来将重点来学习 Node,为了知识体系的完备性,我们先来看一下 StatisticSlot 的 exit 方法。
[](()1.2 StatisticSlot exit 详解StatisticSlot#exit
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object… args) {
DefaultNode node = (DefaultNode)context.getCurNode();
if (context.getCurEntry().getError() == null) { // @1
// Calculate response time (max RT is TIME_DROP_VALVE).
long rt = TimeUtil.currentTimeMillis() - context.getCurEntry().getCreateTime();
if (rt > Constants.TIME_DROP_VALVE) {
rt = Constants.TIME_DROP_VALVE;
}
// Record response time and success count.
node.addRtAndSuccess(rt, count);
if (context.getCurEntry().getOriginNode() != null) {
context.getCurEntry().getOriginNode().addRtAndSuccess(rt, count);
}
node.decreaseThreadNum();
if (context.getCurEntry().getOriginNode() != null) {
context.getCurEntry().getOriginNode().decreaseThreadNum();
}
if (resourceWrapper.getEntryType() == EntryType.IN) {
Constants.ENTRY_NODE.addRtAndSuccess(rt, count);
Constants.ENTRY_NODE.decreaseThreadNum();
}
} else {
// Error may happen.
}
// Handle exit event with registered exit callback handlers.
Collection exitCallbacks = StatisticSlotCallbackRegistry.getExitCallbacks();
for (ProcessorSlotExitCallback handler : exitCallbacks) { // @2
handler.onExit(context, resourceWrapper, count, args);
}
fireExit(context, resourceWrapper, count); // @3
}
代码@1:成功执行,则重点关注响应时间,其实现亮点如下:
计算本次响应时间,将本次响应时间收集到 Node 中。
将当前活跃线程数减一。
代码@2:执行退出时的 callback。可以通过 StatisticSlotCallbackRegistry 的 addExitCallback 方法添加退出回调函数。
代码@3:传播 exit 事件。
接下来我们将重点介绍 DefaultNode,即 Sentinel 的 Node 体系,持有资源的实时调用信息。
[](()2、Sentienl Node 体系
2.1 Node 类体系图
我们先简单介绍一下上述核心类的作用与核心接口或核心属性的含义。
- OccupySupport
支持抢占未来的时间窗口,有点类似借用“未来”的令牌。其核心方法如下:
- long tryO 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 ccupyNext(long currentTime, int acquireCount, double threshold)
尝试抢占未来的令牌,返回值为调用该方法的线程应该 sleep 的时间。
1、long currentTime
当前时间。
2、int acquireCount
本次需要申请的令牌个数。
3、double threshold
设置的阔值。
- long waiting()
获取当前已申请的未来的令牌的个数。
- void addWaitingRequest(long futureTime, int acquireCount)
申请未来时间窗口中的令牌。
- void addOccupiedPass(int acquireCount)
增加申请未来令牌通过的个数。
- double occupiedPassQps()
当前抢占未来令牌的QPS。
- Node
持有实时统计信息的节点。定义了收集统计信息与获取统计信息的接口,上面方法根据方法名称即可得知其含义,故这里就不一一罗列了。
- StatisticNode
实现统计信息的默认实现类。
- DefaultNode
用于在特定上下文环境中保存某一个资源的实时统计信息。
- ClusterNode
实现基于集群限流模式的节点,将在集群限流模式部分详细介绍。
- EntranceNode
用来表示调用链入口的节点信息。
本文将详细介绍 DefaultNode 与 StatisticNode,重点阐述调用树与实时统计信息。DefaultNode 是 StatisticNode 的子类,我们先从 StatisticNode 开始 Node 体系的探究。
[](()2、StatisticNode 详解
[](()2.1 核心类图 总结
如果你选择了IT行业并坚定的走下去,这个方向肯定是没有一丝问题的,这是个高薪行业,但是高薪是凭自己的努力学习获取来的,这次我把P8大佬用过的一些学习笔记(pdf)都整理在本文中了
《Java中高级核心知识全面解析》
小米商场项目实战,别再担心面试没有实战项目:
ode 详解
[](()2.1 核心类图 总结
如果你选择了IT行业并坚定的走下去,这个方向肯定是没有一丝问题的,这是个高薪行业,但是高薪是凭自己的努力学习获取来的,这次我把P8大佬用过的一些学习笔记(pdf)都整理在本文中了
《Java中高级核心知识全面解析》
[外链图片转存中…(img-4FjEIhXZ-1650625820894)]
小米商场项目实战,别再担心面试没有实战项目:
[外链图片转存中…(img-7gf3FQFI-1650625820894)]



