聊聊Sentinel 快速入門(mén)
本文轉(zhuǎn)載自微信公眾號(hào)「運(yùn)維開(kāi)發(fā)故事」,作者老鄭。轉(zhuǎn)載本文請(qǐng)聯(lián)系運(yùn)維開(kāi)發(fā)故事公眾號(hào)。
Sentinel 簡(jiǎn)介
隨著微服務(wù)的流行,服務(wù)和服務(wù)之間的穩(wěn)定性變得越來(lái)越重要。Sentinel 以流量為切入點(diǎn),從流量控制、熔斷降級(jí)、系統(tǒng)負(fù)載保護(hù)等多個(gè)維度保護(hù)服務(wù)的穩(wěn)定性。
Sentinel 具有以下特征:
- 豐富的應(yīng)用場(chǎng)景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場(chǎng)景,例如秒殺(即突發(fā)流量控制在系統(tǒng)容量可以承受的范圍)、消息削峰填谷、集群流量控制、實(shí)時(shí)熔斷下游不可用應(yīng)用等。
- 完備的實(shí)時(shí)監(jiān)控:Sentinel 同時(shí)提供實(shí)時(shí)的監(jiān)控功能。您可以在控制臺(tái)中看到接入應(yīng)用的單臺(tái)機(jī)器秒級(jí)數(shù)據(jù),甚至 500 臺(tái)以下規(guī)模的集群的匯總運(yùn)行情況。
- 廣泛的開(kāi)源生態(tài):Sentinel 提供開(kāi)箱即用的與其它開(kāi)源框架/庫(kù)的整合模塊,例如與 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相應(yīng)的依賴(lài)并進(jìn)行簡(jiǎn)單的配置即可快速地接入 Sentinel。
- 完善的 SPI 擴(kuò)展點(diǎn):Sentinel 提供簡(jiǎn)單易用、完善的 SPI 擴(kuò)展接口。您可以通過(guò)實(shí)現(xiàn)擴(kuò)展接口來(lái)快速地定制邏輯。例如定制規(guī)則管理、適配動(dòng)態(tài)數(shù)據(jù)源等。
Sentinel 的主要特性:
Sentinel 的開(kāi)源生態(tài):
Sentinel 分為兩個(gè)部分:
核心庫(kù)(Java 客戶(hù)端)不依賴(lài)任何框架/庫(kù),能夠運(yùn)行于所有 Java 運(yùn)行時(shí)環(huán)境,同時(shí)對(duì) Dubbo / Spring Cloud 等框架也有較好的支持。
控制臺(tái)(Dashboard)基于 Spring Boot 開(kāi)發(fā),打包后可以直接運(yùn)行,不需要額外的 Tomcat 等應(yīng)用容器。
Sentinel、Hystrix、resilience4j 對(duì)比
Sentinel |
Hystrix |
resilience4j |
|
隔離策略 |
信號(hào)量隔離(并發(fā)控制) |
線(xiàn)程池隔離/信號(hào)量隔離 |
信號(hào)量隔離 |
熔斷降級(jí)策略 |
基于慢調(diào)用比例、異常比例、異常數(shù) |
基于異常比例 |
基于異常比例、響應(yīng)時(shí)間 |
實(shí)時(shí)統(tǒng)計(jì)實(shí)現(xiàn) |
滑動(dòng)窗口(LeapArray) |
滑動(dòng)窗口(基于 RxJava) |
Ring Bit Buffer |
動(dòng)態(tài)規(guī)則配置 |
支持多種數(shù)據(jù)源 |
支持多種數(shù)據(jù)源 |
有限支持 |
擴(kuò)展性 |
多個(gè)擴(kuò)展點(diǎn) |
插件的形式 |
接口的形式 |
基于注解的支持 |
支持 |
支持 |
支持 |
限流 |
基于 QPS,支持基于調(diào)用關(guān)系的限流 |
有限的支持 |
Rate Limiter |
流量整形 |
支持預(yù)熱模式與勻速排隊(duì)控制效果 |
不支持 |
簡(jiǎn)單的 Rate Limiter 模式 |
系統(tǒng)自適應(yīng)保護(hù) |
支持 |
不支持 |
不支持 |
多語(yǔ)言支持 |
Java/Go/C++ |
Java |
Java |
Service Mesh 支持 |
支持 Envoy/Istio |
不支持 |
不支持 |
控制臺(tái) |
提供開(kāi)箱即用的控制臺(tái),可配置規(guī)則、實(shí)時(shí)監(jiān)控、機(jī)器發(fā)現(xiàn)等 |
簡(jiǎn)單的監(jiān)控查看 |
不提供控制臺(tái),可對(duì)接其它監(jiān)控系統(tǒng) |
Sentinel 名詞
資源
資源是 Sentinel 的關(guān)鍵概念。它可以是 Java 應(yīng)用程序中的任何內(nèi)容,例如,由應(yīng)用程序提供的服務(wù),或由應(yīng)用程序調(diào)用的其它應(yīng)用提供的服務(wù),甚至可以是一段代碼。在接下來(lái)的文檔中,我們都會(huì)用資源來(lái)描述代碼塊。
只要通過(guò) Sentinel API 定義的代碼,就是資源,能夠被 Sentinel 保護(hù)起來(lái)。大部分情況下,可以使用方法簽名,URL,甚至服務(wù)名稱(chēng)作為資源名來(lái)標(biāo)示資源。
規(guī)則
圍繞資源的實(shí)時(shí)狀態(tài)設(shè)定的規(guī)則,可以包括流量控制規(guī)則、熔斷降級(jí)規(guī)則以及系統(tǒng)保護(hù)規(guī)則。所有規(guī)則可以動(dòng)態(tài)實(shí)時(shí)調(diào)整。
流量控制
什么是流量控制
流量控制在網(wǎng)絡(luò)傳輸中是一個(gè)常用的概念,它用于調(diào)整網(wǎng)絡(luò)包的發(fā)送數(shù)據(jù)。然而,從系統(tǒng)穩(wěn)定性角度考慮,在處理請(qǐng)求的速度上,也有非常多的講究。任意時(shí)間到來(lái)的請(qǐng)求往往是隨機(jī)不可控的,而系統(tǒng)的處理能力是有限的。我們需要根據(jù)系統(tǒng)的處理能力對(duì)流量進(jìn)行控制。Sentinel 作為一個(gè)調(diào)配器,可以根據(jù)需要把隨機(jī)的請(qǐng)求調(diào)整成合適的形狀,如下圖所示:
流量控制設(shè)計(jì)理念
流量控制有以下幾個(gè)角度:
- 資源的調(diào)用關(guān)系,例如資源的調(diào)用鏈路,資源和資源之間的關(guān)系;
- 運(yùn)行指標(biāo),例如 QPS、線(xiàn)程池、系統(tǒng)負(fù)載等;
- 控制的效果,例如直接限流、冷啟動(dòng)、排隊(duì)等。
Sentinel 的設(shè)計(jì)理念是讓您自由選擇控制的角度,并進(jìn)行靈活組合,從而達(dá)到想要的效果。
熔斷降級(jí)
什么是熔斷降級(jí)
除了流量控制以外,及時(shí)對(duì)調(diào)用鏈路中的不穩(wěn)定因素進(jìn)行熔斷也是 Sentinel 的使命之一。由于調(diào)用關(guān)系的復(fù)雜性,如果調(diào)用鏈路中的某個(gè)資源出現(xiàn)了不穩(wěn)定,可能會(huì)導(dǎo)致請(qǐng)求發(fā)生堆積,進(jìn)而導(dǎo)致級(jí)聯(lián)錯(cuò)誤。
Sentinel 和 Hystrix 的原則是一致的: 當(dāng)檢測(cè)到調(diào)用鏈路中某個(gè)資源出現(xiàn)不穩(wěn)定的表現(xiàn),例如請(qǐng)求響應(yīng)時(shí)間長(zhǎng)或異常比例升高的時(shí)候,則對(duì)這個(gè)資源的調(diào)用進(jìn)行限制,讓請(qǐng)求快速失敗,避免影響到其它的資源而導(dǎo)致級(jí)聯(lián)故障。
熔斷降級(jí)設(shè)計(jì)理念
在限制的手段上,Sentinel 和 Hystrix 采取了完全不一樣的方法。
Hystrix 通過(guò) 線(xiàn)程池隔離 的方式,來(lái)對(duì)依賴(lài)(在 Sentinel 的概念中對(duì)應(yīng) 資源)進(jìn)行了隔離。這樣做的好處是資源和資源之間做到了最徹底的隔離。缺點(diǎn)是除了增加了線(xiàn)程切換的成本(過(guò)多的線(xiàn)程池導(dǎo)致線(xiàn)程數(shù)目過(guò)多),還需要預(yù)先給各個(gè)資源做線(xiàn)程池大小的分配。
Sentinel 對(duì)這個(gè)問(wèn)題采取了兩種手段:
- 通過(guò)并發(fā)線(xiàn)程數(shù)進(jìn)行限制
和資源池隔離的方法不同,Sentinel 通過(guò)限制資源并發(fā)線(xiàn)程的數(shù)量,來(lái)減少不穩(wěn)定資源對(duì)其它資源的影響。這樣不但沒(méi)有線(xiàn)程切換的損耗,也不需要您預(yù)先分配線(xiàn)程池的大小。當(dāng)某個(gè)資源出現(xiàn)不穩(wěn)定的情況下,例如響應(yīng)時(shí)間變長(zhǎng),對(duì)資源的直接影響就是會(huì)造成線(xiàn)程數(shù)的逐步堆積。當(dāng)線(xiàn)程數(shù)在特定資源上堆積到一定的數(shù)量之后,對(duì)該資源的新請(qǐng)求就會(huì)被拒絕。堆積的線(xiàn)程完成任務(wù)后才開(kāi)始繼續(xù)接收請(qǐng)求。
- 通過(guò)響應(yīng)時(shí)間對(duì)資源進(jìn)行降級(jí)
除了對(duì)并發(fā)線(xiàn)程數(shù)進(jìn)行控制以外,Sentinel 還可以通過(guò)響應(yīng)時(shí)間來(lái)快速降級(jí)不穩(wěn)定的資源。當(dāng)依賴(lài)的資源出現(xiàn)響應(yīng)時(shí)間過(guò)長(zhǎng)后,所有對(duì)該資源的訪(fǎng)問(wèn)都會(huì)被直接拒絕,直到過(guò)了指定的時(shí)間窗口之后才重新恢復(fù)。
系統(tǒng)自適應(yīng)保護(hù)
Sentinel 同時(shí)提供系統(tǒng)維度的自適應(yīng)保護(hù)能力。防止雪崩,是系統(tǒng)防護(hù)中重要的一環(huán)。當(dāng)系統(tǒng)負(fù)載較高的時(shí)候,如果還持續(xù)讓請(qǐng)求進(jìn)入,可能會(huì)導(dǎo)致系統(tǒng)崩潰,無(wú)法響應(yīng)。在集群環(huán)境下,網(wǎng)絡(luò)負(fù)載均衡會(huì)把本應(yīng)這臺(tái)機(jī)器承載的流量轉(zhuǎn)發(fā)到其它的機(jī)器上去。如果這個(gè)時(shí)候其它的機(jī)器也處在一個(gè)邊緣狀態(tài)的時(shí)候,這個(gè)增加的流量就會(huì)導(dǎo)致這臺(tái)機(jī)器也崩潰,最后導(dǎo)致整個(gè)集群不可用。
針對(duì)這個(gè)情況,Sentinel 提供了對(duì)應(yīng)的保護(hù)機(jī)制,讓系統(tǒng)的入口流量和系統(tǒng)的負(fù)載達(dá)到一個(gè)平衡,保證系統(tǒng)在能力范圍之內(nèi)處理最多的請(qǐng)求。
Sentinel 原理
Sentinel 的主要工作機(jī)制如下:
- 對(duì)主流框架提供適配或者顯示的 API,來(lái)定義需要保護(hù)的資源,并提供設(shè)施對(duì)資源進(jìn)行實(shí)時(shí)統(tǒng)計(jì)和調(diào)用鏈路分析。
- 根據(jù)預(yù)設(shè)的規(guī)則,結(jié)合對(duì)資源的實(shí)時(shí)統(tǒng)計(jì)信息,對(duì)流量進(jìn)行控制。同時(shí),Sentinel 提供開(kāi)放的接口,方便您定義及改變規(guī)則。
- Sentinel 提供實(shí)時(shí)的監(jiān)控系統(tǒng),方便您快速了解目前系統(tǒng)的狀態(tài)。
Sentinel 使用
普通使用
如果應(yīng)用使用 pom 工程,則在 pom.xml 文件中加入以下代碼即可:
- <dependency>
- <groupId>com.alibaba.csp</groupId>
- <artifactId>sentinel-core</artifactId>
- <version>1.8.1</version>
- </dependency>
接下來(lái),我們把需要控制流量的代碼用 Sentinel API SphU.entry("HelloWorld") 和 entry.exit() 包圍起來(lái)即可。在下面的例子中,我們將 System.out.println("hello world"); 這端代碼作為資源,用 API 包圍起來(lái)(埋點(diǎn))。參考代碼如下:
- while (true) {
- Entry entry = null;
- try {
- entry = SphU.entry("HelloWorld");
- /*您的業(yè)務(wù)邏輯 - 開(kāi)始*/
- System.out.println("hello world");
- TimeUnit.MILLISECONDS.sleep(10);
- /*您的業(yè)務(wù)邏輯 - 結(jié)束*/
- } catch (BlockException e1) {
- /*流控邏輯處理 - 開(kāi)始*/
- System.out.println("block!");
- /*流控邏輯處理 - 結(jié)束*/
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- if (entry != null) {
- entry.exit();
- }
- }
- }
接下來(lái),通過(guò)規(guī)則來(lái)指定允許該資源通過(guò)的請(qǐng)求次數(shù),例如下面的代碼定義了資源 HelloWorld 每秒最多只能通過(guò) 20 個(gè)請(qǐng)求。
- // 規(guī)則配置
- private static void initFlowRules() {
- List<FlowRule> rules = new ArrayList<>();
- FlowRule rule = new FlowRule();
- rule.setResource("HelloWorld");
- rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
- // Set limit QPS to 20.
- rule.setCount(20);
- rules.add(rule);
- FlowRuleManager.loadRules(rules);
- }
Demo 運(yùn)行之后,我們可以在日志 ~/logs/csp/${appName}-metrics.log.xxx 里看到下面的輸出:
- |--timestamp-|------date time----|-resource-|p |block|s |e|rt
- 1619954886000|2021-05-02 19:28:06|HelloWorld|20|1|20|0|12|0|0|0
- 1619954887000|2021-05-02 19:28:07|HelloWorld|20|3197|20|0|11|0|0|0
- 1619954888000|2021-05-02 19:28:08|HelloWorld|20|2857|20|0|11|0|0|0
其中 p 代表通過(guò)的請(qǐng)求, block 代表被阻止的請(qǐng)求, s 代表成功執(zhí)行完成的請(qǐng)求個(gè)數(shù), e 代表用戶(hù)自定義的異常, rt 代表平均響應(yīng)時(shí)長(zhǎng)。
可以看到,這個(gè)程序每秒穩(wěn)定輸出 "hello world" 20 次,和規(guī)則中預(yù)先設(shè)定的閾值是一樣的。
注解方式
Sentinel 提供了 @SentinelResource 注解用于定義資源,并提供了 AspectJ 的擴(kuò)展用于自動(dòng)定義資源、處理 BlockException 等。使用 Sentinel Annotation AspectJ Extension 的時(shí)候需要引入以下依賴(lài):
- <dependency>
- <groupId>com.alibaba.csp</groupId>
- <artifactId>sentinel-annotation-aspectj</artifactId>
- <version>x.y.z</version>
- </dependency>
示例
- // 對(duì)應(yīng)的 `handleException` 函數(shù)需要位于 `ExceptionUtil` 類(lèi)中,并且必須為 public static 函數(shù).
- // 對(duì)應(yīng)的返回值也需要和當(dāng)前方法一樣
- @SentinelResource(value = "createOrder",
- blockHandler = "blockHandler",
- blockHandlerClass = {ExceptionUtils.class})
- @GetMapping("/createOrder")
- public OrderDto createOrder(OrderDto dto) {
- return new OrderDto();
- }
- // ExceptionUtils
- public class ExceptionUtils {
- public static OrderDto blockHandler(OrderDto dto, BlockException ex) {
- ex.printStackTrace();
- return null;
- }
- }
@SentinelResource 注解
注意:注解方式埋點(diǎn)不支持 private 方法。
@SentinelResource 用于定義資源,并提供可選的異常處理和 fallback 配置項(xiàng)。@SentinelResource 注解包含以下屬性:
- value:資源名稱(chēng),必需項(xiàng)(不能為空)
- entryType:entry 類(lèi)型,可選項(xiàng)(默認(rèn)為 EntryType.OUT)
- blockHandler / blockHandlerClass: blockHandler 對(duì)應(yīng)處理 BlockException 的函數(shù)名稱(chēng),可選項(xiàng)。blockHandler 函數(shù)訪(fǎng)問(wèn)范圍需要是 public,返回類(lèi)型需要與原方法相匹配,參數(shù)類(lèi)型需要和原方法相匹配并且最后加一個(gè)額外的參數(shù),類(lèi)型為 BlockException。blockHandler 函數(shù)默認(rèn)需要和原方法在同一個(gè)類(lèi)中。若希望使用其他類(lèi)的函數(shù),則可以指定 blockHandlerClass 為對(duì)應(yīng)的類(lèi)的 Class 對(duì)象,注意對(duì)應(yīng)的函數(shù)必需為 static 函數(shù),否則無(wú)法解析。
- fallback / fallbackClass:fallback 函數(shù)名稱(chēng),可選項(xiàng),用于在拋出異常的時(shí)候提供 fallback 處理邏輯。fallback 函數(shù)可以針對(duì)所有類(lèi)型的異常(除了 exceptionsToIgnore 里面排除掉的異常類(lèi)型)進(jìn)行處理。fallback 函數(shù)簽名和位置要求:
- 返回值類(lèi)型必須與原函數(shù)返回值類(lèi)型一致;
- 方法參數(shù)列表需要和原函數(shù)一致,或者可以額外多一個(gè) Throwable 類(lèi)型的參數(shù)用于接收對(duì)應(yīng)的異常。
- fallback 函數(shù)默認(rèn)需要和原方法在同一個(gè)類(lèi)中。若希望使用其他類(lèi)的函數(shù),則可以指定 fallbackClass 為對(duì)應(yīng)的類(lèi)的 Class 對(duì)象,注意對(duì)應(yīng)的函數(shù)必需為 static 函數(shù),否則無(wú)法解析。
- defaultFallback(since 1.6.0):默認(rèn)的 fallback 函數(shù)名稱(chēng),可選項(xiàng),通常用于通用的 fallback 邏輯(即可以用于很多服務(wù)或方法)。默認(rèn) fallback 函數(shù)可以針對(duì)所有類(lèi)型的異常(除了 exceptionsToIgnore 里面排除掉的異常類(lèi)型)進(jìn)行處理。若同時(shí)配置了 fallback 和 defaultFallback,則只有 fallback 會(huì)生效。defaultFallback 函數(shù)簽名要求:
- 返回值類(lèi)型必須與原函數(shù)返回值類(lèi)型一致;
- 方法參數(shù)列表需要為空,或者可以額外多一個(gè) Throwable 類(lèi)型的參數(shù)用于接收對(duì)應(yīng)的異常。
- defaultFallback 函數(shù)默認(rèn)需要和原方法在同一個(gè)類(lèi)中。若希望使用其他類(lèi)的函數(shù),則可以指定 fallbackClass 為對(duì)應(yīng)的類(lèi)的 Class 對(duì)象,注意對(duì)應(yīng)的函數(shù)必需為 static 函數(shù),否則無(wú)法解析。
- exceptionsToIgnore(since 1.6.0):用于指定哪些異常被排除掉,不會(huì)計(jì)入異常統(tǒng)計(jì)中,也不會(huì)進(jìn)入 fallback 邏輯中,而是會(huì)原樣拋出。
1.8.0 版本開(kāi)始,defaultFallback 支持在類(lèi)級(jí)別進(jìn)行配置。
注:1.6.0 之前的版本 fallback 函數(shù)只針對(duì)降級(jí)異常(DegradeException)進(jìn)行處理,不能針對(duì)業(yè)務(wù)異常進(jìn)行處理。
特別地,若 blockHandler 和 fallback 都進(jìn)行了配置,則被限流降級(jí)而拋出 BlockException 時(shí)只會(huì)進(jìn)入 blockHandler 處理邏輯。若未配置 blockHandler、fallback 和 defaultFallback,則被限流降級(jí)時(shí)會(huì)將 BlockException 直接拋出(若方法本身未定義 throws BlockException 則會(huì)被 JVM 包裝一層 UndeclaredThrowableException)。
Sentinel 控制臺(tái)
下載控制臺(tái)程序地址:
- https://github.com/alibaba/Sentinel/releases/tag/1.8.1
啟動(dòng)命令
- java -Dserver.port=8089
- -Dcsp.sentinel.dashboard.server=127.0.0.1:8089
- -Dproject.name=sentinel-dashboard
- -jar sentinel-dashboard-1.8.1.jar
登錄賬號(hào),默認(rèn)的登錄帳號(hào)和密碼都是:sentinel
登錄控制臺(tái)后我們可以通過(guò)右側(cè)菜單對(duì)我們的服務(wù)進(jìn)行配置
參考
https://github.com/alibaba/Sentinel/wiki/介紹
https://github.com/Netflix/Hystrix/wiki/How-it-Works#benefits-of-thread-pools