Sentinel 流控規(guī)則詳解
本文轉(zhuǎn)載自微信公眾號「運維開發(fā)故事」,作者老鄭。轉(zhuǎn)載本文請聯(lián)系運維開發(fā)故事公眾號。
在前面兩篇文章給大家介紹了 Sentinel 的功能和基本使用?,F(xiàn)在我們繼續(xù)來學(xué)習(xí) Sentinel 控制臺的基本使用,以及一些規(guī)則配置的說明。讓大家能夠在工作中使用 Sentinel 得心應(yīng)手 (大部分理論和描述來源于官方文檔和網(wǎng)絡(luò))。
在正文開始之前,我先說一下我的基本環(huán)境信息
- jdk 1.8
- sentinel 1.8.0
- spring-boot 2.3.5.RELEASE
- spring-cloud Hoxton.SR8
- spring-cloud-alibaba 2.2.5.RELEASE
控制臺簡介
Sentinel 提供一個輕量級的開源控制臺,它提供機器發(fā)現(xiàn)以及健康情況管理、監(jiān)控(單機和集群),規(guī)則管理和推送的功能。這里,我們將會詳細(xì)講述如何通過簡單的步驟就可以使用這些功能。
Sentinel 控制臺包含如下功能:
- 查看機器列表以及健康情況:收集 Sentinel 客戶端發(fā)送的心跳包,用于判斷機器是否在線。
- 監(jiān)控 (單機和集群聚合):通過 Sentinel 客戶端暴露的監(jiān)控 API,定期拉取并且聚合應(yīng)用監(jiān)控信息,最終可以實現(xiàn)秒級的實時監(jiān)控。
- 規(guī)則管理和推送:統(tǒng)一管理推送規(guī)則。
- 鑒權(quán):生產(chǎn)環(huán)境中鑒權(quán)非常重要。這里每個開發(fā)者需要根據(jù)自己的實際情況進(jìn)行定制。
注意:Sentinel 控制臺目前僅支持單機部署。Sentinel 控制臺項目提供 Sentinel 功能全集示例,不作為開箱即用的生產(chǎn)環(huán)境控制臺,若希望在生產(chǎn)環(huán)境使用需要自行定制和改造。
Alibaba 提供了企業(yè)版本的 Sentinel 我們可以在 aliyun.com 上面購買 AHAS Sentinel
查看機器列表以及健康情況
如果我們正確的接入 Sentinel 之后我們可以在 Sentinel 控制臺的 機器列表 菜單中來查看我們服務(wù)節(jié)點的健康情況
如果 Sentinel 接入不成功,可以查閱 Sentinel 官方文檔或者 FAQ 來對應(yīng)排查
服務(wù)監(jiān)控
1. 實時監(jiān)控
同時,同一個服務(wù)下的所有機器的簇點信息會被匯總,并且秒級地展示在"實時監(jiān)控"下。
注意: 實時監(jiān)控僅存儲 5 分鐘以內(nèi)的數(shù)據(jù),如果需要持久化,需要通過調(diào)用實時監(jiān)控接口來定制。
注意:請確保 Sentinel 控制臺所在的機器時間與自己應(yīng)用的機器時間保持一致,否則會導(dǎo)致拉不到實時的監(jiān)控數(shù)據(jù)。
2. 簇點鏈路
簇點鏈路(單機調(diào)用鏈路)頁面實時的去拉取指定客戶端資源的運行情況。它一共提供兩種展示模式:一種用樹狀結(jié)構(gòu)展示資源的調(diào)用鏈路,另外一種則不區(qū)分調(diào)用鏈路展示資源的實時情況。
注意: 簇點鏈路監(jiān)控是內(nèi)存態(tài)的信息,它僅展示啟動后調(diào)用過的資源。
注意:請確保 Sentinel 控制臺所在的機器時間與自己應(yīng)用的機器時間保持一致,否則會導(dǎo)致拉不到實時的監(jiān)控數(shù)據(jù)。
3 流控規(guī)則
流量控制(flow control),其原理是監(jiān)控應(yīng)用流量的 QPS 或并發(fā)線程數(shù)等指標(biāo),當(dāng)達(dá)到指定的閾值時對流量進(jìn)行控制,以避免被瞬時的流量高峰沖垮,從而保障應(yīng)用的高可用性。
FlowSlot 會根據(jù)預(yù)設(shè)的規(guī)則,結(jié)合 NodeSelectorSlot、ClusterBuilderSlot、StatisticSlot 統(tǒng)計出來的實時信息進(jìn)行流量控制。
限流的直接表現(xiàn)是在執(zhí)行 Entry nodeA = SphU.entry(resourceName) 的時候拋出 FlowException 異常。FlowException 是 BlockException 的子類,您可以捕捉 BlockException 來自定義被限流之后的處理邏輯。
Sentinel 在觸發(fā)規(guī)則保護(hù)時,返回的異常頁面是一樣的。不好區(qū)分是因為哪種規(guī)則導(dǎo)致的異常。所以需要自定義異常返回信息,明確是觸發(fā)了哪種類型的規(guī)則。
- @Component
- public class SentinelBlockHandler implements BlockExceptionHandler {
- @Override
- public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
- BlockException e) throws Exception {
- CommonResult<Void> result = new CommonResult<>();
- if (e instanceof FlowException) {
- result = CommonResult.error(101, "接口限流了");
- } else if (e instanceof DegradeException) {
- result = CommonResult.error(102, "服務(wù)降級了");
- } else if (e instanceof ParamFlowException) {
- result = CommonResult.error(103, "熱點參數(shù)限流了");
- } else if (e instanceof SystemBlockException) {
- result = CommonResult.error(104, "系統(tǒng)規(guī)則(負(fù)載/...不滿足要求)");
- } else if (e instanceof AuthorityException) {
- result = CommonResult.error(105, "授權(quán)規(guī)則不通過");
- }
- // http狀態(tài)碼
- httpServletResponse.setStatus(500);
- httpServletResponse.setCharacterEncoding("utf-8");
- httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
- httpServletResponse.setContentType("application/json;charset=utf-8");
- // spring mvc自帶的json操作工具,叫jackson
- new ObjectMapper().writeValue(httpServletResponse.getWriter(), result);
- }
- }
效果如下:
- ➜ curl http://127.0.0.1:8088/getStockDetail
- {"code":1,"message":"this is a success message","data":{"id":1,"code":"STOCK==>1000"}}% ➜ curl http://127.0.0.1:8088/getStockDetail
- {"code":1,"message":"this is a success message","data":{"id":1,"code":"STOCK==>1000"}}% ➜ curl http://127.0.0.1:8088/getStockDetail
- {"code":101,"message":"接口限流了","data":null}%
閾值類型
線程數(shù)
并發(fā)數(shù)控制用于保護(hù)業(yè)務(wù)線程池不被慢調(diào)用耗盡。例如,當(dāng)應(yīng)用所依賴的下游應(yīng)用由于某種原因?qū)е路?wù)不穩(wěn)定、響應(yīng)延遲增加,對于調(diào)用者來說,意味著吞吐量下降和更多的線程數(shù)占用,極端情況下甚至導(dǎo)致線程池耗盡。為應(yīng)對太多線程占用的情況,業(yè)內(nèi)有使用隔離的方案,比如通過不同業(yè)務(wù)邏輯使用不同線程池來隔離業(yè)務(wù)自身之間的資源爭搶(線程池隔離)。這種隔離方案雖然隔離性比較好,但是代價就是線程數(shù)目太多,線程上下文切換的 overhead 比較大,特別是對低延時的調(diào)用有比較大的影響。Sentinel 并發(fā)控制不負(fù)責(zé)創(chuàng)建和管理線程池,而是簡單統(tǒng)計當(dāng)前請求上下文的線程數(shù)目(正在執(zhí)行的調(diào)用數(shù)目),如果超出閾值,新的請求會被立即拒絕,效果類似于信號量隔離。并發(fā)數(shù)控制通常在調(diào)用端進(jìn)行配置。
可以通過線程池模擬客戶端調(diào)用, 也可以通過 Jmeter 模擬,觸發(fā)流控的結(jié)果如下:
- ➜ ~ curl http://127.0.0.1:8088/getStockDetail
- {"code":101,"message":"接口限流了","data":null}%
流控模式
調(diào)用關(guān)系包括調(diào)用方、被調(diào)用方;一個方法又可能會調(diào)用其它方法,形成一個調(diào)用鏈路的層次關(guān)系。
直接
當(dāng)資源觸發(fā)流控規(guī)則過后直接,拋出異常信息
- ➜ ~ curl http://127.0.0.1:8088/getStockDetail
- {"code":101,"message":"接口限流了","data":null}%
關(guān)聯(lián)
當(dāng)兩個資源之間具有資源爭搶或者依賴關(guān)系的時候,這兩個資源便具有了關(guān)聯(lián)。比如對數(shù)據(jù)庫同一個字段的讀操作和寫操作存在爭搶,讀的速度過高會影響寫的速度,寫的速度過高會影響讀的速度。如果放任讀寫操作爭搶資源,則爭搶本身帶來的開銷會降低整體的吞吐量??墒褂藐P(guān)聯(lián)限流來避免具有關(guān)聯(lián)關(guān)系的資源之間過度的爭搶,舉例來說,read_db 和 write_db 這兩個資源分別代表數(shù)據(jù)庫讀寫,我們可以給 read_db 設(shè)置限流規(guī)則來達(dá)到寫優(yōu)先的目的:設(shè)置 strategy 為 RuleConstant.STRATEGY_RELATE 同時設(shè)置 refResource 為 write_db。這樣當(dāng)寫庫操作過于頻繁時,讀數(shù)據(jù)的請求會被限流。
如果配置流控規(guī)則為關(guān)聯(lián)模式,那么當(dāng) /hello 接口超過閾值過后,就會對 /getStockDetail 接口觸發(fā)流控規(guī)則。
鏈路
NodeSelectorSlot 中記錄了資源之間的調(diào)用鏈路,這些資源通過調(diào)用關(guān)系,相互之間構(gòu)成一棵調(diào)用樹。這棵樹的根節(jié)點是一個名字為 machine-root 的虛擬節(jié)點,調(diào)用鏈的入口都是這個虛節(jié)點的子節(jié)點。
一棵典型的調(diào)用樹如下圖所示:
- machine-root
- / \
- / \
- Entrance1 Entrance2
- / \
- / \
- DefaultNode(nodeA) DefaultNode(nodeA)
上圖中來自入口 Entrance1 和 Entrance2 的請求都調(diào)用到了資源 NodeA,Sentinel 允許只根據(jù)某個入口的統(tǒng)計信息對資源限流。比如我們可以設(shè)置 strategy 為 RuleConstant.STRATEGY_CHAIN,同時設(shè)置 refResource 為 Entrance1 來表示只有從入口 Entrance1 的調(diào)用才會記錄到 NodeA 的限流統(tǒng)計當(dāng)中,而不關(guān)心經(jīng) Entrance2 到來的調(diào)用。
調(diào)用鏈的入口(上下文)是通過 API 方法 ContextUtil.enter(contextName) 定義的,其中 contextName 即對應(yīng)調(diào)用鏈路入口名稱。詳情可以參考 ContextUtil 文檔。]
測試會發(fā)現(xiàn) 鏈路 不會生效
從1.6.3版本開始,Sentinel Web filter默認(rèn)收斂所有URL的入口context,因此鏈路限流不生效。1.7.0版本開始(對應(yīng)SCA 2.1.1.RELEASE),我們在CommonFilter引入了WEB_CONTEXT_UNIFY這個init parameter,用于控制是否收斂context。將其配置為false即可根據(jù)不同的URL進(jìn)行鏈路限流。參考:https://github.com/alibaba/sentinel/issues/1213
解決方案:
1.7.0 版本開始(對應(yīng)Spring Cloud Alibaba的2.1.1.RELEASE) 需要新增依賴
- @Configuration
- public class FilterContextConfig {
- @Bean
- public FilterRegistrationBean sentinelFilterRegistration() {
- FilterRegistrationBean registration = new FilterRegistrationBean();
- registration.setFilter(new CommonFilter());
- registration.addUrlPatterns("/*");
- // 入口資源關(guān)閉聚合
- registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
- registration.setName("sentinelFilter");
- registration.setOrder(1);
- return registration;
- }
- }
然后我們再嘗試觸發(fā)流控規(guī)則, 對 /getStockDetail 進(jìn)行訪問,這里返回了FlowException
默認(rèn)情況會返回
如果我們使用 OpenFeign 不添加 fallbackFactory 就會返回500 , 如果我們添加了就可以避免這個問題。
- // Controller
- @Autowired
- private StockFeign stockFeign;
- @GetMapping("/getStockDetail")
- public CommonResult<StockModel> getStockDetail() {
- CommonResult<StockModel> result = stockFeign.getStockDetail();
- if (result.getCode() != 1) {
- return CommonResult.error(null, result.getCode(), result.getMessage());
- }
- return result;
- }
- // FeignClient
- @FeignClient(name = "stock-service")
- //, fallbackFactory = StockFeignFallbackFactory.class)
- public interface StockFeign {
- @GetMapping("/getStockDetail")
- CommonResult<StockModel> getStockDetail();
- }
Sentinel 部分源碼:
所以,我們在設(shè)置鏈路流控規(guī)則的時候一定要設(shè)置 fallbackFactory。不然無法處理 FlowExecption 異常信息,造成系統(tǒng)出錯。對于個人而言,Sentinel 的鏈路規(guī)則比不是特別的好用,無特殊要求,不建議使用,或者選擇Sentinel 的收費版本 AHAS
流控效果
當(dāng) QPS、線程數(shù)超過某個閾值的時候,則采取措施進(jìn)行流量控制。流量控制的效果包括以下幾種:直接拒絕、Warm Up、勻速排隊。
直接拒絕
直接拒絕(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默認(rèn)的流量控制方式,當(dāng)QPS超過任意規(guī)則的閾值后,新的請求就會被立即拒絕,拒絕方式為拋出FlowException。 這種方式適用于對系統(tǒng)處理能力確切已知的情況下,比如通過壓測確定了系統(tǒng)的準(zhǔn)確水位時。
Warm up
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即預(yù)熱/冷啟動方式。當(dāng)系統(tǒng)長期處于低水位的情況下,當(dāng)流量突然增加時,直接把系統(tǒng)拉升到高水位可能瞬間把系統(tǒng)壓垮。通過"冷啟動",讓通過的流量緩慢增加,在一定時間內(nèi)逐漸增加到閾值上限,給冷系統(tǒng)一個預(yù)熱的時間,避免冷系統(tǒng)被壓垮。
通常冷啟動的過程系統(tǒng)允許通過的 QPS 曲線如下圖所示:
默認(rèn) coldFactor 為 3,即請求 QPS 從 threshold / 3 開始,經(jīng)預(yù)熱時長逐漸升至設(shè)定的 QPS 閾值。
規(guī)則設(shè)置如下圖所示:
通過 Jmeter 請求過后,可以看到如下效果,完成流控
勻速排隊
勻速排隊(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式會嚴(yán)格控制請求通過的間隔時間,也即是讓請求以均勻的速度通過,對應(yīng)的是漏桶算法。
這種方式主要用于處理間隔性突發(fā)的流量,例如消息隊列。想象一下這樣的場景,在某一秒有大量的請求到來,而接下來的幾秒則處于空閑狀態(tài),我們希望系統(tǒng)能夠在接下來的空閑期間逐漸處理這些請求,而不是在第一秒直接拒絕多余的請求。
注意:勻速排隊模式暫時不支持 QPS > 1000 的場景。
規(guī)則設(shè)置如下圖所示:
然后我們通過 jmeter 請求過后可以看到如下效果:
4. 降級規(guī)則
流量控制以外,對調(diào)用鏈路中不穩(wěn)定的資源進(jìn)行熔斷降級也是保障高可用的重要措施之一。一個服務(wù)常常會調(diào)用別的模塊,可能是另外的一個遠(yuǎn)程服務(wù)、數(shù)據(jù)庫,或者第三方 API 等。例如,支付的時候,可能需要遠(yuǎn)程調(diào)用銀聯(lián)提供的 API;查詢某個商品的價格,可能需要進(jìn)行數(shù)據(jù)庫查詢。然而,這個被依賴服務(wù)的穩(wěn)定性是不能保證的。如果依賴的服務(wù)出現(xiàn)了不穩(wěn)定的情況,請求的響應(yīng)時間變長,那么調(diào)用服務(wù)的方法的響應(yīng)時間也會變長,線程會產(chǎn)生堆積,最終可能耗盡業(yè)務(wù)自身的線程池,服務(wù)本身也變得不可用。
熔斷降級策略
Sentinel 提供以下幾種熔斷策略:
- 慢調(diào)用比例 (SLOW_REQUEST_RATIO):選擇以慢調(diào)用比例作為閾值,需要設(shè)置允許的慢調(diào)用 RT(即最大的響應(yīng)時間),請求的響應(yīng)時間大于該值則統(tǒng)計為慢調(diào)用。當(dāng)單位統(tǒng)計時長(statIntervalMs)內(nèi)請求數(shù)目大于設(shè)置的最小請求數(shù)目,并且慢調(diào)用的比例大于閾值,則接下來的熔斷時長內(nèi)請求會自動被熔斷。經(jīng)過熔斷時長后熔斷器會進(jìn)入探測恢復(fù)狀態(tài)(HALF-OPEN 狀態(tài)),若接下來的一個請求響應(yīng)時間小于設(shè)置的慢調(diào)用 RT 則結(jié)束熔斷,若大于設(shè)置的慢調(diào)用 RT 則會再次被熔斷。
我們可以在控制臺配置:
jmeter 模擬請求
異常比例 (ERROR_RATIO):當(dāng)單位統(tǒng)計時長(statIntervalMs)內(nèi)請求數(shù)目大于設(shè)置的最小請求數(shù)目,并且異常的比例大于閾值,則接下來的熔斷時長內(nèi)請求會自動被熔斷。經(jīng)過熔斷時長后熔斷器會進(jìn)入探測恢復(fù)狀態(tài)(HALF-OPEN 狀態(tài)),若接下來的一個請求成功完成(沒有錯誤)則結(jié)束熔斷,否則會再次被熔斷。異常比率的閾值范圍是 [0.0, 1.0],代表 0% - 100%。
我們可以在控制臺配置:
- 異常數(shù) (ERROR_COUNT):當(dāng)單位統(tǒng)計時長內(nèi)的異常數(shù)目超過閾值之后會自動進(jìn)行熔斷。經(jīng)過熔斷時長后熔斷器會進(jìn)入探測恢復(fù)狀態(tài)(HALF-OPEN 狀態(tài)),若接下來的一個請求成功完成(沒有錯誤)則結(jié)束熔斷,否則會再次被熔斷。
我們可以在控制臺配置:
熔斷降級說明
熔斷降級規(guī)則(DegradeRule)包含下面幾個重要的屬性:
Field |
說明 |
默認(rèn)值 |
resource |
資源名,即規(guī)則的作用對象 |
|
grade |
熔斷策略,支持慢調(diào)用比例/異常比例/異常數(shù)策略 |
慢調(diào)用比例 |
count |
慢調(diào)用比例模式下為慢調(diào)用臨界 RT(超出該值計為慢調(diào)用);異常比例/異常數(shù)模式下為對應(yīng)的閾值 |
|
timeWindow |
熔斷時長,單位為 s |
|
minRequestAmount |
熔斷觸發(fā)的最小請求數(shù),請求數(shù)小于該值時即使異常比率超出閾值也不會熔斷(1.7.0 引入) |
5 |
statIntervalMs |
統(tǒng)計時長(單位為 ms),如 60*1000 代表分鐘級(1.8.0 引入) |
1000 ms |
slowRatioThreshold |
慢調(diào)用比例閾值,僅慢調(diào)用比例模式有效(1.8.0 引入) |
5. 熱點規(guī)則
何為熱點?熱點即經(jīng)常訪問的數(shù)據(jù)。很多時候我們希望統(tǒng)計某個熱點數(shù)據(jù)中訪問頻次最高的 Top K 數(shù)據(jù),并對其訪問進(jìn)行限制。比如:
- 商品 ID 為參數(shù),統(tǒng)計一段時間內(nèi)最常購買的商品 ID 并進(jìn)行限制
- 用戶 ID 為參數(shù),針對一段時間內(nèi)頻繁訪問的用戶 ID 進(jìn)行限制
熱點參數(shù)限流會統(tǒng)計傳入?yún)?shù)中的熱點參數(shù),并根據(jù)配置的限流閾值與模式,對包含熱點參數(shù)的資源調(diào)用進(jìn)行限流。熱點參數(shù)限流可以看做是一種特殊的流量控制,僅對包含熱點參數(shù)的資源調(diào)用生效。
Sentinel 利用 LRU 策略統(tǒng)計最近最常訪問的熱點參數(shù),結(jié)合令牌桶算法來進(jìn)行參數(shù)級別的流控。熱點參數(shù)限流支持集群模式。
熱點規(guī)則配置需要注意:
1. 首先資源必須是通過 @SentinelResource 申明
2. 參數(shù)類型必須是基礎(chǔ)數(shù)據(jù)類型, 否則配置無效
熱點規(guī)則配置如下圖所示:
注意:資源名稱要和 @SentinelResource 中的資源名稱對應(yīng)才能生效
控制器類的代碼如下所示:
- @SentinelResource(value = "ResOrderGet",
- fallback = "fallback",
- fallbackClass = SentinelExceptionHandler.class,
- blockHandler = "blockHandler",
- blockHandlerClass = SentinelExceptionHandler.class
- )
- @GetMapping("/order/get/{id}")
- public CommonResult<StockModel> getStockDetails(@PathVariable Integer id) {
- StockModel stockModel = new StockModel();
- stockModel.setCode("STOCK==>1000");
- stockModel.setId(id);
- return CommonResult.success(stockModel);
- }
- // 異常處理類
- public class SentinelResourceExceptionHandler {
- //限流熔斷業(yè)務(wù)邏輯
- public static CommonResult<StockModel> blockHandler(@PathVariable Integer id) {
- return CommonResult.error(null, -100, "系統(tǒng)錯誤 (限流熔斷業(yè)務(wù)邏輯)");
- }
- //異常降級業(yè)務(wù)邏輯
- public static CommonResult<StockModel> fallback(@PathVariable Integer id) {
- return CommonResult.error(null, -100, "系統(tǒng)錯誤 (異常降級業(yè)務(wù)邏輯)");
- }
- }
返回異常信息:
6 授權(quán)規(guī)則
很多時候,我們需要根據(jù)調(diào)用來源來判斷該次請求是否允許放行,這時候可以使用 Sentinel 的來源訪問控制(黑白名單控制)的功能。來源訪問控制根據(jù)資源的請求來源(origin)限制資源是否通過,若配置白名單則只有請求來源位于白名單內(nèi)時才可通過;若配置黑名單則請求來源位于黑名單時不通過,其余的請求通過。
調(diào)用方信息通過 ContextUtil.enter(resourceName, origin) 方法中的 origin 參數(shù)傳入。
Sentinel提供了 RequestOriginParser 接口來處理訪問來源,Sentinel保護(hù)的資源如果被訪問,就會調(diào)用 RequestOriginParser解析訪問來源。
- // 注意導(dǎo)包
- import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
- import javax.servlet.http.HttpServletRequest;
- public class SentinelRequestOriginParser implements RequestOriginParser {
- @Override
- public String parseOrigin(HttpServletRequest request) {
- return request.getParameter("origin");
- }
- }
修改 Config 配置信息
- @Configuration
- public class FilterContextConfig {
- @Bean
- public FilterRegistrationBean sentinelFilterRegistration() {
- FilterRegistrationBean registration = new FilterRegistrationBean();
- registration.setFilter(new CommonFilter());
- registration.addUrlPatterns("/*");
- // 入口資源關(guān)閉聚合
- registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
- registration.setName("sentinelFilter");
- registration.setOrder(1);
- // CommonFilter 的 BlockException 自定義處理邏輯
- WebCallbackManager.setUrlBlockHandler(new SentinelFlowHandler());
- //解決授權(quán)規(guī)則不生效的問題
- //com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser
- WebCallbackManager.setRequestOriginParser(new SentinelRequestOriginParser());
- return registration;
- }
- }
規(guī)則配置
執(zhí)行請求
正常通過
異常不通過
7 系統(tǒng)規(guī)則
系統(tǒng)保護(hù)規(guī)則是從應(yīng)用級別的入口流量進(jìn)行控制,從單臺機器的 load、CPU 使用率、平均 RT、入口 QPS 和并發(fā)線程數(shù)等幾個維度監(jiān)控應(yīng)用指標(biāo),讓系統(tǒng)盡可能跑在最大吞吐量的同時保證系統(tǒng)整體的穩(wěn)定性。
系統(tǒng)保護(hù)規(guī)則是應(yīng)用整體維度的,而不是資源維度的,并且僅對入口流量生效。入口流量指的是進(jìn)入應(yīng)用的流量(EntryType.IN),比如 Web 服務(wù)或 Dubbo 服務(wù)端接收的請求,都屬于入口流量。
系統(tǒng)規(guī)則支持以下的模式:
- Load 自適應(yīng)(僅對 Linux/Unix-like 機器生效):系統(tǒng)的 load1 作為啟發(fā)指標(biāo),進(jìn)行自適應(yīng)系統(tǒng)保護(hù)。當(dāng)系統(tǒng) load1 超過設(shè)定的啟發(fā)值,且系統(tǒng)當(dāng)前的并發(fā)線程數(shù)超過估算的系統(tǒng)容量時才會觸發(fā)系統(tǒng)保護(hù)(BBR 階段)。系統(tǒng)容量由系統(tǒng)的 maxQps * minRt 估算得出。設(shè)定參考值一般是 CPU cores * 2.5。
- CPU usage(1.5.0+ 版本):當(dāng)系統(tǒng) CPU 使用率超過閾值即觸發(fā)系統(tǒng)保護(hù)(取值范圍 0.0-1.0),比較靈敏。
- 平均 RT:當(dāng)單臺機器上所有入口流量的平均 RT 達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù),單位是毫秒。
- 并發(fā)線程數(shù):當(dāng)單臺機器上所有入口流量的并發(fā)線程數(shù)達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)。
- 入口 QPS:當(dāng)單臺機器上所有入口流量的 QPS 達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)。
原理
如下圖所示
我們把系統(tǒng)處理請求的過程想象為一個水管,到來的請求是往這個水管灌水,當(dāng)系統(tǒng)處理順暢的時候,請求不需要排隊,直接從水管中穿過,這個請求的RT是最短的;反之,當(dāng)請求堆積的時候,那么處理請求的時間則會變?yōu)椋号抨爼r間 + 最短處理時間。
- 推論一: 如果我們能夠保證水管里的水量,能夠讓水順暢的流動,則不會增加排隊的請求;也就是說,這個時候的系統(tǒng)負(fù)載不會進(jìn)一步惡化。
我們用 T 來表示(水管內(nèi)部的水量),用RT來表示請求的處理時間,用P來表示進(jìn)來的請求數(shù),那么一個請求從進(jìn)入水管道到從水管出來,這個水管會存在 P * RT 個請求。換一句話來說,當(dāng) T ≈ QPS * Avg(RT) 的時候,我們可以認(rèn)為系統(tǒng)的處理能力和允許進(jìn)入的請求個數(shù)達(dá)到了平衡,系統(tǒng)的負(fù)載不會進(jìn)一步惡化。
接下來的問題是,水管的水位是可以達(dá)到了一個平衡點,但是這個平衡點只能保證水管的水位不再繼續(xù)增高,但是還面臨一個問題,就是在達(dá)到平衡點之前,這個水管里已經(jīng)堆積了多少水。如果之前水管的水已經(jīng)在一個量級了,那么這個時候系統(tǒng)允許通過的水量可能只能緩慢通過,RT會大,之前堆積在水管里的水會滯留;反之,如果之前的水管水位偏低,那么又會浪費了系統(tǒng)的處理能力。
- 推論二: 當(dāng)保持入口的流量是水管出來的流量的最大的值的時候,可以最大利用水管的處理能力。
然而,和 TCP BBR 的不一樣的地方在于,還需要用一個系統(tǒng)負(fù)載的值(load1)來激發(fā)這套機制啟動。
注:這種系統(tǒng)自適應(yīng)算法對于低 load 的請求,它的效果是一個“兜底”的角色。對于不是應(yīng)用本身造成的 load 高的情況(如其它進(jìn)程導(dǎo)致的不穩(wěn)定的情況),效果不明顯。
配置頁面
觸發(fā)流控規(guī)則
8 集群流控
為什么要使用集群流控呢?假設(shè)我們希望給某個用戶限制調(diào)用某個 API 的總 QPS 為 50,但機器數(shù)可能很多(比如有 100 臺)。這時候我們很自然地就想到,找一個 server 來專門來統(tǒng)計總的調(diào)用量,其它的實例都與這臺 server 通信來判斷是否可以調(diào)用。這就是最基礎(chǔ)的集群流控的方式。
另外集群流控還可以解決流量不均勻?qū)е驴傮w限流效果不佳的問題。假設(shè)集群中有 10 臺機器,我們給每臺機器設(shè)置單機限流閾值為 10 QPS,理想情況下整個集群的限流閾值就為 100 QPS。不過實際情況下流量到每臺機器可能會不均勻,會導(dǎo)致總量沒有到的情況下某些機器就開始限流。因此僅靠單機維度去限制的話會無法精確地限制總體流量。而集群流控可以精確地控制整個集群的調(diào)用總量,結(jié)合單機限流兜底,可以更好地發(fā)揮流量控制的效果。
集群流控中共有兩種身份:
- Token Client:集群流控客戶端,用于向所屬 Token Server 通信請求 token。集群限流服務(wù)端會返回給客戶端結(jié)果,決定是否限流。
- Token Server:即集群流控服務(wù)端,處理來自 Token Client 的請求,根據(jù)配置的集群規(guī)則判斷是否應(yīng)該發(fā)放 token(是否允許通過)。
規(guī)則推送
Sentinel 控制臺同時提供簡單的規(guī)則管理以及推送的功能。規(guī)則推送分為 3 種模式,包括 "原始模式"、"Pull 模式" 和"Push 模式"。
這里先簡單的介紹"原始模式"。
規(guī)則管理
您可以在控制臺通過接入端暴露的 HTTP API 來查詢規(guī)則。
規(guī)則推送
目前控制臺的規(guī)則推送也是通過 規(guī)則查詢更改 HTTP API 來更改規(guī)則。這也意味著這些規(guī)則僅在內(nèi)存態(tài)生效,應(yīng)用重啟之后,該規(guī)則會丟失。
注:若通過控制臺推送規(guī)則時出現(xiàn) invalid type 或 empty type 的錯誤,請確保 transport 模塊版本與 core 模塊版本保持一致;若控制臺版本 >= 1.7.1,請將接入端的相關(guān)依賴也升級至 1.7.1 及以上版本。
以上是原始模式。當(dāng)了解了原始模式之后,我們非常鼓勵您通過 動態(tài)規(guī)則 并結(jié)合各種外部存儲來定制自己的規(guī)則源。我們推薦通過動態(tài)配置源的控制臺來進(jìn)行規(guī)則寫入和推送,而不是通過 Sentinel 客戶端直接寫入到動態(tài)配置源中。在生產(chǎn)環(huán)境中,我們推薦 push 模式,具體可以參考:在生產(chǎn)環(huán)境使用 Sentinel。
注:若要使用集群流控功能,則必須對接動態(tài)規(guī)則源,否則無法正常使用。您也可以接入 AHAS Sentinel 快速接入全自動托管、高可用的集群流控能力。
Sentinel 同時還提供應(yīng)用維度規(guī)則推送的示例頁面(流控規(guī)則頁面,前端路由為 /v2/flow),用戶改造控制臺對接配置中心后可直接通過 v2 頁面推送規(guī)則至配置中心。Sentinel 抽取了通用接口用于向遠(yuǎn)程配置中心推送規(guī)則以及拉取規(guī)則:
DynamicRuleProvider
DynamicRulePublisher
用戶只需實現(xiàn) DynamicRuleProvider 和 DynamicRulePublisher 接口,并在 v2 的 controller 中通過 @Qualifier 注解替換相應(yīng)的 bean 即可實現(xiàn)應(yīng)用維度推送。我們提供了 Nacos 和 Apollo 的示例,改造詳情可參考 應(yīng)用維度規(guī)則推送示例。
鑒權(quán)
從 Sentinel 1.5.0 開始,控制臺提供通用的鑒權(quán)接口 AuthService,用戶可根據(jù)需求自行實現(xiàn)。
從 Sentinel 1.6.0 起,Sentinel 控制臺引入基本的登錄功能,默認(rèn)用戶名和密碼都是 sentinel。該鑒權(quán)能力非?;A(chǔ),生產(chǎn)環(huán)境使用建議根據(jù)安全需要自行改造。
用戶可以通過如下參數(shù)進(jìn)行配置:
- -Dsentinel.dashboard.auth.username=sentinel 用于指定控制臺的登錄用戶名為 sentinel;
- -Dsentinel.dashboard.auth.password=123456 用于指定控制臺的登錄密碼為 123456;如果省略這兩個參數(shù),默認(rèn)用戶和密碼均為 sentinel;
- -Dserver.servlet.session.timeout=7200 用于指定 Spring Boot 服務(wù)端 session 的過期時間,如 7200 表示 7200 秒;60m 表示 60 分鐘,默認(rèn)為 30 分鐘;
同樣也可以直接在 Spring properties 文件中進(jìn)行配置。
注意:部署多臺控制臺時,session 默認(rèn)不會在各實例之間共享,這一塊需要自行改造。
參考文檔
https://github.com/alibaba/Sentinel/wiki/控制臺