首先咱們看一下Sentinel項目的整個結構:version:1.6.0app
* sentinel-core 核心模塊,限流、降級、系統保護等都在這裏實現 * sentinel-dashboard 控制檯模塊,能夠對鏈接上的sentinel客戶端實現可視化的管理 * sentinel-transport 傳輸模塊,提供了基本的監控服務端和客戶端的API接口,以及一些基於不一樣庫的實現 * sentinel-extension 擴展模塊,主要對DataSource進行了部分擴展實現 * sentinel-adapter 適配器模塊,主要實現了對一些常見框架的適配 * sentinel-demo 樣例模塊,可參考怎麼使用sentinel進行限流、降級等 * sentinel-benchmark 基準測試模塊,對核心代碼的精確性提供基準測試
底層原理:sentinel 入口是 SphU.entry建立框架
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; }
這個方法能夠分爲如下幾個部分:less
* 1.對參數和全局配置項作檢測,若是不符合,不會再進行後面的限流檢測,不然進入下面的檢測流程。 * 2.根據包裝過的資源對象獲取對應的SlotChain * 3.執行SlotChain的entry方法 * 3.1.若是SlotChain的entry方法拋出了BlockException,則將該異常繼續向上拋出 * 3.2.若是SlotChain的entry方法正常執行了,則最後會將該entry對象返回 * 4.若是上層方法捕獲了BlockException,則說明請求被限流了,不然請求能正常執行
其中比較重要的是第二、3兩個步驟,咱們來分解一下這兩個步驟。建立SlotChain默認的實現類爲 DefaultProcessorSlotChain:鏈表實現的責任鏈測試
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; }
* NodeSelectorSlot 負責收集資源的路徑,並將這些資源的調用路徑,以樹狀結構存儲起來,用於根據調用路徑來限流降級; * ClusterBuilderSlot 則用於存儲資源的統計信息以及調用者信息,例如該資源的 RT, QPS, thread count 等等,這些信息將用做爲多維度限流,降級的依據; * StatistcSlot 則用於記錄,統計不一樣緯度的 runtime 信息; * FlowSlot 則用於根據預設的限流規則,以及前面 slot 統計的狀態,來進行限流; * AuthorizationSlot 則根據黑白名單,來作黑白名單控制; * DegradeSlot 則經過統計信息,以及預設的規則,來作熔斷降級; * SystemSlot 則經過系統的狀態,例如 load1 等,來控制總的入口流量; *
執行完業務邏輯處理後,調用了fireEntry()方法,由此觸發了下一個節點的entry方法。此時咱們就知道了sentinel的責任鏈就是這樣傳遞的:每一個Slot節點執行完本身的業務後,會調用fireEntry來觸發下一個節點的entry方法。ui