spring-cloud-alibaba sentinel 流控 熔斷降級

一、概述

Sentinel: 分佈式系統的流量防衛兵
Sentinel 是什麼?
隨着微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 以流量爲切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。

sentinel 中的熔斷降級分爲兩種

  • 針對於流控導致降級的 blockHandler
  • 針對於java運行時異常導致降級的 fallback

使用sentinel主要還是重點關注其流控熔斷,對於java運行時的fallback可以直接使用統一異常處理來解決。讓專業的sentinel來做專業的流控。那些運行時的事情就不要他操心了。
另外值得注意的是。作爲熔斷機制 sentinel並沒有半開狀態。

二、sentinel的安裝和使用

github下載地址:https://github.com/alibaba/Sentinel/tags
本次以1.7.1爲基礎作爲實驗
下載:sentinel-dashboard-1.7.1.jar
對應的springcloud springboot 以及 springcloud-alibaba 版本爲

<!-- 版本對照 https://spring.io/projects/spring-cloud-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.2.5.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>
<!-- 版本對照 https://start.spring.io/actuator/info-->
<!-- "Hoxton.SR3": "Spring Boot >=2.2.0.M4 and <2.2.6.BUILD-SNAPSHOT",-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <!--SR3 版本 與openfeign整合sentinel會報錯-->
    <version>Hoxton.SR1</version>

    <type>pom</type>
    <scope>import</scope>
</dependency>

<!--cloud alibaba https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md-->
<!--2.2.x 版本適用於 Spring Boot 2.2.x-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>2.2.0.RELEASE</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

下載完成後 啓動本地nacos
並使用java -jar 命令來啓動sentinel (它使用的默認端口爲8080)
啓動完成後 訪問 http://localhost:8080 用戶名密碼默認都是sentinel

三、 與springboot微服務整合(關鍵代碼)

POM

<!-- 引入nacos服務發現 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!-- 引入 sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

yml

server:
	port: 8001
spring:
  application:
    name: boot-server
  cloud:
    nacos: # 配置nacos
      discovery: # 服務發現地址
        server-addr: 127.0.0.1:8848 # nacos 啓動地址
    sentinel: # 配置sentinel
      transport:
        dashboard: 127.0.0.1:8080 # 配置sentinel dashboard 啓動地址
        port: 8719 # 直接寫8719 端口號默認是8719 如果被佔用就是+1 直到找到未被佔用端口
      filter:
        url-patterns: /** # 默認是/* 這裏改成/** 則會監控所有的請求

這本地的boot-server微服務就被sentinel監控了

編寫一個controller

@Slf4j
public class FallbackController {


    @GetMapping("/hot_key/{id}")
    @SentinelResource(value = "fallback|hot_key",blockHandler = "testHotKeyFallback")
    @ApiOperation("熱點限流")
    public ResponseEntity<ResponseEntityBody> testHotKey(@PathVariable("id") String id) throws Exception {
        log.info("進入了 熱點限流 測試方法");
        return ResponseEntity.ok(ResponseEntityBody.create("熱點限流方法:" + id));
    }

    public ResponseEntity<ResponseEntityBody> testHotKeyFallback(String id, BlockException exception) throws Exception {
        return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(ResponseEntityBody.create(id + "熱點限流了!!"));
    }


    @GetMapping("/rt1")
    @ApiOperation("RT,這裏要在sentinel中設置 RT小於300 否則不能觸發熔斷,指定自定義限流方法1")
    // 指定全侷限流降級方法
    @SentinelResource(value = "resource_rt1", blockHandlerClass = MyCustomLimiterHandler.class, blockHandler = "globalFallback1")
    public ResponseEntity<ResponseEntityBody> testRT() {
        log.info("進入了RT測試方法");
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return ResponseEntity.ok(ResponseEntityBody.create("A"));
    }

    @GetMapping("/rt2")
    @ApiOperation("RT,這裏要在sentinel中設置 RT小於300 否則不能觸發熔斷,指定自定義限流方法2")
    // 指定全侷限流降級方法
    @SentinelResource(value = "resource_rt2", blockHandlerClass = MyCustomLimiterHandler.class, blockHandler = "globalFallback2")
    public ResponseEntity<ResponseEntityBody> testRT2() {
        log.info("進入了RT測試方法");
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return ResponseEntity.ok(ResponseEntityBody.create("A"));
    }

    @GetMapping("/rate")
    @ApiOperation("異常比例: 這裏是 50%的出錯概率 在sentinel中 設置低於50%的錯誤率就可以觸發熔斷降級")
    public ResponseEntity<ResponseEntityBody> testRate() throws Exception {
        log.info("進入了 異常比例 測試方法");
        throw CommonException.create(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ResponseEntityBody.create("出錯了!")));
    }

    @GetMapping("/err_num")
    @ApiOperation("異常數: 這裏一定會報錯 所以 觸發熔斷後 不會再進入這個方法內 直到下一個窗口期")
    public ResponseEntity<ResponseEntityBody> testErrNum() throws Exception {
        log.info("進入了 異常數 測試方法");
        throw CommonException.create(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ResponseEntityBody.create("出錯了!")));
//        return ServerResponse.createBySuccessMessage("B");
    }

}

統一的降級方法

public class MyCustomLimiterHandler {
    public static ResponseEntity<ResponseEntityBody> globalFallback1(BlockException exception) {
        return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(ResponseEntityBody.create("(Global)服務器太忙 限流 返回限流方法1 "));
    }

    public static ResponseEntity<ResponseEntityBody> globalFallback2(BlockException exception) {
        return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body(ResponseEntityBody.create("(Global)服務器太忙 限流 返回限流方法2 "));
    }
}

四、各種限流方式總結

因爲sentinel是懶加載機制,所以需要訪問一次訪問才能看到簇點列表。
可以在簇點列表中對服務設置限流策略。

4.1. 指定降級方法

添加@SentinelResource註解

  • 可以指定blockHandler降級方法
  • 可以指定blockHandlerClass 自定義降級方法類 並指定類中的某個降級方法 blockHandler

注意 使用SentinelResource註解 一定要加資源名稱value字段 否則不起作用 ,

因爲默認在sentinel控制檯是將限流規則配置在url上的,而url配置後 是默認的降級返回。
所以不會找到註解的資源名對應的降級方法

最後會以默認的方法返回降級

4.2 RT (平均響應時間)

平均響應時間超過設定的閾值 並且在時間窗口內通過的請求大於5 則觸發熔斷降級
窗口期過後 關閉斷路器 回覆服務
RT的最大值爲 4900s 可用通過 -Dcsp.sentinel.statistic.max.rt=xxx 設置最大數

4.3 異常比例

QPS>=5 並且 異常比例(每秒統計) 超過閾值 則觸發熔斷降級
窗口期過後 關閉斷路器 回覆服務

4.4 異常數

按分鐘來統計服務異常數,如果超過閾值則觸發熔斷降級,
窗口期過後 關閉斷路器 回覆服務

4.5 熱點限流

可以設置某個資源的請求qps限流
比如:product/query/{id}
註解中的value就是資源名稱
方法中的參數 從0開始 可以指定是否傳遞某個參數 限流
也可以指定某個參數 的值爲多少 限流
比如 可以指定 id = 1 qps 不能超過 10 則每秒只能有10個請求 訪問該資源。

4.6 系統級限流

Sentinel 系統自適應限流從整體維度對應用入口流量進行控制,
結合應用的 Load、CPU 使用率、總體平均 RT、入口 QPS 和併發線程數等幾個維度的監控指標,
通過自適應的流控策略,讓系統的入口流量和系統的負載達到一個平衡,讓系統儘可能跑在最大吞吐量的同時保證系統整體的穩定性。
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 達到閾值即觸發系統保護。
系統級限流規則應該視情況添加。

五、java運行時fallback降級

fallback java運行時異常降級 這個配置與blockHandler相似,也可以使用全局異常處理代替

六、配置持久化到nacos

防止每次啓動微服務都會丟失之前的限流配置
pom 增加依賴

<!--sentinel 配置 持久化-->
<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

yml增加配置

spring:
	cloud:
      datasource: # 配置sentinel 流控規則持久化
        ds1: # 數據庫1
          nacos: # 持久化到 nacos
            server-addr: 127.0.0.1:8848 # nacos 地址
            dataId: ${spring.application.name} # 數據id
            data-type: json # 格式json
            rule-type: flow # 流控規則
            # namespace: public # 使用默認的 命名空間
            # groupId: DEFAULT_GROUP # 使用默認分組

然後在nacos中添加配置文件json

[
    {
        "resource": "resource_rt1",
        "limitApp": "default",
        "grade": 1,
        "count": 5,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]

resource:資源名
limitApp:流控針對的調用來源,default不區分來源
grade:限流閾值類型(0-根據併發數量來限流 1-根據QPS來進行流量控制)
count:限流閾值
strategy:調用關係限流策略
controlBehavior:流量控制效果(直接拒絕、WarmUP、勻速排隊)
clusterMode:是否集羣模式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章