Sentinel源码三:NodeSelectorSlot
概述
之前解析了插槽链的入口ProcessorSlotChain和它的默认实现DefaultProcessorSlotChain。具体的处理逻辑由链表中的各个节点处理。
1 | public ProcessorSlotChain build() { |
本文解析以下链表中的第一个节点:NodeSelectorSlot。
NodeSelectorSlot 负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级;
解析
NodeSelectorSlot
资源:在 Sentinel 里面,所有的资源都对应一个资源名称(resourceName),资源可以是一个类、一个方法、一个代码块。
继承关系
继承AbstractLinkedProcessorSlot,本身也是链表的一个节点1
public class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object>{}
成员变量
使用 map 保存了资源信息。1
private volatile Map<String, DefaultNode> map = new HashMap<String, DefaultNode>(10);
资源节点DefaultNode放在后面的小节
核心方法
entry
entry 进入资源区域时执行
- 根据context的名称获取资源node,如果没有获取到则新建一个更新到map中;
- 设置上下文中的 curNode;
- 交给下一个节点处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
throws Throwable {
/*
* It's interesting that we use context name rather resource name as the map key.
*
* Remember that same resource({@link ResourceWrapper#equals(Object)}) will share
* the same {@link ProcessorSlotChain} globally, no matter in which context. So if
* code goes into {@link #entry(Context, ResourceWrapper, DefaultNode, int, Object...)},
* the resource name must be same but context name may not.
*
* If we use {@link com.alibaba.csp.sentinel.SphU#entry(String resource)} to
* enter same resource in different context, using context name as map key can
* distinguish the same resource. In this case, multiple {@link DefaultNode}s will be created
* of the same resource name, for every distinct context (different context name) each.
*
* Consider another question. One resource may have multiple {@link DefaultNode},
* so what is the fastest way to get total statistics of the same resource?
* The answer is all {@link DefaultNode}s with same resource name share one
* {@link ClusterNode}. See {@link ClusterBuilderSlot} for detail.
*/
DefaultNode node = map.get(context.getName());
if (node == null) {
synchronized (this) {
node = map.get(context.getName());
if (node == null) {
node = new DefaultNode(resourceWrapper, null);
HashMap<String, DefaultNode> cacheMap = new HashMap<String, DefaultNode>(map.size());
cacheMap.putAll(map);
cacheMap.put(context.getName(), node);
map = cacheMap; // 采用的是替换
}
// Build invocation tree
((DefaultNode)context.getLastNode()).addChild(node);
}
}
// 设置当前 curNode
context.setCurNode(node);
// 交给下一个节点处理
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
exit
exit退出资源区域时执行.
NodeSelectorSlot 中不做任何处理,交由下一个节点处理。1
2
3
4
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
fireExit(context, resourceWrapper, count, args);
}
DefaultNode
StatisticNode的解析放在单独一篇解析。
继承关系
1 | public class DefaultNode extends StatisticNode{} |
成员变量
1 | private ResourceWrapper id; // 资源 |
构造器
1 | public DefaultNode(ResourceWrapper id, ClusterNode clusterNode) { |
主要方法
添加子节点 addChild
1 | public void addChild(Node node) { |
限流的QPS增加 increaseBlockQps
增加qps依赖父类的实现1
2
3
4
5
public void increaseBlockQps(int count) {
super.increaseBlockQps(count);
this.clusterNode.increaseBlockQps(count);
}
异常的QPS增加
1 |
|
响应时间统计
1 |
|
线程数增加
1 |
|