Sentinel :微服務哨兵


1. Sentinel 是什么?

隨着微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。

Sentinel 具有以下特征:

  • 豐富的應用場景:Sentinel 承接了阿里巴巴近10年的雙十一大促流量的核心場景,例如秒殺、消息削峰填谷、集群流量控制、實時熔斷下游不可用應用等。
  • 完備的實時監控:Sentinel 同時提供實時的監控功能。你可以在控制台中看到接入應用的單台機器秒級數據,甚至 500 台以下規模的集群的匯總運行情況。
  • 廣泛的開源生態:Sentinel 提供開箱即用的與其它開源框架/庫的整合模塊,例如與 Spring Cloud、Dubbo、gRPC 的整合。只需要引入相應的依賴並進行簡單的配置即可快速地接入 Sentinel。
  • 完善的SPI擴展點:Sentinel 提供簡單易用、完善的 SPI 擴展接口。您可以通過實現擴展接口來快速地定制邏輯。例如定制規則管理、適配動態數據源等。

Sentinel 的主要特性:

Sentinel 的開源生態:

Sentinel 分為兩個部分:

  • 核心庫(Java 客戶端)不依賴任何框架/庫,能夠運行於所有 Java 運行時環境,同時對 Dubbo / Spring Cloud 等框架也有較好的支持。
  • 控制台(Dashboard)基於 Spring Boot 開發,打包后可以直接運行,不需要額外的 Tomcat 等應用容器。

2. Sentinel 快速開始

首先,引入 Sentinel 依賴

 <dependency>
     <groupId>com.alibaba.csp</groupId>
     <artifactId>sentinel-core</artifactId>
     <version>1.7.1</version>
 </dependency> 

接着,定義資源

資源 是 Sentinel 中的核心概念之一。最常用的資源是我們代碼中的 Java 方法。 當然,您也可以更靈活的定義你的資源,例如,把需要控制流量的代碼用 Sentinel API SphU.entry("HelloWorld") 和 entry.exit() 包圍起來即可。在下面的例子中,我們將 System.out.println("hello world"); 作為資源(被保護的邏輯),用 API 包裝起來。例如:

 try (Entry entry = SphU.entry("HelloWorld")) {
     // Your business logic here.
     System.out.println("hello world");
 } catch (BlockException e) {
     // Handle rejected request.
     e.printStackTrace();
 }
 // try-with-resources auto exit 

還可以使用注解定義資源  https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81 

例如:

 @SentinelResource("HelloWorld")
 public void helloWorld() {
     // 資源中的邏輯
     System.out.println("hello world");
 } 

最后,定義規則

接下來,通過流控規則來指定允許該資源通過的請求次數,例如下面的代碼定義了資源 HelloWorld 每秒最多只能通過 20 個請求。

 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);
 } 

完成!

完整的代碼如下:

pom.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     <modelVersion>4.0.0</modelVersion>
 5     <parent>
 6         <groupId>org.springframework.boot</groupId>
 7         <artifactId>spring-boot-starter-parent</artifactId>
 8         <version>2.2.2.RELEASE</version>
 9         <relativePath/> <!-- lookup parent from repository -->
10     </parent>
11     <groupId>com.cjs.example</groupId>
12     <artifactId>sentinel-example</artifactId>
13     <version>0.0.1-SNAPSHOT</version>
14     <name>sentinel-example</name>
15 
16     <properties>
17         <java.version>1.8</java.version>
18         <spring-cloud.version>Greenwich.SR4</spring-cloud.version>
19         <spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version>
20     </properties>
21 
22     <dependencies>
23         <dependency>
24             <groupId>org.springframework.boot</groupId>
25             <artifactId>spring-boot-starter-actuator</artifactId>
26         </dependency>
27         <dependency>
28             <groupId>org.springframework.boot</groupId>
29             <artifactId>spring-boot-starter-web</artifactId>
30         </dependency>
31         <dependency>
32             <groupId>com.alibaba.cloud</groupId>
33             <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
34         </dependency>
35 
36     </dependencies>
37 
38     <dependencyManagement>
39         <dependencies>
40             <dependency>
41                 <groupId>org.springframework.cloud</groupId>
42                 <artifactId>spring-cloud-dependencies</artifactId>
43                 <version>${spring-cloud.version}</version>
44                 <type>pom</type>
45                 <scope>import</scope>
46             </dependency>
47 
48             <dependency>
49                 <groupId>com.alibaba.cloud</groupId>
50                 <artifactId>spring-cloud-alibaba-dependencies</artifactId>
51                 <version>${spring-cloud-alibaba.version}</version>
52                 <type>pom</type>
53                 <scope>import</scope>
54             </dependency>
55         </dependencies>
56     </dependencyManagement>
57 
58     <build>
59         <plugins>
60             <plugin>
61                 <groupId>org.springframework.boot</groupId>
62                 <artifactId>spring-boot-maven-plugin</artifactId>
63             </plugin>
64         </plugins>
65     </build>
66 
67 </project>

application.properties

 server.port=8084
 spring.application.name=sentinel-example
 spring.cloud.sentinel.transport.dashboard=127.0.0.1:8080

SentinelExampleApplication.java

 1 package com.cjs.example.sentinel;
 2 
 3 import com.alibaba.csp.sentinel.Entry;
 4 import com.alibaba.csp.sentinel.SphU;
 5 import com.alibaba.csp.sentinel.slots.block.BlockException;
 6 import com.alibaba.csp.sentinel.slots.block.RuleConstant;
 7 import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
 8 import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
 9 import org.springframework.boot.SpringApplication;
10 import org.springframework.boot.autoconfigure.SpringBootApplication;
11 
12 import java.util.ArrayList;
13 import java.util.List;
14 
15 @SpringBootApplication
16 public class SentinelExampleApplication {
17 
18     public static void main(String[] args) {
19         SpringApplication.run(SentinelExampleApplication.class, args);
20 
21 
22         // 配置規則.
23         initFlowRules();
24 
25         while (true) {
26             // 1.5.0 版本開始可以直接利用 try-with-resources 特性,自動 exit entry
27             try (Entry entry = SphU.entry("HelloWorld")) {
28                 // 被保護的邏輯
29                 System.out.println("hello world");
30             } catch (BlockException ex) {
31                 // 處理被流控的邏輯
32                 System.out.println("blocked!");
33             }
34         }
35     }
36 
37 
38     private static void initFlowRules() {
39         List<FlowRule> rules = new ArrayList<>();
40 
41         FlowRule rule = new FlowRule();
42         rule.setResource("HelloWorld");
43         rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
44         // Set limit QPS to 20.
45         rule.setCount(20);
46         rules.add(rule);
47 
48         FlowRuleManager.loadRules(rules);
49 
50     }
51 }

TestController.java

 1 package com.cjs.example.sentinel;
 2 
 3 import com.alibaba.csp.sentinel.annotation.SentinelResource;
 4 import org.springframework.web.bind.annotation.GetMapping;
 5 import org.springframework.web.bind.annotation.RestController;
 6 
 7 @RestController
 8 public class TestController {
 9 
10     @GetMapping("/hello")
11     @SentinelResource("hello")
12     public String hello() {
13         return "hello";
14     }
15 
16 }

3. Sentinel 控制台

Sentinel 控制台最少應該包含如下功能:

  • 查看機器列表以及健康情況:收集 Sentinel 客戶端發送的心跳包,用於判斷機器是否在線。
  • 監控 (單機和集群聚合):通過 Sentinel 客戶端暴露的監控 API,定期拉取並且聚合應用監控信息,最終可以實現秒級的實時監控。
  • 規則管理和推送:統一管理推送規則。
  • 鑒權:生產環境中鑒權非常重要。

https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0

獲取控制台:

方式一:下載已經打好的包

https://github.com/alibaba/Sentinel/releases 

wget https://github.com/alibaba/Sentinel/releases/download/1.7.1/sentinel-dashboard-1.7.1.jar

方式二:通過源碼構件 

mvn clean package 

啟動控制台

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.1.jar

默認用戶名密碼都是sentinel 

4. Sentinel 注解支持

@SentinelResource 用於定義資源,並提供可選的異常處理和 fallback 配置項

@SentinelResource 注解的屬性:

  • value :資源名稱
  • entryType :the entry type (inbound or outbound)
  • blockHandler/blockHandlerClass : blockHandler 是對應處理 BlockException 的函數名稱。blockHandler 函數訪問范圍需要是 public,返回類型需要與原方法相匹配,參數類型需要和原方法相匹配並且最后加一個額外的參數,類型為 BlockException。blockHandler 函數默認需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定blockHandlerClass。blockHandlerClass 為對應函數所在類的 Class 對象,注意對應的函數必需為 static 函數,否則無法解析。
  • fallback :fallback 函數名稱,可選項,用於在拋出異常的時候提供 fallback 處理邏輯。fallback 函數可以針對所有類型的異常(除了 exceptionsToIgnore 里面排除掉的異常類型)進行處理。fallback 函數簽名和位置要求:defaultFallback :默認的 fallback 函數名稱,可選項,通常用於通用的 fallback 邏輯
    • 返回值類型必須與原函數返回值類型一致;
    • 方法參數列表需要和原函數一致,或者可以額外多一個 Throwable 類型的參數用於接收對應的異常;
    • fallback 函數默認需要和原方法在同一個類中。若希望使用其他類的函數,則可以指定 fallbackClass 為對應的類的 Class 對象,注意對應的函數必需為 static 函數,否則無法解析; 
  • exceptionsToIgnore :用於指定哪些異常被排除掉,不會計入異常統計中,也不會進入 fallback 邏輯中,而是會原樣拋出 

需要注意的是,若 blockHandler 和 fallback 都進行了配置,則被限流降級而拋出 BlockException 時只會進入 blockHandler 處理邏輯。若未配置 blockHandler、fallback 和 defaultFallback,則被限流降級時會將 BlockException 直接拋出(若方法本身未定義 throws BlockException 則會被 JVM 包裝一層 UndeclaredThrowableException)。 

示例:

 1 public class TestService {
 2 
 3     // 對應的 `handleException` 函數需要位於 `ExceptionUtil` 類中,並且必須為 static 函數.
 4     @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
 5     public void test() {
 6         System.out.println("Test");
 7     }
 8 
 9     // 原函數
10     @SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
11     public String hello(long s) {
12         return String.format("Hello at %d", s);
13     }
14     
15     // Fallback 函數,函數簽名與原函數一致或加一個 Throwable 類型的參數.
16     public String helloFallback(long s) {
17         return String.format("Halooooo %d", s);
18     }
19 
20     // Block 異常處理函數,參數最后多一個 BlockException,其余與原函數一致.
21     public String exceptionHandler(long s, BlockException ex) {
22         // Do some log here.
23         ex.printStackTrace();
24         return "Oops, error occurred at " + s;
25     }
26 } 

可以看到,blockHandler和fallback必須與原方法在同一個類中。如果不想寫在同一個類中,可以利用blockHandlerClass來指定類,然后通過blockHandler指定方法名。

如果同時配置了blockHandler和fallback,則BlockException只會進到blockHandler處理邏輯中。

5. Sentinel 基本概念

資源

只要通過 Sentinel API 定義的代碼,就是資源,能夠被 Sentinel 保護起來。大部分情況下,可以使用方法簽名,URL,甚至服務名稱作為資源名來標示資源。 

規則

圍繞資源的實時狀態設定的規則,可以包括流量控制規則、熔斷降級規則以及系統保護規則。所有規則可以動態實時調整。 

流量控制

流量控制在網絡傳輸中是一個常用的概念,它用於調整網絡包的發送數據。然而,從系統穩定性角度考慮,在處理請求的速度上,也有非常多的講究。任意時間到來的請求往往是隨機不可控的,而系統的處理能力是有限的。我們需要根據系統的處理能力對流量進行控制。Sentinel 作為一個調配器,可以根據需要把隨機的請求調整成合適的形狀,如下圖所示: 

流量控制有以下幾個角度:

  • 資源的調用關系,例如資源的調用鏈路,資源和資源之間的關系;
  • 運行指標,例如 QPS、線程池、系統負載等;
  • 控制的效果,例如直接限流、冷啟動、排隊等; 

Sentinel 的設計理念是讓您自由選擇控制的角度,並進行靈活組合,從而達到想要的效果。

熔斷降級 

Sentinel 和 Hystrix 的原則是一致的: 當檢測到調用鏈路中某個資源出現不穩定的表現,例如請求響應時間長或異常比例升高的時候,則對這個資源的調用進行限制,讓請求快速失敗,避免影響到其它的資源而導致級聯故障。

在限制的手段上,Sentinel 和 Hystrix 采取了完全不一樣的方法。

Hystrix 通過 線程池隔離 的方式,來對依賴(在 Sentinel 的概念中對應 資源)進行了隔離。這樣做的好處是資源和資源之間做到了最徹底的隔離。缺點是除了增加了線程切換的成本(過多的線程池導致線程數目過多),還需要預先給各個資源做線程池大小的分配。 如下圖:

Sentinel 對這個問題采取了兩種手段:

  • 通過並發線程數進行限制

和資源池隔離的方法不同,Sentinel 通過限制資源並發線程的數量,來減少不穩定資源對其它資源的影響。這樣不但沒有線程切換的損耗,也不需要您預先分配線程池的大小。當某個資源出現不穩定的情況下,例如響應時間變長,對資源的直接影響就是會造成線程數的逐步堆積。當線程數在特定資源上堆積到一定的數量之后,對該資源的新請求就會被拒絕。堆積的線程完成任務后才開始繼續接收請求。

  • 通過響應時間對資源進行降級

除了對並發線程數進行控制以外,Sentinel 還可以通過響應時間來快速降級不穩定的資源。當依賴的資源出現響應時間過長后,所有對該資源的訪問都會被直接拒絕,直到過了指定的時間窗口之后才重新恢復。

系統負載保護

Sentinel 同時提供系統維度的自適應保護能力。防止雪崩,是系統防護中重要的一環。當系統負載較高的時候,如果還持續讓請求進入,可能會導致系統崩潰,無法響應。在集群環境下,網絡負載均衡會把本應這台機器承載的流量轉發到其它的機器上去。如果這個時候其它的機器也處在一個邊緣狀態的時候,這個增加的流量就會導致這台機器也崩潰,最后導致整個集群不可用。

針對這個情況,Sentinel 提供了對應的保護機制,讓系統的入口流量和系統的負載達到一個平衡,保證系統在能力范圍之內處理最多的請求。

6. 如何使用Sentinel

Sentinel 可以簡單的分為 Sentinel 核心庫和 Dashboard。核心庫不依賴 Dashboard,但是結合 Dashboard 可以取得最好的效果。

資源,可以是任何東西,服務,服務里的方法,甚至是一段代碼。使用 Sentinel 來進行資源保護,主要分為幾個步驟:

  1. 定義資源
  2. 定義規則
  3. 檢驗規則是否生效 

在編碼的時候,只需要考慮這個代碼是否需要保護,如果需要保護,就將之定義為一個資源。

定義資源的常用方式

方式一: 拋出異常的方式定義資源

SphU 包含了 try-catch 風格的 API。用這種方式,當資源發生了限流之后會拋出 BlockException。這個時候可以捕捉異常,進行限流之后的邏輯處理。示例代碼如下:

1 // 1.5.0 版本開始可以利用 try-with-resources 特性
2 // 資源名可使用任意有業務語義的字符串,比如方法名、接口名或其它可唯一標識的字符串。
3 try (Entry entry = SphU.entry("resourceName")) {
4   // 被保護的業務邏輯
5   // do something here...
6 } catch (BlockException ex) {
7   // 資源訪問阻止,被限流或被降級
8   // 在此處進行相應的處理操作
9 }

特別地,若 entry 的時候傳入了熱點參數,那么 exit 的時候也一定要帶上對應的參數(exit(count, args)),否則可能會有統計錯誤。這個時候不能使用 try-with-resources 的方式。另外通過 Tracer.trace(ex) 來統計異常信息時,由於 try-with-resources 語法中 catch 調用順序的問題,會導致無法正確統計異常數,因此統計異常信息時也不能在 try-with-resources 的 catch 塊中調用 Tracer.trace(ex)。

手動 exit 示例:

 1 Entry entry = null;
 2 // 務必保證 finally 會被執行
 3 try {
 4   // 資源名可使用任意有業務語義的字符串,注意數目不能太多(超過 1K),超出幾千請作為參數傳入而不要直接作為資源名
 5   // EntryType 代表流量類型(inbound/outbound),其中系統規則只對 IN 類型的埋點生效
 6   entry = SphU.entry("自定義資源名");
 7   // 被保護的業務邏輯
 8   // do something...
 9 } catch (BlockException ex) {
10   // 資源訪問阻止,被限流或被降級
11   // 進行相應的處理操作
12 } catch (Exception ex) {
13   // 若需要配置降級規則,需要通過這種方式記錄業務異常
14   Tracer.traceEntry(ex, entry);
15 } finally {
16   // 務必保證 exit,務必保證每個 entry 與 exit 配對
17   if (entry != null) {
18     entry.exit();
19   }
20 }

熱點參數埋點示例:

 1 Entry entry = null;
 2 try {
 3     // 若需要配置例外項,則傳入的參數只支持基本類型。
 4     // EntryType 代表流量類型,其中系統規則只對 IN 類型的埋點生效
 5     // count 大多數情況都填 1,代表統計為一次調用。
 6     entry = SphU.entry(resourceName, EntryType.IN, 1, paramA, paramB);
 7     // Your logic here.
 8 } catch (BlockException ex) {
 9     // Handle request rejection.
10 } finally {
11     // 注意:exit 的時候也一定要帶上對應的參數,否則可能會有統計錯誤。
12     if (entry != null) {
13         entry.exit(1, paramA, paramB);
14     }
15 }

SphU.entry() 的參數描述:

 

方式二、注解方式定義資源 

Sentinel 支持通過 @SentinelResource 注解定義資源並配置 blockHandler 和 fallback 函數來進行限流之后的處理。示例:

 1 // 原本的業務方法.
 2 @SentinelResource(blockHandler = "blockHandlerForGetUser")
 3 public User getUserById(String id) {
 4     throw new RuntimeException("getUserById command failed");
 5 }
 6 
 7 // blockHandler 函數,原方法調用被限流/降級/系統保護的時候調用
 8 public User blockHandlerForGetUser(String id, BlockException ex) {
 9     return new User("admin");
10 }

注意 blockHandler 函數會在原方法被限流/降級/系統保護的時候調用,而 fallback 函數會針對所有類型的異常。另外請注意 blockHandler 和 fallback 函數的形式要求。 

方式三、支持異步調用

Sentinel 支持異步調用鏈路的統計。在異步調用中,需要通過 SphU.asyncEntry(xxx) 方法定義資源,並通常需要在異步的回調函數中調用 exit 方法。示例:

 1 try {
 2     AsyncEntry entry = SphU.asyncEntry(resourceName);
 3 
 4     // 異步調用.
 5     doAsync(userId, result -> {
 6         try {
 7             // 在此處處理異步調用的結果.
 8         } finally {
 9             // 在回調結束后 exit.
10             entry.exit();
11         }
12     });
13 } catch (BlockException ex) {
14     // Request blocked.
15     // Handle the exception (e.g. retry or fallback).
16 }

7. Sentinel 工作主流程

在 Sentinel 里面,所有的資源都對應一個資源名稱(resourceName),每次資源調用都會創建一個 Entry 對象。Entry 可以通過對主流框架的適配自動創建,也可以通過注解的方式或調用 SphU API 顯式創建。Entry 創建的時候,同時也會創建一系列功能插槽(slot chain),這些插槽有不同的職責,例如:

  • NodeSelectorSlot :負責收集資源的路徑,並將這些資源的調用路徑,以樹狀結構存儲起來,用於根據調用路徑來限流降級; 
  • ClusterBuilderSlot :則用於存儲資源的統計信息以及調用者信息,例如該資源的 RT, QPS, thread count 等等,這些信息將用作為多維度限流,降級的依據;
  • StatisticSlot :則用於記錄、統計不同緯度的 runtime 指標監控信息;
  • FlowSlot :用於根據預設的限流規則以及前面 slot 統計的狀態,來進行流量控制;
  • AuthoritySlot :則根據配置的黑白名單和調用來源信息,來做黑白名單控制;
  • DegradeSlot :則通過統計信息以及預設的規則,來做熔斷降級;
  • SystemSlot :則通過系統的狀態,例如 load1 等,來控制總的入口流量;

總體架構圖如下:

這個彩色的圖貌似更好看一點兒

 

Sentinel 將 SlotChainBuilder 作為 SPI 接口進行擴展,使得 Slot Chain 具備了擴展的能力。您可以自行加入自定義的 slot 並編排 slot 間的順序,從而可以給 Sentinel 添加自定義的功能。

 

8. Sentinel 流量控制

流量控制(flow control),其原理是監控應用流量的 QPS 或並發線程數等指標,當達到指定的閾值時對流量進行控制,以避免被瞬時的流量高峰沖垮,從而保障應用的高可用性。

FlowSlot 會根據預設的規則,結合前面 NodeSelectorSlotClusterNodeBuilderSlotStatisticSlot 統計出來的實時信息進行流量控制。

限流的直接表現是在執行 Entry nodeA = SphU.entry(resourceName) 的時候拋出 FlowException 異常。FlowException 是 BlockException 的子類,您可以捕捉 BlockException 來自定義被限流之后的處理邏輯。

同一個資源可以創建多條限流規則。FlowSlot 會對該資源的所有限流規則依次遍歷,直到有規則觸發限流或者所有規則遍歷完畢。

一條限流規則主要由下面幾個因素組成,我們可以組合這些元素來實現不同的限流效果:

  • resource :資源名,即限流規則的作用對象
  • count :限流閾值
  • grade :限流閾值類型(QPS 或並發線程數)
  • limitApp :流控針對的調用來源,若為 default 則不區分調用來源
  • strategy :調用關系限流策略
  • controlBehavior :流量控制效果(直接拒絕、Warm Up、勻速排隊) 

8.1. 基於QPS/並發數的流量控制

流量控制主要有兩種統計類型,一種是統計並發線程數,另外一種則是統計 QPS。類型由 FlowRule 的 grade 字段來定義。其中,0 代表根據並發數量來限流,1 代表根據 QPS 來進行流量控制。其中線程數、QPS 值,都是由 StatisticSlot 實時統計獲取的。

可以通過下面的命令查看實時統計信息:

curl http://localhost:8719/cnode?id=resourceName

並發線程數流量控制

並發線程數限流用於保護業務線程數不被耗盡。例如,當應用所依賴的下游應用由於某種原因導致服務不穩定、響應延遲增加,對於調用者來說,意味着吞吐量下降和更多的線程數占用,極端情況下甚至導致線程池耗盡。為應對太多線程占用的情況,業內有使用隔離的方案,比如通過不同業務邏輯使用不同線程池來隔離業務自身之間的資源爭搶(線程池隔離)。這種隔離方案雖然隔離性比較好,但是代價就是線程數目太多,線程上下文切換的 overhead 比較大,特別是對低延時的調用有比較大的影響。Sentinel 並發線程數限流不負責創建和管理線程池,而是簡單統計當前請求上下文的線程數目,如果超出閾值,新的請求會被立即拒絕,效果類似於信號量隔離。

QPS流量控制

當 QPS 超過某個閾值的時候,則采取措施進行流量控制。流量控制的效果包括以下幾種:直接拒絕、Warm Up、勻速排隊。對應 FlowRule 中的 controlBehavior 字段。

8.2. 基於調用關系的流量控制

調用關系包括調用方、被調用方;一個方法又可能會調用其它方法,形成一個調用鏈路的層次關系。Sentinel 通過 NodeSelectorSlot 建立不同資源間的調用的關系,並且通過 ClusterNodeBuilderSlot 記錄每個資源的實時統計信息。

有了調用鏈路的統計信息,我們可以衍生出多種流量控制手段。

根據調用方限流

ContextUtil.enter(resourceName, origin) 方法中的 origin 參數標明了調用方身份。這些信息會在 ClusterBuilderSlot 中被統計。可通過以下命令來展示不同的調用方對同一個資源的調用數據: 

根據調用鏈路入口限流:鏈路限流 

NodeSelectorSlot 中記錄了資源之間的調用鏈路,這些資源通過調用關系,相互之間構成一棵調用樹。這棵樹的根節點是一個名字為 machine-root 的虛擬節點,調用鏈的入口都是這個虛節點的子節點。

一棵典型的調用樹如下圖所示:

 

具有關系的資源流量控制:關聯流量控制 

當兩個資源之間具有資源爭搶或者依賴關系的時候,這兩個資源便具有了關聯。比如對數據庫同一個字段的讀操作和寫操作存在爭搶,讀的速度過高會影響寫得速度,寫的速度過高會影響讀的速度。如果放任讀寫操作爭搶資源,則爭搶本身帶來的開銷會降低整體的吞吐量。可使用關聯限流來避免具有關聯關系的資源之間過度的爭搶,舉例來說,read_db 和 write_db 這兩個資源分別代表數據庫讀寫,我們可以給 read_db 設置限流規則來達到寫優先的目的:設置 FlowRule.strategy 為 RuleConstant.RELATE 同時設置 FlowRule.ref_identity 為 write_db。這樣當寫庫操作過於頻繁時,讀數據的請求會被限流。

9. Sentinel 文檔

https://github.com/alibaba/Sentinel

https://github.com/alibaba/Sentinel/wiki/%E6%96%B0%E6%89%8B%E6%8C%87%E5%8D%97

https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8

https://github.com/alibaba/Sentinel/wiki/Sentinel%E5%B7%A5%E4%BD%9C%E4%B8%BB%E6%B5%81%E7%A8%8B

https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6

https://github.com/alibaba/Sentinel/tree/master/sentinel-demo 

https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM