NodeSelectorSlot
以下分析Slot代码, 只针对entry方法。exit方法不多做分析。有兴趣的可以自行查看。
NodeSelectorSlot的代码不多,但是却是最复杂的一个Slot。要理解这个Slot的代码,首先需要搞清楚Context相关的内容。
之前简单说明了一下Context,一个持有元数据的上下文,所谓的元数据主要是Entry,而Entry是一个双向链表,构成了一个调用链。
这里进一步分析Entry、Context、Node,为理解NodeSelectorSlot做准备。
回到com.alibaba.csp.sentinel.CtSph#entryWithPriority(com.alibaba.csp.sentinel.slotchain.ResourceWrapper, int, boolean, java.lang.Object…)的代码:
private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
throws BlockException {
Context context = ContextUtil.getContext();
if (context instanceof NullContext) {
// The {@link NullContext} indicates that the amount of context has exceeded the threshold,
// so here init the entry only. No rule checking will be done.
return new CtEntry(resourceWrapper, null, context);
}
if (context == null) {
// Using default context.
context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
}
// Global switch is close, no rule checking will do.
if (!Constants.ON) {
return new CtEntry(resourceWrapper, null, context);
}
// 获取该资源对应的SlotChain
ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
/*
* Means amount of resources (slot chain) exceeds {@link Constants.MAX_SLOT_CHAIN_SIZE},
* so no rule checking will be done.
*/
if (chain == null) {
return new CtEntry(resourceWrapper, null, context);
}
Entry e = new CtEntry(resourceWrapper, chain, context);
try {
// 执行Slot的entry方法
chain.entry(context, resourceWrapper, null, count, prioritized, args);
} catch (BlockException e1) {
// 抛出BlockExecption
e.exit(count, args);
throw e1;
} catch (Throwable e1) {
// This should not happen, unless there are errors existing in Sentinel internal.
RecordLog.info("Sentinel unexpected exception", e1);
}
return e;
}
先看一下Entry的创建过程
CtEntry(ResourceWrapper resourceWrapper, ProcessorSlot<Object> chain, Context context) {
super(resourceWrapper);
this.chain = chain;
this.context = context;
setUpEntryFor(context);
}
private void setUpEntryFor(Context context) {
// The entry should not be associated to NullContext.
if (context instanceof NullContext) {
return;
}
// 获取「上下文」中上一次的入口
this.parent = context.getCurEntry();
if (parent != null) {
// 然后将当前入口设置为上一次入口的子节点
((CtEntry) parent).child = this;
}
// 设置「上下文」的当前入口为该类本身
context.setCurEntry(this);
}
每次进行entry调用都会创建一个新的CtEntry,当Context 不变的情况下,context下的curEntry会设置对应的parent和child,同时curEntry指向最新的创建的CtEntry。这里就构成了一个双向链表。
而Context的创建和销毁过程是如何处理的呢?
详细分析一下Context的创建过程
context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
private final static class InternalContextUtil extends ContextUtil {
static Context internalEnter(String name) {
return trueEnter(name, "");
}
static Context internalEnter(String name, String origin) {
return trueEnter(name, origin);
}