一轨帜、概述
前面介紹過(guò)Sentinel核心框架就是通過(guò)插槽鏈一層層的調(diào)用瘩将,每個(gè)插槽的功能如下:
- NodeSelectorSlot 負(fù)責(zé)收集資源的路徑凯傲,并將這些資源的調(diào)用路徑锐涯,以樹(shù)狀結(jié)-構(gòu)存儲(chǔ)起來(lái)磕诊,用于根據(jù)調(diào)用路徑來(lái)限流降級(jí)。
- ClusterBuilderSlot 則用于存儲(chǔ)資源的統(tǒng)計(jì)信息以及調(diào)用者信息,例如該資源的 RT, QPS, thread count 等等秀仲,這些信息將用作為多維度限流融痛,降級(jí)的依據(jù)。
- StatisticSlot 則用于記錄神僵、統(tǒng)計(jì)不同緯度的 runtime 指標(biāo)監(jiān)控信息。
- LogSlot 則用于記錄blockException信息的日志信息覆劈,會(huì)寫(xiě)入的日志文件中保礼。
- ParamFlowSlot 則用于根據(jù)熱點(diǎn)參數(shù)進(jìn)行限流控制的。
- SystemSlot 則通過(guò)系統(tǒng)的狀態(tài)责语,例如 load1 等炮障,來(lái)控制總的入口流量。
- AuthoritySlot 則根據(jù)配置的黑白名單和調(diào)用來(lái)源信息坤候,來(lái)做黑白名單控制胁赢。
- FlowSlot 則用于根據(jù)預(yù)設(shè)的限流規(guī)則以及前面 slot 統(tǒng)計(jì)的狀態(tài),來(lái)進(jìn)行流量控制白筹。
- DegradeSlot 則通過(guò)統(tǒng)計(jì)信息以及預(yù)設(shè)的規(guī)則智末,來(lái)做熔斷降級(jí)。
這邊文章會(huì)先介紹NodeSelectorSlot和ClusterBuilderSlot功能
二徒河、節(jié)點(diǎn)Node
我們知道在進(jìn)行資源的限流降級(jí)中系馆,需要拿到Entry,Entry就像是一個(gè)憑證顽照,拿到這個(gè)憑證由蘑,代碼才能繼續(xù)走下去。Entry又被封裝到上下文對(duì)象Context中代兵。在Context中還有一個(gè)entranceNode節(jié)點(diǎn)尼酿,代表這個(gè)資源調(diào)用樹(shù)一個(gè)入口。Entry中又有curNode植影、originNode這兩個(gè)屬性裳擎。curNode保存了這一次調(diào)用統(tǒng)計(jì)信息,riginNode保存了在這個(gè)上下文中何乎,所有的調(diào)用的統(tǒng)計(jì)信息和句惯。
有這么多Node,那他們之間的關(guān)系是怎樣的支救,如圖:
- Node是一個(gè)接口抢野,有很多各種指標(biāo)的方法,Sentinel就用通過(guò)這些指標(biāo)進(jìn)行限流降級(jí)的各墨。
- StatisticNode:統(tǒng)計(jì)實(shí)時(shí)統(tǒng)計(jì)指標(biāo)的Node指孤。有兩個(gè)子類(lèi)DefaultNode和ClusterNode。
- EntranceNode:EntranceNode是每個(gè)上下文的入口,該節(jié)點(diǎn)是掛在root下的恃轩,是全局唯一的结洼,每一個(gè)context都會(huì)對(duì)應(yīng)一個(gè)entranceNode。
- DefaultNode:DefaultNode是記錄當(dāng)前調(diào)用實(shí)時(shí)數(shù)據(jù)的叉跛,在同一個(gè)上下文中不同資源關(guān)聯(lián)著不同的DefaultNode松忍。在同一個(gè)上下文中,對(duì)不同的資源調(diào)用筷厘,DefaultNode會(huì)有子childNode生成鸣峭。
- ClusterNode:全局的統(tǒng)計(jì)數(shù)據(jù),包括 rt, thread count, qps等酥艳,相同的資源關(guān)DefaultNode聯(lián)著統(tǒng)一個(gè)ClusterNode摊溶,無(wú)論它在哪個(gè)上下文中。
三充石、NodeSelectorSlot
在Sentinel之Entry構(gòu)建源碼解析 這篇文章介紹過(guò)莫换,Entry可以理解Context這棵樹(shù)的樹(shù)干,那么NodeSelectorSlot這個(gè)插槽可以理解為正式構(gòu)建entry葉子而形成的骤铃,下面具體分析拉岁。
//注意這里的可以使context的name
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 {
DefaultNode node = map.get(context.getName());
if (node == null) {
synchronized (this) {
node = map.get(context.getName());
if (node == null) {
node = Env.nodeBuilder.buildTreeNode(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);
}
}
context.setCurNode(node);
}
分析
- 1、從map中獲取改context下的defaultNode劲厌,如果defaultNode不存在膛薛,到2,反之直接到4补鼻。
- 2哄啄、雙重鎖校驗(yàn)defaultNode存不存,若不存在风范,則創(chuàng)建一個(gè)defaultNode咨跌,并重新構(gòu)造map,并添加到map中硼婿,然后到3锌半。
- 3、獲取context的當(dāng)前調(diào)用Node的LastNode寇漫,并把該node添加到子Node中刊殉。
- 4、重新設(shè)置Context的curEntry的curNode州胳。
看圖分析
在一個(gè)Context中第一次進(jìn)入時(shí)记焊,在第三步,調(diào)用Context的getLastNode方法栓撞。
//在Context中
public Node getLastNode() {
//curEntry.getLastNode() 調(diào)用CtEntry的方法getLastNode
if (curEntry != null && curEntry.getLastNode() != null) {
return curEntry.getLastNode();
} else {
return entranceNode;
}
}
在CtEntry中
@Override
public Node getLastNode() {
return parent == null ? null : parent.getCurNode();
}
上篇文章分析過(guò)Entry的構(gòu)建過(guò)程遍膜,第一次調(diào)用時(shí):
可以發(fā)現(xiàn)curEntry != null條件滿(mǎn)足碗硬,但是parent是null。所以瓢颅,lastNode第一次的值就是context的entranceNode恩尾,然后將node添加到entranceNode中,并設(shè)置curEntry的curNode節(jié)點(diǎn)挽懦。然后Context內(nèi)存結(jié)構(gòu)變成如下翰意。
接著又一個(gè)請(qǐng)求進(jìn)入,若資源不同信柿,在生成一個(gè)新的Entry后(上一篇分析過(guò))猎物。
這個(gè)時(shí)候再次調(diào)用context.getLastNode()將會(huì)返回parent.getCurNode(),把這個(gè)節(jié)點(diǎn)放入到lastNode的子節(jié)點(diǎn)中角塑,再把node設(shè)置給context的curEntry。最后Context內(nèi)存結(jié)構(gòu)變成如下淘讥。
這里假設(shè)的是資源名不同的情況圃伶,若是資源相同的話(huà),則context.getLastNode()).addChild(node);邏輯就不會(huì)執(zhí)行蒲列,只會(huì)把當(dāng)前node設(shè)置為context的curEntry中窒朋。這時(shí)的內(nèi)存結(jié)構(gòu)如圖。
上面分析了在NodeSelectorSlot中CurEntry葉子構(gòu)建的過(guò)程蝗岖,在一次構(gòu)建中會(huì)設(shè)置entranceNode的childNode侥猩。
資源路徑收集
經(jīng)過(guò)上述分析可以發(fā)現(xiàn),NodeSelectorSlot主要負(fù)責(zé)收集資源的路徑抵赢,并將這些資源的調(diào)用路徑欺劳,以樹(shù)狀結(jié)構(gòu)存儲(chǔ)起來(lái),用于根據(jù)調(diào)用路徑來(lái)限流降級(jí)铅鲤。
ContextUtil.enter("entrance1", "appA");
Entry nodeA = SphU.entry("nodeA");
if (nodeA != null) {
nodeA.exit();
}
ContextUtil.exit();
上述代碼通過(guò) ContextUtil.enter() 創(chuàng)建了一個(gè)名為 entrance1 的上下文划提,同時(shí)指定調(diào)用發(fā)起者為 appA;接著通過(guò) SphU.entry()請(qǐng)求一個(gè) token邢享,如果該方法順利執(zhí)行沒(méi)有拋 BlockException鹏往,表明 token 請(qǐng)求成功。
以上代碼將在內(nèi)存中生成以下結(jié)構(gòu):
machine-root
/
/
EntranceNode1
/
/
DefaultNode(nodeA
注意:每個(gè) DefaultNode 由資源 ID 和輸入名稱(chēng)來(lái)標(biāo)識(shí)骇塘。換句話(huà)說(shuō)伊履,一個(gè)資源 ID 可以有多個(gè)不同入口的 DefaultNode。
ContextUtil.enter("entrance1", "appA");
Entry nodeA = SphU.entry("nodeA");
if (nodeA != null) {
nodeA.exit();
}
ContextUtil.exit();
ContextUtil.enter("entrance2", "appA");
nodeA = SphU.entry("nodeA");
if (nodeA != null) {
nodeA.exit();
}
ContextUtil.exit();
以上代碼將在內(nèi)存中生成以下結(jié)構(gòu):
machine-root
/ \
/ \
Entrance1 Entrance2
/ \
/ \
DefaultNode(nodeA) DefaultNode(nodeA)
| |
+- - - - - - - - - - +- - - - - - -> ClusterNode(nodeA);
可以發(fā)現(xiàn)同一個(gè)nodeA資源共用一個(gè)ClusterNode款违,而不管它在哪一個(gè)上線(xiàn)文中唐瀑。接下面講解ClusterBuilderSlot,來(lái)看ClusterNode構(gòu)造奠货。
四介褥、ClusterBuilderSlot
ClusterBuilderSlot插槽用于構(gòu)建資源的 ClusterNode 以及調(diào)用來(lái)源節(jié)點(diǎn)。ClusterNode 保持資源運(yùn)行統(tǒng)計(jì)信息(響應(yīng)時(shí)間、QPS柔滔、block 數(shù)目溢陪、線(xiàn)程數(shù)、異常數(shù)等)以及原始調(diào)用者統(tǒng)計(jì)信息列表睛廊。來(lái)源調(diào)用者的名字由 Context.enter(contextName形真,origin) 中的 origin 標(biāo)記。
看源碼:
public class ClusterBuilderSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
private static volatile Map<ResourceWrapper, ClusterNode> clusterNodeMap
= new HashMap<ResourceWrapper, ClusterNode>();
private static final Object lock = new Object();
private ClusterNode clusterNode = null;
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args)
throws Throwable {
if (clusterNode == null) {
synchronized (lock) {
if (clusterNode == null) {
// Create the cluster node.
clusterNode = Env.nodeBuilder.buildClusterNode();
HashMap<ResourceWrapper, ClusterNode> newMap = new HashMap<ResourceWrapper, ClusterNode>(16);
newMap.putAll(clusterNodeMap);
newMap.put(node.getId(), clusterNode);
clusterNodeMap = newMap;
}
}
}
node.setClusterNode(clusterNode);
/*
* if context origin is set, we should get or create a new {@link Node} of
* the specific origin.
*/
if (!"".equals(context.getOrigin())) {
Node originNode = node.getClusterNode().getOrCreateOriginNode(context.getOrigin());
context.getCurEntry().setOriginNode(originNode);
}
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
//以下代碼省略
}
細(xì)心地人可以發(fā)現(xiàn)ClusterBuilderSlot集成的AbstractLinkedProcessorSlot的泛型對(duì)象由Object變?yōu)镈efaultNode了超全,這是因?yàn)镈efaultNode已經(jīng)在NodeSelectorSlot插槽中構(gòu)建好了咆霜。
來(lái)看ClusterBuilderSlot插槽的entry方法做了哪些事。
- 1嘶朱、判斷clusterNode是否為空蛾坯,若為空,到2疏遏,反之到3脉课。
- 2、創(chuàng)建一個(gè)clusterNode節(jié)點(diǎn)财异,并添加到clusterNodeMap中倘零,注意這里的map的可以使ResourceWrapper,用以表示不區(qū)分在哪個(gè)上下文中戳寸,到3呈驶。
- 3、設(shè)置clusterNode到defaultNode中疫鹊,到4袖瞻。
- 4、如果context的origin不為空订晌,則把originNode設(shè)置到Context當(dāng)前cutEntry的originNode中虏辫。這個(gè)originNode用于后續(xù)根據(jù)調(diào)用源進(jìn)行限流或資源保護(hù)。
五锈拨、我的小結(jié)
1砌庄、本文是插槽分析的第一篇,介紹的NodeSelectorSlot和ClusterBuilderSlot的作用奕枢。
2娄昆、NodeSelectorSlot用來(lái)構(gòu)建Context的curEntry的葉子節(jié)點(diǎn),不同的資源id在不同的上下文中有不同的入口缝彬,并且對(duì)應(yīng)不同的defaultNode萌焰,但同一個(gè)資源對(duì)應(yīng)同一個(gè)clusterNode。
3谷浅、ClusterBuilderSlot設(shè)置了defaultNode的clusterNode扒俯,并設(shè)置了Entry的originNode奶卓。clusterNode保存clusterNodeMap中,可以發(fā)現(xiàn)系統(tǒng)運(yùn)行的時(shí)間越長(zhǎng)撼玄,這個(gè)map就越穩(wěn)定夺姑。
4、NodeSelectorSlot和ClusterBuilderSlot是整個(gè)插槽鏈中的前兩個(gè)插槽掌猛,這個(gè)兩個(gè)插槽完成Context數(shù)據(jù)的封裝和對(duì)資源調(diào)用鏈Node包裝盏浙,以便對(duì)后續(xù)數(shù)據(jù)的收集和資源的保護(hù)限流。
以上內(nèi)容荔茬,若有不當(dāng)住處废膘,請(qǐng)指正