Sentinel 是什么?

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

官網:https://github.com/alibaba/Sentinel

中文官網:https://github.com/alibaba/Sentinel/wiki

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 等應用容器。

Sentinel 重要概念

定義資源

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

復制代碼
 1 public static void main(String[] args) {
 2     // 配置規則.
 3     initFlowRules();
 4 
 5     while (true) {
 6         // 1.5.0 版本開始可以直接利用 try-with-resources 特性,自動 exit entry
 7         try (Entry entry = SphU.entry("HelloWorld")) {
 8             // 被保護的邏輯
 9             System.out.println("hello world");
10     } catch (BlockException ex) {
11             // 處理被流控的邏輯
12         System.out.println("blocked!");
13     }
14     }
15 }
復制代碼

也可以通過我們提供的 注解支持模塊,來定義我們的資源,類似于下面的代碼:

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

這樣,helloWorld() 方法就成了我們的一個資源。注意注解支持模塊需要配合 Spring AOP 或者 AspectJ 一起使用。

定義規則

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

復制代碼
 1 private static void initFlowRules(){
 2     List<FlowRule> rules = new ArrayList<>();
 3     FlowRule rule = new FlowRule();
 4     rule.setResource("HelloWorld");
 5     rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
 6     // Set limit QPS to 20.
 7     rule.setCount(20);
 8     rules.add(rule);
 9     FlowRuleManager.loadRules(rules);
10 }
復制代碼

  完成上面 ,Sentinel 就能夠正常工作了。更多的信息可以參考官網。

Sentinel使用

  案例架構圖如下:

  

搭建Sentinel控制臺

  1、下載sentienl的jar包,本例使用:sentinel-dashboard-1.7.2.jar,地址:https://github.com/alibaba/Sentinel/releases

  2、使用java -jar命令啟動Sentinel控制臺,注意:啟動 Sentinel 控制臺需要 JDK 版本為 1.8 及以上版本。

      命令格式:java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

    如若8080端口沖突,可使用 -Dserver.port=新端口 進行設置。

    命令:java -jar sentinel-dashboard-1.7.2.jar

    

  3、訪問地址:http://localhost:8080/,8080為Sentinel的默認端口

    

  4、輸入默認用戶名/密碼:sentinel/sentinel,進入首頁

    

搭建Sentinel客戶端

  1、新建項目Spring Cloud項目(springcloud-sentinel-service8401)

    

  2、編輯pom.xml文件,引入依賴

    <dependencies>

        <!--sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!--nacos-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

    </dependencies>

 

  3、編輯application.yml文件

# 端口
server:
  port: 8401
spring:
  application:
    name: alibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.186.128:8848
    sentinel:
      transport:
        # 配置Sentinel DashBoard地址
        dashboard: 192.168.186.128:8998
        # 應用與Sentinel控制臺交互的端口,應用本地會起一個該端口占用的HttpServer
        # 默認8719端口,假如端口被占用,依次+1,直到找到未被占用端口
        port: 8719
management:
  endpoints:
    web:
      exposure:
        include: '*'

 

  4、編輯主啟動類

復制代碼
1 @SpringBootApplication
2 @EnableDiscoveryClient
3 public class SentinelMain8401 {
4     public static void main(String[] args) {
5         SpringApplication.run(SentinelMain8401.class, args);
6     }
7 }
復制代碼

  5、編輯一個Controller

復制代碼
 1 @RestController
 2 public class FlowLimitController {
 3 
 4     @GetMapping("/testA")
 5     public String testA(){
 6         return "--------testA";
 7     }
 8 
 9     @GetMapping("/testB")
10     public String testB(){
11         return "--------testB";
12     }
13 }
復制代碼

  6、測試

    1)啟動項目,啟動Nacos服務(【SpringCloud】Spring Cloud Alibaba 之 Nacos注冊中心(二十七)

      啟動Sentinel控制臺

    2)在瀏覽器中訪問地址:http://localhost:8401/testA

    3)查看Sentinel控制臺-〉選擇alibaba-sentinel-service服務-〉實時監控-〉可以看到監控詳情

      

    4)同時可以查看簇點鏈路

      

    5)同時可以查看機器列表

      

 

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、勻速排隊)

   以下是關于流量控制中名詞解析

復制代碼
資源名:唯一名稱、默認請求路徑

針對來源:Sentinel可以針對調用者進行限流,填寫微服務名,默認default(不區分來源)

閥值類型/單機閥值:
	
	QPS(每秒鐘的請求數量):當調用該api的QPS達到閥值的時候,進行限流
	線程數:當調用該api的線程數達到閥值的時候,進行限流

是否集群:不需要集群

流控模式:
	
	直接:api達到限流條件時,直接限流
	關聯:當關聯的資源達到閥值時,就限流自己
	鏈路:只記錄指定鏈路上的流量(指定資源從入口資源進來的流量,如果達到閥值,就限流)【api級別的針對來源】

流控效果:

	快速失敗:直接失敗,拋異常
	Warm Up:根據codeFactor(冷加載因子,默認3)的值,從閥值/codeFactor,經過預熱時長,才達到設置的QPS閥值
	排隊等待:勻速排隊,讓請求以均勻的速度通過,閥值類型必須設置為QPS,否則無效
復制代碼

基于QPS的流量控制

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

  1、搭建項目,Controller內容如下:

復制代碼
 1 @RestController
 2 public class FlowLimitController {
 3 
 4     @GetMapping("/testA")
 5     public String testA(){
 6         return "--------testA";
 7     }
 8 
 9     @GetMapping("/testB")
10     public String testB(){
11         return "--------testB";
12     }
13 }
復制代碼

 

   2、將項目啟動,以及Sentinel啟動,添加流控規則,如下:

    

  3、設置流控規則,每秒鐘的請求數量為1,流控效果為快速失敗,如下,保存后,即生效

    

  4、測試

    1)瀏覽器訪問地址:http://localhost:8401/testA,正常訪問

      

    2)快速點擊瀏覽器刷新按鈕,刷新界面,頁面顯示:Blocked by Sentinel (flow limiting)

      

基于并發數的流量控制

  1、繼續使用以上項目,修改testA方法,如下:

復制代碼
1 @GetMapping("/testA")
2 public String testA(){
3     try {
4         Thread.sleep(800);
5     } catch (InterruptedException e) {
6         e.printStackTrace();
7     }
8     return "--------testA";
9 }
復制代碼

 

  2、編輯流量規則,將流控規則改為當調用該api的線程數達到1的時候,進行限流,如下:

    

  3、測試

    1)重新啟動項目

    2)使用瀏覽器單次請求,http://localhost:8401/testA,正常顯示

    2)使用JMeter進行并發測試請求:http://localhost:8401/testA,響應內容為:Blocked by Sentinel (flow limiting)

流控模式:直接

  上面2個例子,流控模式都是直接,對自己本身資源的限制。 

流控模式:關聯

  當兩個資源之間具有資源爭搶或者依賴關系的時候,這兩個資源便具有了關聯。像對數據庫同一個字段的讀操作和寫操作存在爭搶,讀的速度過高會影響寫得速度,寫的速度過高會影響讀的速度。如果放任讀寫操作爭搶資源,則爭搶本身帶來的開銷會降低整體的吞吐量。

  示例

  1、項目代碼還是以上的項目代碼

  2、編輯流量規則,設置資源testA關聯的testB,當testB達到閥值時,testA被限流。如下:

    

  3、測試

    1)啟動項目

    2)使用瀏覽器請求,http://localhost:8401/testA,正常顯示

    3)使用JMeter不停的循環請求:http://localhost:8401/testB,testB請求結果都正確

    4)然后使用瀏覽器請求,http://localhost:8401/testA,無法正常顯示,資源限制

流控模式:鏈路

  1、項目代碼還是以上的項目代碼,Controllerh中testB方法

1 @GetMapping("/testB")
2 public String testB(){
3     return "--------testB";
4 }

 

  2、查看簇點鏈路,發現/test,資源入口是sentinel_web_servlet_context

    

  3、刪除其他流控規則,新增流量規則,設置資源testB鏈路限流,如下:、

    

  4、測試

    1)啟動項目

    2)使用瀏覽器請求,http://localhost:8401/testB,正常顯示

    3)快速點擊瀏覽器刷新按鈕,刷新界面,頁面顯示:Blocked by Sentinel (flow limiting) 

流控效果:快速失敗

  以上例子顯示了快速失敗的效果,快速失敗(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默認的流量控制方式,當QPS超過任意規則的閾值后,新的請求就會被立即拒絕,拒絕方式為拋出FlowException。這種方式適用于對系統處理能力確切已知的情況下,比如通過壓測確定了系統的準確水位時。

流控效果:Warm Up(預熱)

  Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即預熱/冷啟動方式。當系統長期處于低水位的情況下,當流量突然增加時,直接把系統拉升到高水位可能瞬間把系統壓垮。通過"冷啟動",讓通過的流量緩慢增加,在一定時間內逐漸增加到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮。

  應用場景:

   如:秒殺系統在開啟瞬間,會有很多流量上來,很有可能把系統打死,預熱方式就是把為了保護系統,可慢慢的把流量放進來,慢慢的把閥值增長到設置的閥值

  案例:

    閥值為10 + 預熱時長設置5秒

    系統初始化的閥值為10/3 約等于 3,即閥值剛開始為3;然后過了5秒閥值才慢慢升高恢復到10

  1、項目代碼還是以上的項目代碼,Controllerh中testC方法

1 @GetMapping("/testC")
2 public String testC(){
3     return "--------testC";
4 }

  2、新增流量規則,設置資源testC,單機閥值為10,流控效果為Warm Up,預熱時長為5秒,如下:

    

  3、測試

    1)啟動項目

    2)使用瀏覽器訪問地址,http://localhost:8401/testC,正常顯示

    3)以每秒5次的刷新,一直刷新界面,效果,可以看到部分請求返回錯誤

    4)5秒鐘過后,還是以以每秒5次的刷新速度,刷新界面,請求正常

流控效果:排隊等待

  排隊等待(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式會嚴格控制請求通過的間隔時間,也即是讓請求以均勻的速度通過,對應的是漏桶算法。

  這種方式主要用于處理間隔性突發的流量,例如消息隊列。想象一下這樣的場景,在某一秒有大量的請求到來,而接下來的幾秒則處于空閑狀態,我們希望系統能夠在接下來的空閑期間逐漸處理這些請求,而不是在第一秒直接拒絕多余的請求。

  案例

  1、項目代碼還是以上的項目代碼,Controllerh中testA方法

復制代碼
1 @GetMapping("/testA")
2 public String testA(){
3     try {
4         Thread.sleep(800);
5     } catch (InterruptedException e) {
6         e.printStackTrace();
7     }
8     return "--------testA";
9 }
復制代碼

  2、新增流量規則,設置資源testA,流控效果為排隊等待,超時時間為20000毫秒,如下:

    

  3、測試

    1)啟動項目

    2)使用瀏覽器訪問地址,http://localhost:8401/testA,正常顯示

    3)使用JMeter并發10個請求,執行一次,觀察10次執行結果

    4)所有請求都成功了,請求響應時間逐漸增加