Sentinel(三)之如何使用
轉載自??Sentinel如何使用
簡介
Sentinel 可以簡單的分為 Sentinel 核心庫和 Dashboard。核心庫不依賴 Dashboard,但是結合 Dashboard 可以取得最好的效果。
這篇文章主要介紹 Sentinel 核心庫的使用。如果希望有一個最快最直接的了解,可以參考?新手指南?來獲取一個最直觀的感受。
我們說的資源,可以是任何東西,服務,服務里的方法,甚至是一段代碼。使用 Sentinel 來進行資源保護,主要分為幾個步驟:
先把可能需要保護的資源定義好(埋點),之后再配置規則。也可以理解為,只要有了資源,我們就可以在任何時候靈活地定義各種流量控制規則。在編碼的時候,只需要考慮這個代碼是否需要保護,如果需要保護,就將之定義為一個資源。
對于主流的框架,我們提供適配,只需要按照適配中的說明配置,Sentinel 就會默認定義提供的服務,方法等為資源。
?
定義資源
方式一:主流框架的默認適配
為了減少開發的復雜程度,我們對大部分的主流框架,例如 Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux、Reactor 等都做了適配。您只需要引入對應的依賴即可方便地整合 Sentinel。可以參見:?主流框架的適配。
方式二:拋出異常的方式定義資源
SphU?包含了 try-catch 風格的 API。用這種方式,當資源發生了限流之后會拋出?BlockException。這個時候可以捕捉異常,進行限流之后的邏輯處理。示例代碼如下:
// 1.5.0 版本開始可以利用 try-with-resources 特性(使用有限制) // 資源名可使用任意有業務語義的字符串,比如方法名、接口名或其它可唯一標識的字符串。 try (Entry entry = SphU.entry("resourceName")) {// 被保護的業務邏輯// do something here... } catch (BlockException ex) {// 資源訪問阻止,被限流或被降級// 在此處進行相應的處理操作 }特別地,若 entry 的時候傳入了熱點參數,那么 exit 的時候也一定要帶上對應的參數(exit(count, args)),否則可能會有統計錯誤。這個時候不能使用 try-with-resources 的方式。另外通過?Tracer.trace(ex)?來統計異常信息時,由于 try-with-resources 語法中 catch 調用順序的問題,會導致無法正確統計異常數,因此統計異常信息時也不能在 try-with-resources 的 catch 塊中調用?Tracer.trace(ex)。
手動 exit 示例:
Entry entry = null; // 務必保證 finally 會被執行 try {// 資源名可使用任意有業務語義的字符串,注意數目不能太多(超過 1K),超出幾千請作為參數傳入而不要直接作為資源名// EntryType 代表流量類型(inbound/outbound),其中系統規則只對 IN 類型的埋點生效entry = SphU.entry("自定義資源名");// 被保護的業務邏輯// do something... } catch (BlockException ex) {// 資源訪問阻止,被限流或被降級// 進行相應的處理操作 } catch (Exception ex) {// 若需要配置降級規則,需要通過這種方式記錄業務異常Tracer.traceEntry(ex, entry); } finally {// 務必保證 exit,務必保證每個 entry 與 exit 配對if (entry != null) {entry.exit();} }熱點參數埋點示例:
Entry entry = null; try {// 若需要配置例外項,則傳入的參數只支持基本類型。// EntryType 代表流量類型,其中系統規則只對 IN 類型的埋點生效// count 大多數情況都填 1,代表統計為一次調用。entry = SphU.entry(resourceName, EntryType.IN, 1, paramA, paramB);// Your logic here. } catch (BlockException ex) {// Handle request rejection. } finally {// 注意:exit 的時候也一定要帶上對應的參數,否則可能會有統計錯誤。if (entry != null) {entry.exit(1, paramA, paramB);} }SphU.entry()?的參數描述:
| entryType | EntryType | 資源調用的流量類型,是入口流量(EntryType.IN)還是出口流量(EntryType.OUT),注意系統規則只對 IN 生效 | EntryType.OUT |
| count | int | 本次資源調用請求的 token 數目 | 1 |
| args | Object[] | 傳入的參數,用于熱點參數限流 | 無 |
注意:SphU.entry(xxx)?需要與?entry.exit()?方法成對出現,匹配調用,否則會導致調用鏈記錄異常,拋出?ErrorEntryFreeException?異常。常見的錯誤:
- 自定義埋點只調用?SphU.entry(),沒有調用?entry.exit()
- 順序錯誤,比如:entry1 -> entry2 -> exit1 -> exit2,應該為?entry1 -> entry2 -> exit2 -> exit1
方式三:返回布爾值方式定義資源
SphO?提供 if-else 風格的 API。用這種方式,當資源發生了限流之后會返回?false,這個時候可以根據返回值,進行限流之后的邏輯處理。示例代碼如下:
// 資源名可使用任意有業務語義的字符串if (SphO.entry("自定義資源名")) {// 務必保證finally會被執行try {/*** 被保護的業務邏輯*/} finally {SphO.exit();}} else {// 資源訪問阻止,被限流或被降級// 進行相應的處理操作}注意:SphO.entry(xxx)?需要與 SphO.exit()方法成對出現,匹配調用,位置正確,否則會導致調用鏈記錄異常,拋出ErrorEntryFreeException` 異常。
方式四:注解方式定義資源
Sentinel 支持通過?@SentinelResource?注解定義資源并配置?blockHandler?和?fallback?函數來進行限流之后的處理。示例:
// 原本的業務方法. @SentinelResource(blockHandler = "blockHandlerForGetUser") public User getUserById(String id) {throw new RuntimeException("getUserById command failed"); }// blockHandler 函數,原方法調用被限流/降級/系統保護的時候調用 public User blockHandlerForGetUser(String id, BlockException ex) {return new User("admin"); }注意?blockHandler?函數會在原方法被限流/降級/系統保護的時候調用,而?fallback?函數會針對所有類型的異常。請注意?blockHandler?和?fallback?函數的形式要求,更多指引可以參見?Sentinel 注解支持文檔。
方式五:異步調用支持
Sentinel 支持異步調用鏈路的統計。在異步調用中,需要通過?SphU.asyncEntry(xxx)?方法定義資源,并通常需要在異步的回調函數中調用?exit?方法。以下是一個簡單的示例:
try {AsyncEntry entry = SphU.asyncEntry(resourceName);// 異步調用.doAsync(userId, result -> {try {// 在此處處理異步調用的結果.} finally {// 在回調結束后 exit.entry.exit();}}); } catch (BlockException ex) {// Request blocked.// Handle the exception (e.g. retry or fallback). }SphU.asyncEntry(xxx)?不會影響當前(調用線程)的 Context,因此以下兩個 entry 在調用鏈上是平級關系(處于同一層),而不是嵌套關系:
// 調用鏈類似于: // -parent // ---asyncResource // ---syncResource asyncEntry = SphU.asyncEntry(asyncResource); entry = SphU.entry(normalResource);若在異步回調中需要嵌套其它的資源調用(無論是?entry?還是?asyncEntry),只需要借助 Sentinel 提供的上下文切換功能,在對應的地方通過?ContextUtil.runOnContext(context, f)?進行 Context 變換,將對應資源調用處的 Context 切換為生成的異步 Context,即可維持正確的調用鏈路關系。示例如下:
public void handleResult(String result) {Entry entry = null;try {entry = SphU.entry("handleResultForAsync");// Handle your result here.} catch (BlockException ex) {// Blocked for the result handler.} finally {if (entry != null) {entry.exit();}} }public void someAsync() {try {AsyncEntry entry = SphU.asyncEntry(resourceName);// Asynchronous invocation.doAsync(userId, result -> {// 在異步回調中進行上下文變換,通過 AsyncEntry 的 getAsyncContext 方法獲取異步 ContextContextUtil.runOnContext(entry.getAsyncContext(), () -> {try {// 此處嵌套正常的資源調用.handleResult(result);} finally {entry.exit();}});});} catch (BlockException ex) {// Request blocked.// Handle the exception (e.g. retry or fallback).} }此時的調用鏈就類似于:
-parent ---asyncInvocation -----handleResultForAsync更詳細的示例可以參考 Demo 中的?AsyncEntryDemo,里面包含了普通資源與異步資源之間的各種嵌套示例。
規則的種類
Sentinel 的所有規則都可以在內存態中動態地查詢及修改,修改之后立即生效。同時 Sentinel 也提供相關 API,供您來定制自己的規則策略。
Sentinel 支持以下幾種規則:流量控制規則、熔斷降級規則、系統保護規則、來源訪問控制規則?和?熱點參數規則。
流量控制規則 (FlowRule)
流量規則的定義
重要屬性:
| resource | 資源名,資源名是限流規則的作用對象 | ? |
| count | 限流閾值 | ? |
| grade | 限流閾值類型,QPS 模式(1)或并發線程數模式(0) | QPS 模式 |
| limitApp | 流控針對的調用來源 | default,代表不區分調用來源 |
| strategy | 調用關系限流策略:直接、鏈路、關聯 | 根據資源本身(直接) |
| controlBehavior | 流控效果(直接拒絕/WarmUp/勻速+排隊等待),不支持按調用關系限流 | 直接拒絕 |
| clusterMode | 是否集群限流 | 否 |
同一個資源可以同時有多個限流規則,檢查規則時會依次檢查。
通過代碼定義流量控制規則
理解上面規則的定義之后,我們可以通過調用?FlowRuleManager.loadRules()?方法來用硬編碼的方式定義流量控制規則,比如:
private void initFlowQpsRule() {List<FlowRule> rules = new ArrayList<>();FlowRule rule = new FlowRule(resourceName);// set limit qps to 20rule.setCount(20);rule.setGrade(RuleConstant.FLOW_GRADE_QPS);rule.setLimitApp("default");rules.add(rule);FlowRuleManager.loadRules(rules); }更多詳細內容可以參考?流量控制。
熔斷降級規則 (DegradeRule)
熔斷降級規則包含下面幾個重要的屬性:
| resource | 資源名,即規則的作用對象 | ? |
| grade | 熔斷策略,支持慢調用比例/異常比例/異常數策略 | 慢調用比例 |
| count | 慢調用比例模式下為慢調用臨界 RT(超出該值計為慢調用);異常比例/異常數模式下為對應的閾值 | ? |
| timeWindow | 熔斷時長,單位為 s | ? |
| minRequestAmount | 熔斷觸發的最小請求數,請求數小于該值時即使異常比率超出閾值也不會熔斷(1.7.0 引入) | 5 |
| statIntervalMs | 統計時長(單位為 ms),如 60*1000 代表分鐘級(1.8.0 引入) | 1000 ms |
| slowRatioThreshold | 慢調用比例閾值,僅慢調用比例模式有效(1.8.0 引入) | ? |
同一個資源可以同時有多個降級規則。
理解上面規則的定義之后,我們可以通過調用?DegradeRuleManager.loadRules()?方法來用硬編碼的方式定義流量控制規則。
private void initDegradeRule() {List<DegradeRule> rules = new ArrayList<>();DegradeRule rule = new DegradeRule();rule.setResource(KEY);// set threshold RT, 10 msrule.setCount(10);rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);rule.setTimeWindow(10);rules.add(rule);DegradeRuleManager.loadRules(rules); }更多詳情可以參考?熔斷降級。
系統保護規則 (SystemRule)
Sentinel 系統自適應限流從整體維度對應用入口流量進行控制,結合應用的 Load、CPU 使用率、總體平均 RT、入口 QPS 和并發線程數等幾個維度的監控指標,通過自適應的流控策略,讓系統的入口流量和系統的負載達到一個平衡,讓系統盡可能跑在最大吞吐量的同時保證系統整體的穩定性。
系統規則包含下面幾個重要的屬性:
| highestSystemLoad | load1?觸發值,用于觸發自適應控制階段 | -1 (不生效) |
| avgRt | 所有入口流量的平均響應時間 | -1 (不生效) |
| maxThread | 入口流量的最大并發數 | -1 (不生效) |
| qps | 所有入口資源的 QPS | -1 (不生效) |
| highestCpuUsage | 當前系統的 CPU 使用率(0.0-1.0) | -1 (不生效) |
理解上面規則的定義之后,我們可以通過調用?SystemRuleManager.loadRules()?方法來用硬編碼的方式定義流量控制規則。
private void initSystemRule() {List<SystemRule> rules = new ArrayList<>();SystemRule rule = new SystemRule();rule.setHighestSystemLoad(10);rules.add(rule);SystemRuleManager.loadRules(rules); }注意系統規則只針對入口資源(EntryType=IN)生效。更多詳情可以參考?系統自適應保護文檔。
訪問控制規則 (AuthorityRule)
很多時候,我們需要根據調用方來限制資源是否通過,這時候可以使用 Sentinel 的訪問控制(黑白名單)的功能。黑白名單根據資源的請求來源(origin)限制資源是否通過,若配置白名單則只有請求來源位于白名單內時才可通過;若配置黑名單則請求來源位于黑名單時不通過,其余的請求通過。
授權規則,即黑白名單規則(AuthorityRule)非常簡單,主要有以下配置項:
- resource:資源名,即規則的作用對象
- limitApp:對應的黑名單/白名單,不同 origin 用?,?分隔,如?appA,appB
- strategy:限制模式,AUTHORITY_WHITE?為白名單模式,AUTHORITY_BLACK?為黑名單模式,默認為白名單模式
更多詳情可以參考?來源訪問控制。
熱點規則 (ParamFlowRule)
詳情可以參考?熱點參數限流。
查詢更改規則
引入了 transport 模塊后,可以通過以下的 HTTP API 來獲取所有已加載的規則:
http://localhost:8719/getRules?type=<XXXX>其中,type=flow?以 JSON 格式返回現有的限流規則,degrade 返回現有生效的降級規則列表,system 則返回系統保護規則。
獲取所有熱點規則:
http://localhost:8719/getParamRules定制自己的持久化規則
上面的規則配置,都是存在內存中的。即如果應用重啟,這個規則就會失效。因此我們提供了開放的接口,您可以通過實現?DataSource?接口的方式,來自定義規則的存儲數據源。通常我們的建議有:
- 整合動態配置系統,如 ZooKeeper、Nacos、Apollo 等,動態地實時刷新配置規則
- 結合 RDBMS、NoSQL、VCS 等來實現該規則
- 配合 Sentinel Dashboard 使用
更多詳情請參考?動態規則配置。
規則生效的效果
判斷限流降級異常
在 Sentinel 中所有流控降級相關的異常都是異常類?BlockException?的子類:
- 流控異常:FlowException
- 熔斷降級異常:DegradeException
- 系統保護異常:SystemBlockException
- 熱點參數限流異常:ParamFlowException
我們可以通過以下函數判斷是否為 Sentinel 的流控降級異常:
BlockException.isBlockException(Throwable t);除了在業務代碼邏輯上看到規則生效,我們也可以通過下面簡單的方法,來校驗規則生效的效果:
- 暴露的 HTTP 接口:通過運行下面命令?curl http://localhost:8719/cnode?id=<資源名稱>,觀察返回的數據。如果規則生效,在返回的數據欄中的?block?以及?block(m)?中會有顯示
- 日志:Sentinel 提供秒級的資源運行日志以及限流日志,詳情可以參考:?日志
block 事件
Sentinel 提供以下擴展接口,可以通過?StatisticSlotCallbackRegistry?向?StatisticSlot?注冊回調函數:
- ProcessorSlotEntryCallback: callback when resource entry passed (onPass) or blocked (onBlocked)
- ProcessorSlotExitCallback: callback when resource entry successfully completed (onExit)
可以利用這些回調接口來實現報警等功能,實時的監控信息可以從?ClusterNode?中實時獲取。
其它 API
業務異常統計 Tracer
業務異常記錄類?Tracer?用于記錄業務異常。相關方法:
- trace(Throwable e):記錄業務異常(非?BlockException?異常),對應的資源為當前線程 context 下 entry 對應的資源。該方法必須在?SphU.entry(xxx)?成功之后且 exit 之前調用,否則當前 context 為空則會拋出異常。
- trace(Throwable e, int count):記錄業務異常(非?BlockException?異常),異常數目為傳入的?count。該方法必須在?SphU.entry(xxx)?成功之后且 exit 之前調用,否則當前 context 為空則會拋出異常。
- traceEntry(Throwable, int, Entry):向傳入 entry 對應的資源記錄業務異常(非?BlockException?異常),異常數目為傳入的?count。
如果用戶通過?SphU?或?SphO?手動定義資源,則 Sentinel 不能感知上層業務的異常,需要手動調用?Tracer.trace(ex)?來記錄業務異常,否則對應的異常不會統計到 Sentinel 異常計數中。注意不要在 try-with-resources 形式的?SphU.entry(xxx)?中使用,否則會統計不上。
從 1.3.1 版本開始,注解方式定義資源支持自動統計業務異常,無需手動調用?Tracer.trace(ex)?來記錄業務異常。Sentinel 1.3.1 以前的版本需要手動記錄。
上下文工具類 ContextUtil
相關靜態方法:
標識進入調用鏈入口(上下文):
以下靜態方法用于標識調用鏈路入口,用于區分不同的調用鏈路:
- public static Context enter(String contextName)
- public static Context enter(String contextName, String origin)
其中?contextName?代表調用鏈路入口名稱(上下文名稱),origin?代表調用來源名稱。默認調用來源為空。返回值類型為?Context,即生成的調用鏈路上下文對象。
流控規則中若選擇“流控方式”為“鏈路”方式,則入口資源名即為上面的?contextName。
注意:
- ContextUtil.enter(xxx)?方法僅在調用鏈路入口處生效,即僅在當前線程的初次調用生效,后面再調用不會覆蓋當前線程的調用鏈路,直到 exit。Context?存于 ThreadLocal 中,因此切換線程時可能會丟掉,如果需要跨線程使用可以結合?runOnContext?方法使用。
- origin 數量不要太多,否則內存占用會比較大。
退出調用鏈(清空上下文):
- public static void exit():該方法用于退出調用鏈,清理當前線程的上下文。
獲取當前線程的調用鏈上下文:
- public static Context getContext():獲取當前線程的調用鏈路上下文對象。
在某個調用鏈上下文中執行代碼:
- public static void runOnContext(Context context, Runnable f):常用于異步調用鏈路中 context 的變換。
指標統計配置
Sentinel 底層采用高性能的滑動窗口數據結構來統計實時的秒級指標數據,并支持對滑動窗口進行配置。主要有以下兩個配置:
- windowIntervalMs:滑動窗口的總的時間長度,默認為 1000 ms
- sampleCount:滑動窗口劃分的格子數目,默認為 2;格子越多則精度越高,但是內存占用也會越多
我們可以通過?SampleCountProperty?來動態地變更滑動窗口的格子數目,通過?IntervalProperty?來動態地變更滑動窗口的總時間長度。注意這兩個配置都是全局生效的,會影響所有資源的所有指標統計。
Dashboard
詳情請參考:Sentinel Dashboard 文檔。
總結
以上是生活随笔為你收集整理的Sentinel(三)之如何使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑快捷键大全电脑快捷键大全表格
- 下一篇: Sentinel(四)之工作主流程