從入口開始
在學(xué)習(xí)使用Sentinel時(shí)可以知道坷牛,Sentinel的限流入口是Sphu.entry()。那我們就從Sphu.entry()開始,像剝洋蔥一樣打開Sentinel。
/**
* Checking all {@link Rule}s about the resource.
*
* @param name the unique name of the protected resource
* @throws BlockException if the block criteria is met, eg. when any rule's threshold is exceeded.
*/
public static Entry entry(String name) throws BlockException {
return Env.sph.entry(name, EntryType.OUT, 1, OBJECTS0);
}
Env類將觸發(fā)Sentinel的所有初始化操作暗甥。它擁有一個(gè)屬性sph,這個(gè)屬性的實(shí)現(xiàn)類是CtSph捉捅。
進(jìn)入CtSph類查看entry的實(shí)現(xiàn)。
@Override
public Entry entry(String name, EntryType type, int count, Object... args) throws BlockException {
StringResourceWrapper resource = new StringResourceWrapper(name, type);
return entry(resource, count, args);
}
在這段代碼里首先生成了一個(gè)StringResourceWrapper對(duì)象虽风。追蹤進(jìn)去發(fā)現(xiàn)進(jìn)入了entryWithPriority方法棒口。
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 = MyContextUtil.myEnter(Constants.CONTEXT_DEFAULT_NAME, "", resourceWrapper.getType());
}
// Global switch is close, no rule checking will do.
if (!Constants.ON) {
return new CtEntry(resourceWrapper, null, context);
}
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 {
chain.entry(context, resourceWrapper, null, count, prioritized, args);
} catch (BlockException e1) {
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;
}
這個(gè)方法做了下面幾件事:
- 查看是否有可用的Context,如果沒有辜膝,則生成一個(gè)Context
- 檢查全局開關(guān)是否關(guān)閉了无牵,如果是關(guān)閉的則返回一個(gè)不受限制的Entry對(duì)象
- 進(jìn)入lookProcessChain()方法獲取/生成slot(元件、插槽)鏈
- 執(zhí)行slot鏈的entry()方法
我們先看lookProcessChain()方法做了什么
ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {
ProcessorSlotChain chain = chainMap.get(resourceWrapper);
if (chain == null) {
synchronized (LOCK) {
chain = chainMap.get(resourceWrapper);
if (chain == null) {
// Entry size limit.
if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) {
return null;
}
chain = SlotChainProvider.newSlotChain();
Map<ResourceWrapper, ProcessorSlotChain> newMap = new HashMap<ResourceWrapper, ProcessorSlotChain>(
chainMap.size() + 1);
newMap.putAll(chainMap);
newMap.put(resourceWrapper, chain);
chainMap = newMap;
}
}
}
return chain;
}
跟進(jìn)SlotChainProvider.newSlotChain()
方法厂抖,實(shí)際是由DefaultSlotChainBuilder
中的build()
方法生成一個(gè)新的slot鏈表茎毁,這個(gè)slot鏈表里有:
- NodeSelectorSlot:負(fù)責(zé)生成調(diào)用路徑
- ClusterBuilderSlot:負(fù)責(zé)維護(hù)資源的運(yùn)行統(tǒng)計(jì)(響應(yīng)時(shí)間、并發(fā)量、線程數(shù)七蜘、異常)谭溉,以及調(diào)用者列表
- LogSlot:記錄日志
- StatisticSlot:負(fù)責(zé)統(tǒng)計(jì)與ClusterBuilderSlot限流運(yùn)行統(tǒng)計(jì)不同維度的數(shù)據(jù)
- AuthoritySlot:負(fù)責(zé)黑白名單控制
- SystemSlot:根據(jù)設(shè)置的系統(tǒng)規(guī)則及前面slot的統(tǒng)計(jì)信息進(jìn)行系統(tǒng)流量控制
- FlowSlot:根據(jù)設(shè)置的限流規(guī)則和前面的統(tǒng)計(jì)信息進(jìn)行流量控制
- DegradeSlot:根據(jù)設(shè)置的降級(jí)規(guī)則及前面的統(tǒng)計(jì)信息進(jìn)行服務(wù)降級(jí)控制
slotChain的entry()方法是在DefaultProcessorSlotChain實(shí)現(xiàn)的。在上一個(gè)節(jié)點(diǎn)執(zhí)行完成后會(huì)調(diào)用下一個(gè)節(jié)點(diǎn)橡卤,如果在執(zhí)行過程中某個(gè)規(guī)則被觸發(fā)扮念,則會(huì)拋出BlockException。