sentinel 限流熔断神器详细介绍
一、限流熔斷神器 sentinel
1、什么是 sentinel:
????????在基于 SpringCloud 構建的微服務體系中,服務間的調用鏈路會隨著系統的演進變得越來越長,這無疑會增加了整個系統的不可靠因素。在并發流量比較高的情況下,由于網絡調用之間存在一定的超時時間,鏈路中的某個服務出現宕機都會大大增加整個調用鏈路的響應時間,而瞬間的流量洪峰則會導致這條鏈路上所有服務的可用線程資源被打滿,從而造成整體服務的不可用,這也就是我們常說的 “雪崩效應”。而在微服務系統設計的過程中,為了應對這樣的糟糕情況,最常用的手段就是進行 ”流量控制“ 以及對網絡服務的調用實現“熔斷降級”。因此,Sentinel 就因運而生了。
????????Sentinel 是一款面向分布式服務架構的輕量級流量控制組件,主要以流量為切入點,從流量控制、熔斷降級、系統自適應保護等多個維度來保障服務的穩定性,核心思想是:根據對應資源配置的規則來為資源執行相應的流控/降級/系統保護策略,Sentinel 的主要特性如下圖:
2、主流限流熔斷組件對比:
????????從三者的對比來看,Hystrix 功能相對較少且官方已宣布停止維護,官方推薦 resilience4j 作為 Hystrix 的替代品。而對比 resilience4j 和 sentinel,可明顯看出 sentinel 支持的功能、場景更豐富,靈活性可用性更高。
3、sentinel-dashboard 控制臺的部署與使用說明:
(1)進入官網下載 sentinel-dashboard 的 jar 包:Releases · alibaba/Sentinel · GitHub
(2)使用以下命令直接運行 jar 包(JDK 版本必須≥ 1.8):
java -jar -Dserver.port=9999 sentinel-dashboard-1.8.2.jar
(3)啟動參數說明:
- -Dserver.port:指定啟動的端口,默認8080
- -Dproject.name:指定本服務的名稱
- -Dcsp.sentinel.dashboard.server:指定sentinel控制臺的地址,用于將自己注冊進入實現監控自己
- -Dsentinel.dashboard.auth.username=sentinel 用于指定控制臺的登錄用戶名為 "sentinel",默認值為 “sentinel”
- -Dsentinel.dashboard.auth.password=123456 用于指定控制臺的登錄密碼為 "123456",默認值為 "sentinel"
- -Dserver.servlet.session.timeout=7200 用于指定 Spring Boot 服務端 session 的過期時間,如 7200 表示 7200 秒;60m 表示 60 分鐘,默認為 30 分鐘,需要注意的是,部署多臺控制臺時,session 默認不會在各實例之間共享,這一塊需要自行改造。
更多具體啟動參數配置如下圖:
官方文檔地址:https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0
?(4)控制臺訪問地址:http://localhost:9999,默認賬號密碼都為 sentinel/sentinel
二、SpringBoot 整合 sentinel-dashboard
? ? ? ? ?接下來我們介紹下 SpringBoot 集成 sentinel-dashboard 控制臺進行規則配置。那為什么要集成 sentinel-dashboard 控制臺,sentinel 不是提供了相關的API嗎?其實 SpringBoot 官方一直提倡 約定>配置>編碼 的規則,能夠不硬編碼何樂而不為呢?因此本文后續內容主要還是結合 sentinel-dashboard 控制臺進行講解,關于API的使用大家可以按照官方文檔學習,講解的非常清楚,這里就不過多介紹了。
1、引入依賴:
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>2、添加配置:
# sentinel看板的地址 spring.cloud.sentinel.transport.dashboard = localhost:9999 # 開啟對sentinel看板的饑餓式加載。sentinel默認是懶加載機制,只有訪問過一次的資源才會被監控,通過關閉懶加載,在項目啟動時就連接sentinel控制臺 spring.cloud.sentinel.eager = true3、新建測試接口:
@RestController @RequestMapping("/sentinel") public class FlowLimitController {@GetMapping("/test")public String test(){return "接收到一條消息--------";} }4、啟動微服務:
啟動微服務,然后瀏覽器輸入:http://localhost:9999/sentinel/test,此時查看sentinel控制臺,將會看見?sentinel-service?這個服務已經被監控了,如下圖:
三、Sentinel-dashboard的使用與規則配置
????????前面我們基于 SpringBoot 整合了 Sentinel-dashboard,這一部分我們就詳細介紹下 sentinel-dashboard 控制臺的使用。
1、資源與規則:
????????Sentinel 可以簡單分為 Sentinel 核心庫和 Dashboard,核心庫不依賴 Dashboard,但是結合 Dashboard 可以獲得更好的效果。使用 Sentinel 來進行資源保護,主要分為幾個步驟:
(1)定義資源:資源可以是程序中的任何內容,例如一個服務,服務里的方法,甚至是一段代碼。
(2)定義規則:Sentinel 支持以下幾種規則:流量控制規則、熔斷降級規則、系統保護規則、來源訪問控制規則 和 熱點參數規則。
(3)檢驗規則是否生效
????????由于 Sentinel 中所有的規則都可以在動態地查詢和修改,并且修改后立即生效,并且 Sentinel 中資源定義和規則的配置是分離的。因此在編碼時,我們先把需要保護的資源定義好(埋點),之后便可以在需要的時候動態配置規則了。也可以理解為,只要有了資源,我們就能在任何時候靈活地定義各種規則。
1.1、定義資源:
對于資源的定義有兩種,一種是硬編碼的方式,一種是通過 @SentinelResource 注解的方式。
(1)硬編碼方式(不推薦):
但由于硬編碼的方式對代碼的侵入性太強,不推薦使用,所以我們在下文只簡單介紹下了如何使用
① 拋出異常的方式定義資源:SphU
????????使用 SphU.entry(“資源名”)定義資源的方式主要用于 try-catch,將需要保護的代碼使用 SphU.entry("資源名") 和 entry.exit() 包圍起來,當 catch 到 BlockException 時執行異常處理
代碼示例:
Entry entry = null;try {// 定義一個sentinel保護的資源,entry = SphU.entry(resourceName);// 被保護的業務邏輯} catch (BlockException e) {// 如果被保護的資源被限流或者降級了,就會拋出BlockExceptionlog.warn("資源被限流或降級了", e);} catch (InterruptedException e) {log.error("發生InterruptedException",e);} finally {if (entry != null) {entry.exit();}}在下面的例子中, 用 try-with-resources 來定義資源。參考代碼如下:
public static void main(String[] args) {// 配置規則.initFlowRules();// 1.5.0 版本開始可以直接利用 try-with-resources 特性try (Entry entry = SphU.entry(resourceName)) {// 被保護的邏輯System.out.println("hello world");} catch (BlockException ex) {// 處理被流控的邏輯System.out.println("blocked!");} }② 返回布爾值的方式定義資源:
????????使用 SphO.entry(“資源名”) 定義資源的方式主要用于 if-else,當資源發生觸發 sentinel 規則之后就會返回 false,這時可以根據返回值,進行限流降級的處理邏輯。示例代碼如下:
// 資源名可使用任意有業務語義的字符串if (SphO.entry("自定義資源名")) {// 務必保證finally會被執行try {/*** 被保護的業務邏輯*/} finally {SphO.exit();}} else {// 資源訪問阻止,被限流或被降級// 進行相應的處理操作}注意:SphO.entry(xxx) 需要與 SphO.exit() 方法成對出現,匹配調用,位置正確,否則會導致調用鏈記錄異常,拋出 ErrorEntryFreeException 異常。
(2)@SentinelResource注解方式(推薦):
????????使用 @SentinelResource 注解可以更加的靈活的定義資源,如下示例就定義了一個名為 “HelloWorld” 的資源
@SentinelResource("HelloWorld") public void helloWorld() {// 資源中的邏輯System.out.println("hello world"); }????????需要注意的是,@SentinelResource 注解用于定義資源埋點,但不支持 private 方法。默認情況下,Sentinel 對控制資源的保護處理是直接拋出異常,這樣對用戶不友好,所以我們需要通過可選的異常處理 blockHandler 和 fallback 配置項處理一下異常信息。 @SentinelResource 注解包含以下屬性:
-
① value:資源名稱,必需項
-
② entryType:entry 類型,可選項(默認為 EntryType.OUT)
-
③ blockHandler / blockHandlerClass:blockHandler 指定函數負責處理 BlockException 異常,可選項。blockHandler 函數默認需要和原方法在同一個類中,通過指定 blockHandlerClass 為對應類的 Class 對象,則可以指定其他類中的函數,但注意對應的函數必需為 static 函數,否則無法解析。
blockHandler 函數訪問范圍需要是 public,返回類型需要與原方法相匹配,參數類型必須和原方法一致并且最后加一個類型為 BlockException 的異常參數。
-
④ fallback /fallbackClass:fallback 指定的函數負責處理業務運行的異常,可選項,fallback 函數可以針對所有類型的異常(除了exceptionsToIgnore里面排除掉的異常類型)進行處理。fallback 函數默認需要和原方法在同一個類中,通過指定 fallbackClass 為對應類的 Class 對象,則可以指定指定為其他類的函數,但注意對應的函數必需為 static 函數,否則無法解析。
fallback 函數的返回值類型必須與原函數返回值類型一致;方法參數列表需要和原函數一致,或者可以額外多一個 Throwable 類型的參數用于接收對應的異常。
-
⑤ defaultFallback(since 1.6.0):默認的 fallback 函數名稱,可選項,通常用于通用的 fallback 邏輯。defaultFallback 函數默認需要和原方法在同一個類中,通過指定 fallbackClass 為對應類的 Class 對象,則可以指定指定為其他類的函數,但注意對應的函數必需為 static 函數,否則無法解析。defaultFallback 函數可以針對所有類型的異常(除了 exceptionsToIgnore 里面排除掉的異常類型)進行處理。若同時配置了 fallback和 defaultFallback,則只有 fallback會生效。
defaultFallback函數的返回值類型必須與原函數返回值類型一致;方法參數列表需要為空,或者可以額外多一個 Throwable 類型的參數用于接收對應的異常。
-
⑥ exceptionsToIgnore(since 1.6.0):用于指定哪些異常被排除掉,不會計入異常統計中,也不會進入 fallback 邏輯中,而是會原樣拋出。
????????特別地,若 blockHandler 和 fallback 都進行了配置,則被限流降級而拋出 BlockException 時只會進入 blockHandler 處理邏輯。若未配置 blockHandler、fallback 和 defaultFallback,則被限流降級時會將 BlockException 直接拋出(若方法本身未定義 throws BlockException 則會被 JVM 包裝一層 UndeclaredThrowableException)
@SentinelResource 的 fallback 負責業務運行的異常,blockHandler 負責 sentinel 配置的違規。
1.2、定義規則:
????????Sentinel 支持以下幾種規則:流量控制規則、熔斷降級規則、系統保護規則、來源訪問控制規則 和 熱點參數規則。而定義規則同樣支持兩種方式:一種是硬編碼的方式,一種是通過 sentinel-dashboard 控制臺直接配置,這部分我們在下文會繼續介紹。
2、流控規則:
2.1、流控規則的核心屬性:
????????流量控制,原理是監控應用流量的 QPS 或 并發線程數 等指標,當達到指定閾值時對流量進行控制,避免系統被瞬時的流量高峰沖垮,保障應用高可用性。同一個資源可以創建多條限流規則,一條限流規則由以下屬性組成:
① resource:資源名,即限流規則的作用對象,默認請求路徑。
② limitApp:流控針對的調用來源,若為 default 則不區分調用來源,默認值default
③ count:限流閾值
④ grade:限流閾值類型(1代表 QPS,0 代表并發線程數),默認值QPS
⑤ strategy:流控模式
- 直接拒絕(默認):接口達到限流條件時,直接限流
- 關聯:當關聯的資源達到閾值時,就限流自己(適合做應用讓步)
- 鏈路:只記錄指定鏈路上的流量,指定資源從入口資源進來的流量,如果達到閾值,就可以限流
⑥ controlBehavior:流控效果
- 快速失敗(默認):當 QPS 超過任意規則的閾值后,新的請求就會被立即拒絕,拒絕方式為拋出FlowException
- 排隊等待:這種方式嚴格控制了請求通過的間隔時間,也即是讓請求以均勻的速度通過,對應的是漏桶算法。
- Warm Up:該方式主要用于系統長期處于低水位的情況下,當流量突然增加時,直接把系統拉升到高水位可能瞬間把系統壓垮。通過"冷啟動",讓通過的流量緩慢增加,在一定時間內逐漸增加到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮的情況。預熱底層是根據令牌桶算法實現的。
以上幾種屬性在 sentinel-dashboard 控制臺對應的規則如下圖:
?2.2、閾值類型:
(1)基于 QPS 的流控:
????????QPS,每秒請求數,即在不斷向服務器發送請求的情況下,服務器每秒能夠處理的請求數量。
(2)基于并發線程數的流控:
??? ??? ?并發數控制用于保護業務線程池不被慢調用耗盡。例如,當應用所依賴的下游應用由于某種原因導致服務不穩定、響應延遲增加,對于調用者來說,意味著吞吐量下降和更多的線程數占用,極端情況下甚至導致線程池耗盡。為應對太多線程占用的情況,業內有使用隔離的方案,比如:
- 通過不同業務邏輯使用不同線程池來隔離業務自身之間的資源爭搶(線程池隔離),這種隔離方案雖然隔離性比較好,但是代價就是線程數目太多,線程上下文切換的 overhead 比較大,特別是對低延時的調用有比較大的影響。
- 使用信號量來控制同時請求的個數(信號量隔離),這種隔離方案雖然能夠控制線程數量,但無法控制請求排隊時間,當請求過多時排隊也是無益的,直接拒絕能夠迅速降低系統壓力。
????????Sentinel 并發線程數限流不負責創建和管理線程池,而是簡單統計當前請求上下文的線程個數(正在執行的調用數目),如果超出閾值,新的請求會被立即拒絕,效果類似于信號量隔離。并發數控制通常在調用端進行配置。
2.3、流控效果:
??? ??? ?當系統的流量超過設定的閾值時,sentinel 則采取措施進行流量控制,流控效果總共分為三種:快速失敗、Warm Up、排隊等待。對應的 FlowRule 中的 controlBehavior 字段。
- 快速失敗(默認):當 QPS 超過任意規則的閾值后,新的請求就會被立即拒絕,拒絕方式為拋出FlowException
- 排隊等待:這種方式嚴格控制了請求通過的間隔時間,也即是讓請求以均勻的速度通過,對應的是漏桶算法。
- Warm Up:該方式主要用于系統長期處于低水位的情況下,當流量突然增加時,直接把系統拉升到高水位可能瞬間把系統壓垮。通過"冷啟動",讓通過的流量緩慢增加,在一定時間內逐漸增加到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮的情況。預熱底層是根據令牌桶算法實現的。
注意:若使用除了快速失敗之外的流量控制效果,則調用關系限流策略(strategy)會被忽略。
(1)快速失敗:
????????默認的流量控制方式,當 QPS 超過任意規則的閾值后,新的請求就會被立即拒絕,拒絕方式為拋出FlowException。這種方式適用于對系統處理能力確切已知的情況下,比如通過壓測確定了系統的準確水位時
(2)Warm Up:
注意:該方式只針對 QPS 流控,對并發線程數流控不支持
????????即預熱/冷啟動方式,該方式主要用于系統長期處于低水位的情況下,當流量突然增加時,直接把系統拉升到高水位可能瞬間把系統壓垮。通過"冷啟動",讓通過的流量緩慢增加,在一定時間內逐漸增加到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮的情況。
????????預熱底層是根據令牌桶算法實現的,源碼對應得類在 com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController 中,算法中有一個冷卻因子coldFactor,默認值是3,即請求 QPS 從 threshold(閾值) / 3 開始,經預熱時長逐漸升至設定的 QPS 閾值。
????????比如通過 sentinel-dashboard 設定 testWarmUP 資源的 QPS 閾值為,流控效果為 warm up,預熱時長為5秒,如下圖所示,testWarmUP 資源剛開始限流的閾值為 20/3=7,但經過10秒的預熱后,慢慢將閾值升至20。
?通常冷啟動的過程系統允許通過的 QPS 曲線如下圖所示:
? ? ? ? ?比如秒殺系統在開啟瞬間,會有很多流量上來,很可能把系統打死,預熱方式就是為了保護系統,可慢慢的把流量放進來,慢慢的把閾值增長到設置的閾值。
(3)排隊等待:
注意:這一效果只針對QPS流控,并發線程數流控不支持。
?? ??? ?排隊等待的方式會以勻速排隊方式嚴格控制請求通過的間隔時間,也就是讓請求以均勻的速度通過,其余的排隊等待,它還會讓設置一個超時時間,當請求超過超時間時間還未處理,則會被丟棄。對應的是漏桶算法,源碼對應得類:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController。該方式的作用如下圖:
?????????該方式主要用于處理間隔性突發的流量。假設某時刻來了大流量的請求,如果此時要處理所有請求,很可能會導致系統負載過高,影響穩定性。但其實接下來幾秒可能系統處于空閑狀態,若直接把多余的請求丟棄則沒有充分利用系統的處理能力,所以我們希望系統能夠在接下來的空閑期間逐漸處理這些請求,而不是在第一秒直接拒絕多余的請求。
????????Sentinel 的 Rate Limiter 模式能在某一段時間間隔內以勻速方式處理這樣的請求, 充分利用系統的處理能力, 也就是削峰填谷, 保證資源的穩定性。Sentinel會以固定的間隔時間讓請求通過, 訪問資源,當請求到來時,如果當前請求距離上個通過的請求通過的時間間隔不小于預設值,則讓當前請求通過;否則,計算當前請求的預期通過時間,如果該請求的預期通過時間大于規則預設的 timeout 時間,則該請求會等待直到預設時間到來通過;反之,則馬上拋出阻塞異常。
????????下圖能很形象的展示這種場景的削峰填谷的作用:X軸代表時間,Y軸代表系統處理的請求
????????比如通過 sentinel-dashboard 對 service 資源設置限流閾值為10,流控效果為排隊等候,每秒10次請求時,再有請求就排隊等候,等待超時時間為 10000ms,超時過后,請求將被踢出排隊隊列,返回限流異常。
?2.4、流控模式:
????????調用關系包括調用方、被調用方;一個方法又可能會調用其它方法,形成一個調用鏈路的層次關系。Sentinel 通過 NodeSelectorSlot 建立不同資源間的調用的關系,并且通過 ClusterBuilderSlot 記錄每個資源的實時統計信息。有了調用鏈路的統計信息,我們可以衍生出多種流量控制模式,總共分為三種,對應屬性為 strategy:
- 直接拒絕(默認):接口達到限流條件時,直接限流
- 關聯:當關聯的資源達到閾值時,就限流自己(適合做應用讓步)
- 鏈路:只記錄指定鏈路上的流量,指定資源從入口資源進來的流量,如果達到閾值,就可以限流
(1)直接拒絕:
????????默認的流控模式,當QPS超過任意規則的閾值后,新的請求就會被立即拒絕,拒絕方式為拋出 FlowException,這里不再詳細介紹。
(2)關聯:
????????當兩個資源之間具有資源爭搶或者依賴關系的時候,這兩個資源便具有了關聯,該流控模式的作用是當關聯的資源達到閾值時,就限流自己,適合做應用讓步。比如對數據庫同一個字段的讀操作和寫操作存在爭搶,讀的速度過高會影響寫得速度,寫的速度過高會影響讀的速度,如果放任讀寫操作爭搶資源,則爭搶本身帶來的開銷會降低整體的吞吐量,這時可使用關聯限流來避免具有關聯關系的資源之間過度的爭搶,舉例來說,read_db 和 write_db 這兩個資源分別代表數據庫讀寫,我們可以給 read_db 設置限流規則來達到寫優先的目的
????????還有一個例子,電商的 下訂單 和 支付 兩個操作,一旦支付接口達到了閾值,那么需要優先保障支付操作, 那么訂單接口就應用被限流,從而保護支付的目的。
@RestController @RequestMapping("/sentinel") public class FlowLimitController {/*** 下單接口*/@GetMapping("/order")public String order() {return "下單成功..........";}/*** 支付接口*/@GetMapping("/pay")public String pay() {return "支付成功..........";} }此時的流控規則配置如下圖:
注意:關聯之后,這里設置的限流規則是針對被關聯資源,也就是 /sentinel/pay 這個資源,但是真正被限流則是 /sentinel/order。我們只需要不斷的請求 /sentinel/pay 達到閾值,然后在請求 /sentinel/order,就可以看到訂單接口被限流了
?(3)鏈路:
????????該流控模式針對資源鏈路上的接口進行限流,例如:A、B兩個接口都調用某一資源C,A -> C、B -> C 可以看成兩個簡單的鏈路,此時可以針對C配置鏈路限流,比如限制A調用C,而B調用C則不受影響,它的功能有點類似于針對來源配置項,但鏈路流控是針對上級接口,它的粒度更細。
@RestController public class TestController {@AutowiredTestService testService;@GetMapping("/test")public String test() throws InterruptedException {testService.test();System.out.println("鏈路流控模式限流示例");return "test";}@GetMapping("/test1")public String test1(){testService.test();System.out.println("正常鏈路接口");return "test";} } @Service public class TestService {@SentinelResource(value = "service")public void test() {System.out.println("service");} }添加配置:
spring.cloud.sentinel.web-context-unify=false此時的流控規則配置如下圖:
不斷訪問 test 和 test1 兩個接口,發現即使 test 接口被限流了,test1 接口也不受影響。
2.5、流控規則的硬編碼方式:
????????前面的示例都是基于 sentinel-dashboard 控制臺去定義限流規則,但我們也可以通過調用 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); }官網示例鏈接:https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6%E8%A7%84%E5%88%99-flowrule
3、熔斷降級規則:
3.1、熔斷降級規則的核心屬性:
????????熔斷機制可以防止應用程序不斷執行可能失敗的操作,快速地拒絕可能導致錯誤的調用,當感知到下游服務的資源出現不穩定狀態(調用超時或異常比例升高時),暫時切斷對下游服務的調用,而不是一直阻塞等待服務響應,阻止級聯失敗導致的雪崩效應,保證系統的可用性;尤其是后端太忙的時候,使用熔斷設計可以保護后端不會過載。另外,在微服務架構下,我們一般都會設置超時與重試機制,但如果錯誤太多,或是在短時間內得不到修復,那么我們重試也沒有意義了,這時也需要使用熔斷機制快速返回結果。當資源被熔斷降級后,在接下來的降級時間窗口之內,對該資源的調用都會自動返回降級數據(默認行為是拋出 DegradeException),當檢測到該節點的服務調用響應正常后,則恢復調用鏈路。熔斷降級規則包含以下核心屬性:
?以上幾種屬性在 sentinel-dashboard 控制臺對應的規則如下圖:
3.2、熔斷策略:
sentinel 提供了以下幾種榮熔斷策略:
(1)平均響應時間 :
????????DEGRADE_GRADE_RT,當資源的平均響應時間超過閾值(DegradeRule 中的 count,以 ms 為單位)之后,資源會進入降級狀態,那么在接下的時間窗口(DegradeRule 中的 timeWindow,以 s 為單位)之內,對這個方法的調用都會自動地熔斷(拋出 DegradeException)。
注意:Sentinel 默認統計的 RT 上限是 4900 ms,超出此閾值的都會算作 4900 ms,若需要變更此上限可以通過啟動配置項 -Dcsp.sentinel.statistic.max.rt=xxx 來配置。
(2)異常比例 :
????????DEGRADE_GRADE_EXCEPTION_RATIO,當資源的每秒異常總數占通過量的比值超過閾值(DegradeRule 中的 count)之后,資源進入降級狀態,即在接下的時間窗口(DegradeRule 中的 timeWindow,以 s 為單位)之內,對這個方法的調用都會自動地熔斷。
異常比率的閾值范圍是 [0.0, 1.0],代表 0% - 100%。
(3)異常數 :
????????DEGRADE_GRADE_EXCEPTION_COUNT,當資源近 1 分鐘的異常數目超過閾值之后會進行熔斷。
注意由于統計時間窗口是分鐘級別的,若 timeWindow 小于 60s,則結束熔斷狀態后仍可能再進入熔斷狀態。
3.3、熔斷降級規則的硬編碼方式:
????????在 sentinel-dashboard 控制臺去定義熔斷規則比較簡單,我們就不詳細展開了。除此之外,我們也可以通過調用 DegradeRuleManager.loadRules() 方法來用硬編碼的方式定義流量控制規則,比如:
@PostConstruct public void initSentinelRule() {//熔斷規則: 5s內調用接口出現異常次數超過5的時候, 進行熔斷List<DegradeRule> degradeRules = new ArrayList<>();DegradeRule rule = new DegradeRule();rule.setResource("queryGoodsInfo");rule.setCount(5);//熔斷規則rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);rule.setTimeWindow(5);degradeRules.add(rule);DegradeRuleManager.loadRules(degradeRules); }官方示例鏈接: https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7%E8%A7%84%E5%88%99-degraderule
4、熱點參數規則:
????????熱點就是經常訪問的數據,很多時候我們肯定希望統計某個訪問頻次 Top K 數據并對其進行限流,比如:
- 商品 ID 為參數,統計一段時間內最常購買的商品 ID 并進行限制
- 用戶 ID 為參數,針對一段時間內頻繁訪問的用戶 ID 進行限制
????????熱點參數限流會統計傳入參數中的熱點參數,并根據配置的限流閾值與模式,對包含熱點參數的資源調用進行限流。熱點參數限流可以看做是一種特殊的流量控制,僅對包含熱點參數的資源調用生效。
Sentinel 利用 LRU 策略統計最近最常訪問的熱點參數,結合令牌桶算法來進行參數級別的流控。
熱點參數限流只針對QPS。
4.1、熱點參數規則的核心屬性:
熱點參數規則(ParamFlowRule)類似于流量控制規則(FlowRule):
?4.2、熱點參數規則的硬編碼方式:
(1)定義資源:
@GetMapping("/byHotKey") @SentinelResource(value = "byHotKey",blockHandler = "userAccessError") public String byHotKeyTest(@RequestParam(value = "userId", required = false) String userId,@RequestParam(value = "goodId", required = false) int goodId) {log.info(Thread.currentThread().getName() + "\t" + "...byHotKey");return "-----------by HotKey: UserId"; }可以看到,我們上邊的代碼示例有兩個參數,分別是 userId、goodId
(2)添加熱點參數限流規則:
我們可以通過 ParamFlowRuleManager 的 loadRules 方法配置熱點參數規則,案例的具體限流代碼如下:
ParamFlowRule pRule = new ParamFlowRule("byHotKey").setParamIdx(1).setCount(1); // 針對 goodId 為1000 的參數值,單獨設置限流 QPS 閾值為 5,而不是全局的閾值 1. ParamFlowItem item = new ParamFlowItem().setObject(String.valueOf(1000)).setClassType(int.class.getName()).setCount(5); pRule.setParamFlowItemList(Collections.singletonList(item));ParamFlowRuleManager.loadRules(Collections.singletonList(pRule));官方示例鏈接 :https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81
4.3、熱點參數的控制臺配置方式:
?????????上述配置的具體含義,當 “byHotKey” 這個資源的第1個參數(這里參數索引是從0開始),即 goodId 的 QPS 超過1秒1次將會被限流,需要注意的是,只有包含指定索引的參數請求才會被限流,否則不影響。
????????另外,sentinel 提供了 參數例外項 這個配置,從上圖配置中,我們將 參數 goodId 的參數值等于1000 時,限流閾值設置成了 5,也就是說 goodId = 1000 這個請求 QPS 放寬到每秒 5 次 以上才會被限流。
5、系統保護規則:
????????Sentinel 系統保護的目標是在系統不被拖垮的情況下,提高系統的吞吐率,系統自適應限流從整體維度對應用入口流量進行控制,從單臺機器的 Load、CPU 使用率、總體平均 RT、入口 QPS 和 并發線程數 等幾個維度的監控指標,通過自適應的流控策略,讓系統的入口流量和系統的負載達到一個平衡,讓系統盡可能跑在最大吞吐量的同時保證系統整體的穩定性。
5.1、系統保護規則的核心屬性:
????????系統保護規則包含下面幾個重要的屬性:
?所以,sentinel 的系統保護支持以下幾種模式:
- Load 自適應:(僅對 Linux/Unix-like 機器生效)系統的 load1 作為觸發指標,進行自適應系統保護。當系統 load1 超過設定的觸發值,且系統當前的并發線程數超過估算的系統容量時才會觸發系統保護(BBR 階段)。系統容量由系統的 maxQps * minRt 估算得出。設定參考值一般是 CPU cores * 2.5。
- CPU usage:(1.5.0+ 版本)當系統 CPU 使用率超過閾值即觸發系統保護(取值范圍 0.0-1.0),比較靈敏。
- 平均 RT:當單臺機器上所有入口流量的平均 RT 達到閾值即觸發系統保護,單位是毫秒。
- 并發線程數:當單臺機器上所有入口流量的并發線程數達到閾值即觸發系統保護。
- 入口 QPS:當單臺機器上所有入口流量的 QPS 達到閾值即觸發系統保護。
以上幾種模式在 sentinel-dashboard 控制臺對應的配置如下圖:
注意:系統保護規則是應用整體維度的,而不是資源維度的,并且僅對入口流量生效。入口流量指的是進入應用的流量,比如 Web 服務或 Dubbo 服務端接收的請求,都屬于入口流量。 ?
5.2、系統保護規則的硬編碼方式:
????????除了在 sentinel 控制臺配置系統保護規則,我們也可以通過調用 SystemRuleManager.loadRules() 方法來用硬編碼的方式定義流量控制規則。
private void initSystemRule() {List<SystemRule> rules = new ArrayList<>();SystemRule rule = new SystemRule();rule.setHighestSystemLoad(10);rules.add(rule);SystemRuleManager.loadRules(rules); }官方示例鏈接: https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E7%B3%BB%E7%BB%9F%E4%BF%9D%E6%8A%A4%E8%A7%84%E5%88%99-systemrule
6、來源訪問控制規則:
????????來源訪問控制,就是根據調用來源判斷該次請求是否允許放行,也就是黑白名單控制。若配置白名單則只有請求來源位于白名單內時才可通過;若配置黑名單則請求來源位于黑名單時不通過,其余的請求通過。
6.1、來源訪問控制的核心配置:
來源訪問控制規則(AuthorityRule)非常簡單,主要有以下配置項:
- resource:資源名,即限流規則的作用對象。
- limitApp:對應的黑名單/白名單,不同來源用 “,” 分隔,如 “appA,appB”。
- strategy:限制模式,AUTHORITY_WHITE 為白名單模式,AUTHORITY_BLACK 為黑名單模式,默認為白名單模式。
6.2、來源訪問規則的硬編碼方式:
????????比如我們希望控制對資源 test 的訪問設置白名單,只有來源為 appA 和 appB 的請求才可通過,則可以配置如下白名單規則:
官方示例鏈接:https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8#%E8%AE%BF%E9%97%AE%E6%8E%A7%E5%88%B6%E8%A7%84%E5%88%99-authorityrule
6.3、來源訪問規則的控制臺配置方式:
7、集群流控:
7.1、為什么需要集群流控:
有了單機流控,還需要集群流控的原因如下:
- 微服務要保證高可用,必須是集群,假設有100個集群,那么想要設置流控規則,就要每個微服務都設置一遍,這樣的維護成本太高了
- 單體流控容易造成流量不均勻的問題,出現總流控閾值沒有達到,但某些微服務卻已經被限流了,這個是非常糟糕的問題,因此實際生產中對于集群不推薦單體流控。
????????所以 sentinel 提供了集群流控規則解決上述的問題,思想很簡單,就是提供一個專門的 server 來統計調用的總量,其他的實例都與 server 保持通信。通過集群流控可以精確地控制整個集群的調用總量,結合單機限流兜底,可以更好地發揮流量控制的效果。集群流控中共有兩種身份:
- Token Client:集群流控客戶端,用于向所屬 Token Server 通信請求 token。集群限流服務端會返回給客戶端結果,決定是否限流。
- Token Server:即集群流控服務端,處理來自 Token Client 的請求,根據配置的集群規則判斷是否應該發放 token(是否允許通過)。
sentinel的集群限流有兩種模式,分別如下:
- 獨立模式(Alone):即作為獨立的 token server 進程啟動,獨立部署,隔離性好,但是需要額外的部署操作。獨立模式適合作為 Global Rate Limiter 給集群提供流控服務。
- 嵌入模式(Embedded):即作為內置的 token server 與服務在同一進程中啟動。在此模式下,集群中各個實例都是對等的,token server 和 client 可以隨時進行轉變,因此無需單獨部署,靈活性比較好。但是隔離性不佳,需要限制 token server 的總 QPS,防止影響應用本身。嵌入模式適合某個應用集群內部的流控。
7.2、集群流控的控制臺配置方式:
下面就以嵌入模式為例介紹一下如何配置。就以 sentinel-openfeign-provider 這個模塊作為演示,直接啟動三個集群,端口分別為 9009、9011、9013,如下圖:
此時只需要在控制臺指定一個服務為token server,其他的為token client,集群流控->新增token server,操作如下圖:
?選取一個作為服務端,另外兩個作為客戶端,此時就已經配置好了,如下圖:
此時就可以添加集群流控規則了,可以在sentinel控制臺直接添加,如下圖:
至此集群流控到這兒就介紹完了
四、sentinel 整合 openFeign:
????????上文介紹的都是在單個模塊間的進行 fallback 和 blockhandler 測試,但在實際的 SpringCloud 微服務開發場景中肯定會遇到服務間遠程服務調用的問題,而目前最主流的遠程調用組件就是 openFeign 了,那接下來我們看看 sentinel 如何整合 openFeign 進行熔斷降級。
1、引入 openFeign 的依賴:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>?2、配置文件中開啟 epenFeign 對 sentinel 熔斷降級的支持:
feign.sentinel.enabled=true3、消費者主啟動類添加注解 @EnableFeignClients 激活 openFeign
4、消費者聲明接口,標識要調用提供方模塊的哪個方法,并在 @FeignClient 注解中使用 fallback 屬性指定熔斷降級類:
@FeignClient(value = "payment-provider", fallback = PaymentServiceImpl.class) public interface PaymentService {@GetMapping("/payment/get/{id}")public CommonResult paymentSql(@PathVariable("id")Long id); }5、添加降級類:實現第4步的定義的接口,并添加降級邏輯:
@Component public class PaymentServiceFallback implements PaymentService {@Overridepublic CommonResult paymentSql(Long id) {return new CommonResult(414, "open-feign 整合 sentinel 實現的全局服務降級策略",null);} }PaymentServiceFallback 是 PaymentService 的降級回調類,一旦PaymentService 中對應得接口出現了異常,則會調用這個類中對應得方法進行降級處理。
五、Sentinel 所存在的問題:
????????通過 Sentinel-Dashboard 控制臺配置的規則依然要推送到微服務應用中 Sentinel 客戶端本身才能生效,而微服務之間的調用鏈路等指標信息也需要推送給Sentinel控制臺,才能比較方便地使用Sentinel提供的一些能力,因此在開源的架構版本中需要微服務應用本身開啟獨立端口與 sentinel-dashboard 進行通信,從而獲取配置規則以及上送微服務應用的各類指標信息。而這顯然也會占用微服務額外的資源,并且由于 sentinel-dashboard 在此條件下并不具備集群部署能力,因此也會形成一個單節點問題,但是有一套控制臺總好過于沒有,如果希望比較方便快速地應用 Sentinel 這也是一種代價。此時的 Sentinel 架構如下圖所示:
????????在開源版本架構中,通過 sentinel-dashboard 控制臺配置的限流、熔斷降級等規則都是存儲于 Sentinel-Dashboard 控制臺服務的內存之中,如果控制臺服務重啟或者微服務應用重啟都會導致規則丟失,而這在生產環境下顯然是不能接受的,因此 Sentinel 官方推薦在生產架構中使用第三方數據源作為永久存儲中心,比如 nacos、apollo、zookeeper,這樣各個微服務的限流、降級規則都可以永久存儲。那么下篇文章我們就介紹下,Sentinel 如何集成 apollo 進行規則持久化?以及 如何對 sentinel-dashboard 進行改造,實現 Sentinel-Dashboard 與 apollo 規則的相互同步)
參考文章:
(官方文檔)https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5
https://mp.weixin.qq.com/s/Q7Xv8cypQFrrOQhbd9BOXw
https://www.cnblogs.com/crazymakercircle/p/14285001.html
總結
以上是生活随笔為你收集整理的sentinel 限流熔断神器详细介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Sentinel 规则持久化到 apol
- 下一篇: ElasticSearch搜索引擎: 内