文章目錄
1 什麼是 Sentinel
- Sentinel 是 2018 年, 阿里開源的一個輕量級流量控制組件
- 它以流量爲切入點,從限流、熔斷降級、系統負載等多個維度來保證服務端的穩定性
- 限流(flowslot):指定某個接口最大 QPS 或 最大併發線程數,當超過指定值時,會採用幾種限流策略
- 通過 FlowRule.controlBehavior 字段指定
- 直接拒絕(0)
- 預熱(1):當突然有大流量來時,會逐漸提高流量限制的閾值,最終才達到指定值。用來應對服務端初始化資源時和初始化資源後,它的流量負載能力不一樣的場景
- 流量規整(2):使得流量按規定的 QPS 進入資源,其餘的等待(有最大等待時間)
- 預熱 + 流量規整(3)
- 熔斷降級(degradeslot):當某個資源的平均響應時間或異常比例超過指定值時拒絕請求
- 系統負載:當應用的 QPS、併發線程數、CPU 使用率、平均響應時間等達到指定值時拒絕請求
- 限流(flowslot):指定某個接口最大 QPS 或 最大併發線程數,當超過指定值時,會採用幾種限流策略
2 Sentinel 核心概念簡介
2.1 resource
- 資源是對受保護的某一方法或代碼塊的唯一標識,用 resourceName 來表示
2.2 context
- 用 context 來構成資源調用的鏈路
2.3 slot
- 它是 Sentinel 功能實現的核心,通過不同的 slot 來做不同維度的保護,如 flowSlot 做限流保護、degradeSlot 做熔斷降級保護、paramSlot 做熱點參數限流保護、systemSlot 做系統負載保護、statisticSlot 收集 Metrics 數據等
2.4 slotChain
- 由多個 slot 串聯成的一個調用鏈,這樣使得所有定義的 slot 按一定的順序執行
2.5 slotChainBuilder
- 通過不同的 slotChainBuilder 來創建不同的 slotChain,默認爲 DefaultSlotChainBuilder,可以通過 SPI 接口擴展自定義的 slotChainBuilder
2.6 Node
- 通過不同的 Node 收集 Metrics 數據,各個 Slot 就是使用 Node 中的數據來做保護判斷的,如 QPS、併發線程數都被實時的收集在不同的 Node 中
3 Sentinel 擴展點
3.1 InitFunc
- 通過實現 InitFunc 接口,並且在 META-INF/services 目錄下的 com.alibaba.csp.sentinel.init.InitFunc 文件中指定該實現類全名
- 當第一次資源調用的時候,就會通過 Env 類中的 static 塊加載到,並且運行 init 方法
- 所以我們可以在項目中創建 InitFunc 實例,並指定
- 如通過 InitFunc 實例來初始化配置數據源
- 如 sentinel-parameter-flow-control 包中就通過此擴展點註冊了 ParamFlowStatisticEntryCallback 和 ParamFlowStatisticExitCallback
3.2 StatisticSlotCallbackRegistry
- StatisticSlot 中會調用此類來獲取所有的 SlotEntryCallback、SlotExitCallback 實例 的 onPass、onBlocked 、onExit 來處理其他的一些成功或阻塞後的一些邏輯
- 如 ParamFlowStatisticCallback 來處理熱點參數的線程數信息
- 註冊動作可以通過 InitFunc 方式來添加 Callback 實例
3.3 SlotChainBuilder
- 通過實現 SlotChainBuilder 接口,並且在 META-INF/services 目錄下的 com.alibaba.csp.sentinel.slotchain.SlotChainBuilder 文件中指定該實現類全名
- 當第一次資源調用的時候,就會通過 SlotChainProvider 類加載此文件中指定的類,如果有多個實例,就會選取一個最爲唯一的 SlotChainBuilder 類
- 所以我們可以在項目中創建 SlotChainBuilder 實例,並指定
- 如我們需要自定義某種 slot,那就需要通過自定義 SlotChainBuilder 來將我們定義的 slot 加入到 slotChain 中
- 如 sentinel-parameter-flow-control 包中就通過此擴展點實現了 HotParamSlotChainBuilder,將 ParmaFlowSlot 加入到 slotChain 中
3.4 MetricExtension
- 通過實現 MetricExtension 接口,並且在 META-INF/services 目錄下的 com.alibaba.csp.sentinel.metric.extension.MetricExtension 文件中指定該實現類全名
- 當第一次資源調用的時候,就會通過 MetricExtensionProvider 類加載此文件中指定的類,並放入 metricExtensions 列表中
- 目前 在 MetricEntryCallback、MetricExitCallback 中就會調用所有的 MetricExtension 實例
- 作用:可以通過它對 statistic 做擴展
4 Slot 簡介
4.1 NodeSelectorSlot
- 生成 contextName 對應的 resource 屬性,並存入 map 中
- 此 map 爲 實例屬性,又因爲 一個 slot 實例對應一個 resourceName
- 所以這個 map 存儲的是一個 resource(value) 下,對應的多個 contextName(key)
- 這樣的話,一個 resource 就會對應 多個 DefaultNode 了!因爲一個 resource 下的每一個 contextName 都會對應不同的 DefaultNode
- 那怎麼通過 resource 下所有的 statistics 呢?用 ClusterNode!
- 設置 context 的 curNode 屬性爲 新建的 DefaultNode(resourceWrapper,null)
- 往後傳的 node 就是 DefaultNode(resourceWrapper,null) 了!
- Sentinel 通過
NodeSelectorSlot
建立不同資源間的調用的關係,並且通過ClusterNodeBuilderSlot
記錄每個資源的實時統計信息。
4.2 ClusterBuilderSlot
- 生成 resourceName(key) 對應的 clusterNode 實例屬性,並存入 map 中
- 設置 DefaultNode(resourceWrapper,null) 的 clusterNode 屬性
- 如果 origin != “”,則設置 這個 clusterNode 對應的 originNodeMap 實例屬性
- 即將同一個 resource 中的 clusterNode 按 origin 細分爲 多個 originNode
- 爲了使統計針對 origin 粒度的信息
- 現在的粒度有:contextName(這個粒度可大可小),origin,resource
4.3 LogSlot
- 記錄日誌
4.4 StatisticSlot
- 記錄不同粒度的 matrix
//這裏 EntryType 起作用了
if (resourceWrapper.getEntryType() == EntryType.IN) {
// Add count for global inbound entry node for global statistics.
Constants.ENTRY_NODE.increaseThreadNum();//只要是 IN 的都會進入這個全局節點
Constants.ENTRY_NODE.addPassRequest(count);
}
4.5 SystemSlot
- 系統規則保護:thread、rt、load、cpu。可以自定義設置閾值
- 則通過系統的狀態,例如 load1 等,來控制總的入口流量
//在此 Slot 中,對 statisticSlot 統計的 ENTRY_NODE 信息進行了規則保護
Constants.ENTRY_NODE
4.6 AuthoritySlot
- 權限過濾:黑白名單,判斷 origin 是否是被拒絕的 origin
4.7 FlowSlot
- FlowSlot 通過使用預設的規則,來判斷正在訪問的請求是否應該被阻塞
- 如果任意預設的規則被觸發了,就會拋 FlowException 異常,用戶可以通過捕獲這個異常來處理他們想要的邏輯
- 每一個 FlowRule 主要由:grade、strategy 、path 組成
- grade:0 表示線程指標,1 表示 QPS 指標。這兩個指標都會被實時收集
- stage:主要用來處理不斷堆積的線程問題,當一個資源訪問耗時的時候
- 流量規整:使得每段時間只有固定的線程通過
4.8 DegradeSlot
- 處理降級規則
5 Sentinel 使用案例
5.1 原始接口使用
//其中 contextName對應調用鏈路入口名稱,通過 contextName 形成一條調用鏈
public void foo() {
Entry entry = null;
try {
//ContextUtil.enter("contextName","originName");//可以不用
entry = SphU.entry("abc");//"abc" 爲被保護資源的唯一標識
// resource that need protection
} catch (BlockException blockException) {
//因爲遍歷的時候會拋 BlockException,所以在這裏處理 blocked handle logic
//降級、限流、系統保護等拒絕後的邏輯
} catch (Throwable bizException) {
// business exception
} finally {
// ensure finally be executed
if (entry != null){
entry.exit();//必須是同一線程釋放,否則拋 ErrorEntryFreeException 異常
}
//ContextUtil.exit();
}
}
//--------------------- 各個方法分析
//對 resourceName 資源做保護
//1、
//獲取 context,如果之前沒有設置,則會使用 sentinel_default_context 來生成 context
//這裏有個要注意的地方,一個 contextName 可能就對應多個資源了!
//context = new Context(node, name); node 爲 context 中的 entranceNode 屬性
//2、
//生成 resourceName 對應的 slotChain,並存入 map 中
//即,同一個 resourceName 肯定會走同一條 slotChain!
//這個 map 的最大值爲 6000,也就是說一個項目最後定義 6000 個 resourceName
//3、
//生成 resource、slotChain、context 對應的 Entry,最爲返回值,爲了處理 context 等信息
//4、
//遍歷 slotChain,執行相應的方法(功能)
entry = SphU.entry("resourceName");
entry.exit();//清除 context 信息
ContextUtil.exit();//清除線程信息
5.2 註解使用
-
value: 資源名
-
entryType:IN:表示進入系統的流量,OUT:表示出口流量,即調用其他資源。systemSlot 只對 IN 類型的流量生效,目前沒有其它 slot 使用這個屬性
-
blockHandler:被拒絕之後走的處理方法,對應原始接口的 BlockException 後的邏輯
-
blockHandlerClass:定義 blockHandler 所在的類,如果定義了這個,則 blockHandler 對應的類必須是 static 方法(因爲這樣 反射的話就不用指定 類的實例了)
-
blockHandler 方法的返回類型必須和資源的返回類型一樣,參數類型必須必資源的參數多一個 BlockException 參數
-
fallback:處理 blockHandler 不能處理的異常
-
fallbackClass:同理 blockHandlerClass
-
blockHandler 方法的返回類型必須和資源的返回類型一樣,參數類型可以加一個 Throwable 參數,也可以不加
-
defaultFallback:如果之前的 fallback 流程走不通,則走這個 defaultFallback 流程,只是一個備選方案,也對應 fallbackClass。方法參數最多加個 Throwable 參數
-
exceptionsToTrace:非 BlockException 異常,不通過 handleFallback 處理的 異常
-
exceptionsToIgnore:非 BlockException 異常,通過 handleFallback 處理的 異常,且異常會被跟蹤
@SentinelResource(value = "hello")
public String hello(String name) {
return "hello:" + name;
}
- 如果要用註解,需要添加依賴,並且要將 SentinelResourceAspect 類注入容器中以使得註解生效
@Configuration
public class SentinelAspect {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
System.out.println("init SentinelResourceAspect");
return new SentinelResourceAspect();
}
}
--- 添加依賴
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.6.3</version>
</dependency>
參考
sentinel-core-1.6.3