NodeSelectorSlot 负责为资源的首次访问创建 DefaultNode,以及维护 Context.curNode 和调用树。NodeSelectorSlot 被放在 ProcessorSlotChain 链表的第一个位置,这是因为后续的 ProcessorSlot 都需要依赖这个 ProcessorSlot。NodeSelectorSlot 源码如下:
public class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object> {
//key: 上下文名称 value: resource对应的DefaultNode
private volatile Map<String, DefaultNode> map = new HashMap<String, DefaultNode>(10);
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
throws Throwable {
//DCL初始化
DefaultNode node = map.get(context.getName());
if (node == null) {
synchronized (this) {
node = map.get(context.getName());
if (node == null) {
node = new DefaultNode(resourceWrapper, null);
//map并非线程安全,使用临时变量替换的方式优化性能
HashMap<String, DefaultNode> cacheMap = new HashMap<String, DefaultNode>(map.size());
cacheMap.putAll(map);
cacheMap.put(context.getName(), node);
map = cacheMap;
// 构建调用树
((DefaultNode) context.getLastNode()).addChild(node);
}
}
}
//设置当前结点 在这里设置ctEntry的node属性
context.setCurNode(node);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
//什么也没做,交给后续的processSlot处理
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
fireExit(context, resourceWrapper, count, args);
}
}
注意:每一个resource对应一条ProcessSlotChain, 也就是对应同样的NodeSelectorSlot 。所以NodeSelectorSlot的属性map,以contextName为key,来区分DefaultNode.
((DefaultNode) context.getLastNode()).addChild(node); 最终会构造类似下图的tree.
ROOT (machine-root)
/
EntranceNode (context name: sentinel_spring_web_context)
/ \
DefaultNode (resource name: GET:/hello) DefaultNode (resource name: GET:/err)