微服務架構 | 5.2 基於 Sentinel 的服務限流及熔斷

多氯環己烷發表於2022-01-30

目錄


前言

參考資料
《Spring Microservices in Action》
《Spring Cloud Alibaba 微服務原理與實戰》
《B站 尚矽谷 SpringCloud 框架開發教程 周陽》
《Sentinel GitHub 官網》
《Sentinel 官網》

Sentinel 是面向分散式服務架構的輕量級流量控制元件,主要以流量為切入點,從限流、流量整形、服務降級、系統負載保護等多個維度來幫助我們保障微服務的穩定性;


1. Sentinel 基礎知識

1.1 Sentinel 的特性

  • 豐富的應用場景:幾乎涵蓋所有的應用場景,例如秒殺(即突發流量控制在系統容量可以承受的範圍)、訊息削峰填谷、叢集流量控制等;
  • 實時監控:開發者可以在控制檯中看到接入應用的單臺機器秒級資料,甚至500臺以下規模的叢集彙總執行情況;
  • 開源生態支援:Sentine提供開箱即用的與其他開源框架/庫的整合,例如與Spring Cloud、Dubbo、gRPC的整合;
  • SPI 擴充套件點支援:提供了 SPI 擴充套件點支援,開發者可以通過擴充套件點來定製化限流規則,動態資料來源適配等需求;

Sentinel 的特性
Sentinel 的開源生態

1.2 Sentinel 的組成

  • 分兩部分:
    • 核心庫(Java 客戶端):不依賴任何框架/庫,能夠執行於所有 Java 執行時環境,同時對 Dubbo、Spring Cloud 等框架也有較好的支援;
    • 控制檯(Dashboard):亦稱 Sentinel 伺服器。基於 Spring Boot 開發,打包後可以直接執行,不需要額外的 Tomcat 等應用容器;

1.3 Sentinel 控制檯上的 9 個功能

功能 說明
實時監控 實時監控每個資源名(介面、請求路徑)的通過 QPS、拒絕 QPS 和響應時間;
簇點鏈路 通過樹狀檢視和列表檢視展示介面呼叫的關係以及通過 QPS、拒絕 QPS、併發數、平均 RT、分鐘通過和拒絕等資訊;
流控規則 又稱:流量控制(flow control)。其原理是監控應用流量的 QPS 或併發執行緒數等指標,當達到指定的閾值時對流量進行控制,以避免被瞬時的流量高峰沖垮,從而保障應用的高可用性;
熔斷規則 對不穩定的弱依賴服務呼叫進行熔斷降級,暫時切斷不穩定呼叫,避免區域性不穩定因素導致整體的雪崩;
熱點規則 又稱:熱點引數限流規則。熱點即經常訪問的資料。很多時候我們希望統計某個熱點資料中訪問頻次最高的 Top K 資料,並對其訪問進行限制;
系統規則 系統保護規則是從應用級別的入口流量進行控制,從單臺機器的 load、CPU 使用率、平均 RT、入口 QPS 和併發執行緒數等幾個維度監控應用指標,讓系統儘可能跑在最大吞吐量的同時保證系統整體的穩定性;
授權規則 根據呼叫來源來判斷該次請求是否允許放行;
叢集流控 叢集流控可以解決流量不均勻導致總體限流效果不佳的問題;
機器列表 收集 Sentinel 客戶端傳送的心跳包,用於判斷機器是否線上;

Sentinel 控制檯上的 9 個功能

1.4 Sentinel 工作原理

  • Sentinel 的核心分為三部分:工作流程、資料結構和限流演算法;

Sentinel 工作原理

  • 呼叫鏈路是 Sentinel 的工作主流程,由各個 Slot 插槽組成,將不同的 Slot 按照順序串在一起(責任鏈模式),從而將不同的功能(限流、降級、系統保護)組合在一起;
  • Sentinel 中各個 Slot 承擔了不同的職責,如:LogSlot 負責記錄日誌、StatisticSlot 負責統計指標資料、FlowSlot 負責限流等。這是一種職責分離的設計,每個模組更聚焦於實現某個功能;
  • 在 Sentinel 中,所有的資源都對應一個資源名稱(resourceName),每次訪問該資源都會建立一個 Entry 物件,在建立 Entry 的同時,會建立一系列功能槽(Slot Chain),這些槽會組成一個責任鏈,每個槽負責不同的職責;
Slot 插槽 說明
NodeSelectorSlot 負責收集資源的呼叫路徑,以樹狀結構儲存呼叫棧,用於根據呼叫路徑來限流降級;
ClusterBuilderSlot 負責建立以資源名維度統計的 ClusterNode ,以及建立每個 ClusterNode 下按呼叫來源 origin 劃分的 StatisticNode;
LogSlot 在出現限流、熔斷、系統保護時負責記錄日誌;
AuthoritySlot 許可權控制,支援黑名單和白名單兩種策略;
SystemSlot 控制總的入口流量,限制條件依次是總 QPS、匯流排程數、RT 閾值、作業系統當前 load1、作業系統當前 CPU 利用率;
FlowSlot 根據限流規則和各個Node中的統計資料進行限流判斷;
DegradeSlot 根據熔斷規則和各個Node中的統計資料進行服務降級;
StatisticSlot 統計不同維度的請求數、通過數、限流數、執行緒數等 runtime 資訊,這些資料儲存在 DefaultNode、OriginNode 和 ClusterNode 中;

1.5 Sentinel 原始碼分析


2. 安裝並執行 Sentinel 控制檯

Sentinel 與 Nacos 類似,有兩種安裝方式:使用已經編譯好的安裝包和原始碼部署;由於要對 Sentinel 原始碼進行分析,這裡推薦原始碼部署;
這裡選擇的版本是 1.8.3;

2.1 安裝包安裝 Sentinel 控制檯

2.1.1 下載 Sentinel

從官網下載 Sentinel

2.1.2 使用命令啟動 Sentinel 控制檯

  • 執行前要滿足兩個條件:有 java8 環境、8080 埠不能被佔用;
    • win10 解決埠占用用以下三個命令即可(用管理員開啟 cmd):
    • 檢視所有埠:netstat -ano 或者指定檢視 8080 埠占用情況 netstat -aon|findstr 8080
    • 根據程式查詢應用程式:tasklist|findstr {上面查到的程式 PID}
    • 關閉程式:taskkill /f /t /im {上面查到的應用程式}
  • 到下載的 jar 包下啟動 cmd 視窗,執行下面命令:
  • java -jar sentinel-dashboard-1.8.3.jar
  • 如果不想殺掉 8080 程式可以之地埠號執行:java -Dserver.port=7777 -Dcsp.sentinel.dashboard.server=localhost:7777 -jar sentinel-dashboard-1.8.3.jar
    • -Dserver.port:指定 Sentinel 控制檯的訪問埠,預設是 8080;
    • -Dcsp.sentinel.dashboard.server:指定 Sentinel Dashboard 控制檯的 IP 地址和埠,這裡進行設定的目的是把自己的限流資料暴露到監控平臺;
    • -Dproject.name:設定專案名稱;

開啟 cmd 命令視窗
啟動 Sentinel

2.1.3 訪問 Sentinel 控制檯

  • 傳送請求:http://localhost:8080
  • 1.6.0 版本後引入登入功能,預設登入賬號密碼均為 sentinel

成功啟動 sentinel

2.2 原始碼部署 Sentinel 控制檯

2.2.1 拉取原始碼

拉取 Sentinel 原始碼

2.2.2 啟動 Sentinel 控制檯

  • mvn install 將專案安裝到本地;
  • 接著找到 sentinel-dashboard 模組下的主程式類 DashboardApplication 執行即可;
  • *為了防止埠占用,這裡筆者將 Sentinel 控制檯的埠號改為了 28080;

3. Spring Cloud Nacos 整合 Sentinel

3.1 引入 pom.xml 依賴檔案

<!-- Sentinel 核心包 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

3.2 修改 bootstrap.yml 配置檔案

spring:
  application:
    name: nacos-config-client #必須,構成 Nacos 配置管理 Data ID 欄位的一部分
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos 服務註冊中心地址
      config:
        server-addr: localhost:8848 #Nacos 作為配置中心地址
        file-extension: yaml #指定 yaml 格式的配置
    #以下新增
    sentinel:
      transport:
        dashboard: localhost:28080  #配置Sentinel dashboard地址
        port: 8719  #預設8719埠,假如被佔用會自動從8719開始依次+1掃描,直至找到未被佔用的埠

3.3 編寫業務類

  • 編寫個介面測試用;
  • 基於 Sentinel 控制檯的流控規則不需要新增任何資源埋點;
  • 在預設情況下 Sentinel Starter 會對所有 HTTP 請求進行限流;
@RestController
public class ConfigClientController {
    @GetMapping("/easytest")
    public String testMothod(){
        return "test";
    }
}
  • 這時啟動程式,訪問 Sentinel 控制檯,並不能看到效果。由於 Sentinel 使用的是懶載入機制,需要進行一次介面呼叫才能看到監控效果介面:

監控效果介面

3.4 基於 Sentinel 控制檯新增容災規則

  • 手動配置容災規則請見本篇《5. 手動配置流控規則》;

3.4.1 流控規則頁面說明

下圖表示 1 秒鐘內查詢 1 次就是 OK,若超過次數 1,就直接-快速失敗,報預設錯誤;

流控規則控制頁面

  • 資源名 resource:唯一名稱,預設請求路徑;
  • 針對來源 limitApp:Sentinel 可以針對呼叫者進行限流,填寫微服務名,預設 default(不區分來源);
  • 閾值型別 grade、單機閾值 count
    • QPS grade值1(每秒鐘的請求數量):當呼叫該 API 的 QPS 達到閾值的時候,進行限流;
    • 執行緒數 grade值0:當呼叫該 API 的執行緒數達到閾值的時候,進行限流;
  • 是否叢集 clusterMode:不需要叢集;
  • 流控模式
    • 直接:API 達到限流條件時,直接限流;
    • 關聯 strategy:當關聯的資源 B 達到閾值時,就限流自己 A(這裡需要手速快或使用 Postman 模擬高併發);
    • 鏈路:只記錄指定鏈路上的流量(指定資源從入口資源進來的流量,如果達到閾值,就進行限流)(API 級別的針對來源);
  • 流控效果 controlBehavior
    • 快速失敗 RuleConstant.CONTROL_BEHAVIOR_DEFAULT:直接失敗,拋異常 Blocked by Sentinel (flow limiting)
    • Warm Up(預熱) RuleConstant.CONTROL_BEHAVIOR_WARM_UP:根據 codeFactor(冷載入因子,預設3)的值,閾值除以 codeFactor 得到預熱時長。經過預熱時長,才達到設定的 QPS 閾值。
      • 用於秒殺系統在開啟的瞬間,剛開始不行,後續慢慢OK;
    • 排隊等待 RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER:勻速排隊,讓請求以勻速的速度通過(漏桶演算法),閾值型別必須設定為 QPS,否則無效;
      • 用於處理間隔性突發的流量;

3.4.2 熔斷規則頁面說明

下圖表示當我們的請求響應超過 1000ms ,並且該統計的請求比例超過 50% 時(統計的請求數量需要大於 5),觸發熔斷;
經過熔斷時長 5s 後進入探測恢復狀態,若下一個請求響應時間小於 1000ms,則熔斷結束;反之再次熔斷;

 熔斷規則控制介面

  • 資源名 resource:唯一名稱,預設請求路徑;
  • 熔斷策略 grade
    • 慢呼叫比例 (SLOW_REQUEST_RATIO):請求的響應時間大於 RT 統計為慢呼叫。當單位統計時長(statIntervalMs)內請求數目大於設定的最小請求數,並且慢呼叫的比例大於閾值,則接下來的熔斷時長內請求會自動被熔斷。經過熔斷時長後熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求響應時間小於設定的慢呼叫 RT 則結束熔斷,若大於設定的慢呼叫 RT 則會再次被熔斷;
    • 異常比例 (ERROR_RATIO):當單位統計時長(statIntervalMs)內請求數目大於設定的最小請求數目,並且異常的比例大於閾值,則接下來的熔斷時長內請求會自動被熔斷。經過熔斷時長後熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。異常比率的閾值範圍是 [0.0, 1.0],代表 0% - 100%;
    • 異常數 (ERROR_COUNT):當單位統計時長內的異常數目超過閾值之後會自動進行熔斷。經過熔斷時長後熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷;
  • 最大 RT、比例閾值 count:慢呼叫比例模式下為慢呼叫臨界 RT(超出該值計為慢呼叫);異常比例和異常數模式下為對應的閾值;
  • 慢呼叫比例閾值 slowRatioThreshold:僅慢呼叫比例模式有效(1.8.0 引入);
  • 熔斷時長 timeWindow:單位為 s;
  • 最小請求數 minRequestAmount:請求數小於該值時即使異常比率超出閾值也不會熔斷(1.7.0 引入);
  • 統計時長 statIntervalMs:如 60*1000 代表分鐘級(1.8.0 引入);

3.4.3 熱點規則頁面說明

  • 常用於:統計某個熱點資料中訪問頻次最高的 Top K 資料,並對其訪問進行限制;
  • 熱點引數限流可以看做是一種特殊的流量控制,僅對包含熱點引數的資源呼叫生效;
  • Sentinel 利用 LRU 策略統計最近最常訪問的熱點引數,結合令牌桶演算法來進行引數級別的流控。熱點引數限流支援叢集模式;

1. 簡單示例

  • 編寫業務類
  • @SentinelResource 的用法詳情請見本篇第 4 點;
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "dealHandlerTestHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                         @RequestParam(value = "p2",required = false) String p2){
    return "testHotKey";
}
public String dealHandlerTestHotKey(String p1, String p2, BlockException exception) {
    return "dealHandler_testHotKey";
}
  • 在 Sentinel 控制檯上配置規則

下圖表示第一個引數(索引為0)有值的話(對應上述程式碼的 p1),1 秒的 QPS 為 1,超過就限流,限流後呼叫 dealHandler_testHotKey 支援方法;

熱點規則控制介面

  • 資源名 resource:唯一名稱,預設請求路徑;

  • 限流模式 grade:限流模式只支援 QPS 模式;

  • 引數索引 paramIdx:必填,對應 SphU.entry(xxx, args) 中的引數索引位置;

  • 單機閾值 count:限流閾值,必填;

  • 統計視窗時長 durationInSec:單位為秒,1.6.0 版本開始支援;

  • 是否叢集 clusterMode:是否是叢集引數流控規則;

  • 叢集流控相關配置 clusterConfig

  • 引數例外項 paramFlowItemList:可以針對指定的引數值單獨設定限流閾值,不受前面 count 閾值的限制。僅支援基本型別和字串型別;

  • 流控效果 controlBehavior:流控效果(支援快速失敗和勻速排隊模式),1.6.0 版本開始支援;

  • 測試訪問

    • http://localhost:18082/testHotKey?p1=22:觸發限流;
    • http://localhost:18082/testHotKey?p1=22&p2=33:觸發限流;
    • http://localhost:18082/testHotKey?p2=33:不觸發限流;

2. 引數例外項配置

當 p1 的值等於 5 時,它的閾值可以達到 200;

熱點規則控制介面-引數例外配置

  • 注意點選“新增”;
  • 熱點引數必須是基本型別或者 String;

3.4.4 系統規則頁面說明

  • 系統保護規則是從應用級別的入口流量進行控制,從單臺機器的 load、CPU 使用率、平均 RT、入口 QPS 和併發執行緒數等幾個維度監控應用指標,讓系統儘可能跑在最大吞吐量的同時保證系統整體的穩定性;

系統規則控制介面

  • load1 表示系統的負載;
  • 系統規則支援一下五種模式:
    • 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 達到閾值即觸發系統保護;

3.4.5 授權規則頁面說明

  • 根據呼叫來源來判斷該次請求是否允許放行;

授權規則控制介面

  • 資源名 resource:資源名,即限流規則的作用物件;
  • 流控應用 limitApp:對應的黑名單/白名單,不同 origin 用 , 分隔,如 appA,appB;
  • 授權型別 strategy:限制模式,AUTHORITY_WHITE 為白名單模式,AUTHORITY_BLACK 為黑名單模式,預設為白名單模式;

4. 使用 @SentinelResource 自定義限流處理邏輯

  • Sentinel starter 在預設情況下會為所有的 HTTP 服務提供限流埋點,所以如果只想對 HTTP 服務進行限流,那麼只需要新增依賴即可,不需要修改任何程式碼;
  • 如果想要對特定的方法進行限流或者降級,則需要通過 @SentinalResouce 註解來實現限流資源的定義;

4.1 註解引數屬性說明

  • @SentinelResource 註解引數屬性說明:
public @interface SentinelResource {
    //資源名稱,必需項(不能為空)
    String value() default "";
    
    //entry 型別,有 IN 和 OUT 兩個選項,(預設為 EntryType.OUT)
    EntryType entryType() default EntryType.OUT;

    int resourceType() default 0;

    //對應處理 BlockException 的函式名稱,可選項
    String blockHandler() default "";

    /** 
     * blockHandler 函式預設需要和原方法在同一個類中,如果希望使用其他類的函式,則需要指定 blockHandlerClass 為對應的類的 Class 物件,注意對應的函式必需為 static 函式,否則無法解析
    Class<?>[] blockHandlerClass() default {};
    
    /**
     * fallback 函式預設需要和原方法在同一個類中;
     * 若希望使用其他類的函式,則可以指定 fallbackClass 為對應的類的 Class 物件;
     * 對應的函式必需為 static 函式,否則無法解析;
     **/
    String fallback() default "";

    /**
     * 預設的 fallback 函式名稱,可選項,通常用於通用的 fallback邏輯(即可以用於很多服務或方法);
     * 預設 fallback 函式可以針對所有型別的異常(除了exceptionsToIgnore裡面排除掉的異常型別)進行處理;
     * 若同時配置了 fallback 和 defaultFallback,則只有 fallback會生效;
     * defaultFallback 函式簽名要求:返回值型別必須與原函式返回值型別一致;方法引數列表需要為空,或者可以額外多一個 Throwable 型別的引數用於接收對應的異常;
     * defaultFallback 函式預設需要和原方法在同一個類中。若希望使用其他類的函式,則可以指定 fallbackClass 為對應的類的 Class 物件,注意對應的函式必需為 static 函式,否則無法解析;
     **/ 
    String defaultFallback() default "";

    /**
     * fallback 函式名稱,可選項,用於在丟擲異常的時候提供  fallback 處理邏輯;
     * fallback 函式可以針對所有型別的異常(除了 exceptionsToIgnore 裡面排除掉的異常型別)進行處理;
     * fallback 函式簽名和位置要求:返回值型別必須與原函式返回值型別一致;方法引數列表需要和原函式一致,或者可以額外多一個 Throwable 型別的引數用於接收對應的異常
     **/
    Class<?>[] fallbackClass() default {};
    
    //用於指定哪些異常被排除掉,不會計入異常統計中,也不會進入 fallback 邏輯中,而是會原樣丟擲
    Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};

    Class<? extends Throwable>[] exceptionsToIgnore() default {};
}
  • @SentinelResource:處理的是 Sentinel 控制檯配置的違規情況,有 blockHandler 方法配置的兜底處理;
  • RuntimeException:如:int age = 10/0。這個是 java 執行時報出的執行時異常 RunTimeException,@SentinelResource 不管;
  • @SentinelResource 主管配置出錯,執行出錯走異常處理程式;

4.2 配置與程式碼的關係

圖形配置和程式碼關係

4.3 自定義限流處理邏輯

4.3.1 建立 CustomerBlockHandler 類用於自定義限流處理邏輯

在 handler 包下新建 CustomerBlockHandler 類;

public class CustomerBlockHandler{
    public static CommonResult handlerException(BlockException exception){
        return new CommonResult(4444,"按客戶自定義,global handlerException----1");
    }
    public static CommonResult handlerException2(BlockException exception){
        return new CommonResult(4444,"按客戶自定義,global handlerException----2");
    }
}

4.3.2 在 controller 介面上配置自定義邏輯

@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
        blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException2")
public CommonResult customerBlockHandler(){
    return new CommonResult(200,"按客戶自定義限流處理邏輯");
}

程式碼對應

  • 在實際生產中,上述所有的程式碼都要用 try-catch-finally 方式進行處理;

4.3.3 在 Sentinel 控制檯上配置

在 Sentinel 控制檯上配置

5. 手動配置流控規則

  • 我們除了能在 Sentinel 控制檯上配置流控規則外,還可以藉助 Sentinel 的 InitFunc SPI 擴充套件介面來實現:
    • 需要實現自己的 InitFunc 介面;
    • 並在 init 方法中編寫規則載入的邏輯;
  • 我們接著 3.4.3 的 controller 示例:

5.1 controller 介面

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "dealHandlerTestHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                         @RequestParam(value = "p2",required = false) String p2){
    return "testHotKey";
}
public String dealHandlerTestHotKey(String p1, String p2, BlockException exception) {
    return "dealHandler_testHotKey";
}

5.2 實現 InitFunc 介面

public class FlowRuleInitFunc implements InitFunc{
    @Override 
    public void init() throws Exception{
        List<FlowRule>rules=new ArrayList<>(); 
        FlowRule rule=new FlowRule(); 
        rule.setcount(1); 
        rule.setResource("testHotKey"); 
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS); 
        rule.setLimitApp("default"); 
        rules.add(rule); 
        FlowRuleManager.loadRules(rules);
}
  • SPI 是擴充套件點機制,如果需要被 Sentinel 載入,那麼還要在 resource 目錄下建立 META-INF/services/com.alibaba.csp.sentinelinit.InitFunc 檔案,檔案內容就是自定義擴充套件點 FlowRuleInitFunc 的全路徑;

6. Sentinel 規則持久化

  • 一旦我們重啟應用,Sentinel 規則將消失,生產環境需要將配置規則進行持久化;
  • 將限流配置規則持久化進 Nacos 儲存,只要重新整理 REST 地址,Sentinel控制檯的流控規則就能看到,只要 Nacos 裡面的配置不刪除,針對 Sentinel 上的流控規則持續有效;

6.1 新增 pom.xml 依賴檔案

<!-- nacos 配置中心 -->
<dependency>
   <groupId>com.alibaba.cloud</groupId>
   <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Sentinel 持久化相關 -->
<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

6.2 修改 bootstrap.yml 配置檔案

spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848
        file-extension: yaml
    sentinel:
      transport:
        dashboard: localhost:28080
        port: 8719
      # 以下新增  
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848  #將規則儲存進 Nacos 配置中心
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json #指定配置項的內容格式,可選:JSON、XML。如果需要自定義,則可以將值配置為 custom,並配置 converter-class 指向 converte r類;
            rule-type: flow #資料來源中規則的型別,可選:flow、degrade、param-flow、gw-flow

6.3 在 Nacos 伺服器上新增配置

  • /easytest 介面新增流控規則;

對 /easytest 介面新增流控規則

  • Resource:資源名稱;
  • LimitApp:來源應用;
  • Grade:閾值型別,0表示執行緒數,1表示 QPS;
  • Count:單機閾值;
  • Strategy:流控模式,0表示直接,1表示關聯,2表示鏈路;
  • ControlBehavior:流控效果,0表示快速失敗,1表示 Warm Up,2表示排隊等待;
  • ClusterMode:是否叢集;

6.4 一些說明

  • 訪問 Sentinel 控制檯可能看不見配置,多次呼叫 /easytest 後才能看見配置規則;
  • 在 Sentinel 控制檯上新增和修改配置不能同步到 Nacos 配置中心,服務重啟後配置規則不能同步到 Nacos 上;
  • Nacos 在這當中扮演的角色應該是一個查詢資料庫,不建議在 Nacos 上修改流控規則;

7. Sentinel 控制檯整合 Nacos 實現規則同步

  • 上面說過,在 Sentinel 控制檯上新增和修改配置不能同步到 Nacos 配置中心,服務重啟後配置規則不能同步到 Nacos 上;
  • 但我們可以自己實現這個功能,配置步驟如下:

7.1 修改依賴與配置檔案

  • 修改 sentinel-dashboard 模組裡的 pom.xml 依賴檔案;
  • 註釋掉 <scrop>:
<dependency>
  <groupId>com.alibaba.csp</groupId>
  <artifactId>sentinel-datasource-nacos</artifactId>
  <!--<scope>test</scope>-->
<dependency>
  • 修改:src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html;裡的 dashboard.flowV1 為 dashboard.flow;
  • 使之呼叫 FlowControllerV2 中的介面;
<li ui-sref-active="active" ng-if="!entry.isGateway">
  <!--<a ui-sref="dashboard.flowV1({app: entry.app})">-->
  <a ui-sref="dashboard.flow({app: entry.app})">
    <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控規則</a>
</li>
  • application.yml 裡新增 Nacos 伺服器的配置資訊:
    • sentinel.nacos.serverAddr=localhost:8848
    • sentinel.nacos.namespace=
    • sentinel.nacos.group-id=DEFAULT_GROUP

7.2 新建 Nacos 規則包,裡面存放與同步的配置類

  • 新建包 com/alibaba/csp/sentinel/dashboard/rule/nacos/
  • 下面新建五個類,可以從這個目錄下拷貝 src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos
  • 新建後的圖:

新建五個類

7.2.1 新建 NacosPropertiesConfiguration 類

  • 在該包下新建 NacosPropertiesConfiguration.java 類,用來載入外部化配置;
@ConfigurationProperties(prefix="sentinel.nacos")
public class NacosPropertiesConfiguration {
    private String serverAddr;
    private String dataId;
    private String groupId = "DEFAULT_GROUP";
    private String namespace;
    //這裡省略 get/set 方法
}    

7.2.2 新建 NacosConfiguration 類

  • 在該包下建立一個 Nacos 配置類 NacosConfiguration
@EnableConfigurationProperties(NacosPropertiesConfiguration.class)
@Configuration
public class NacosConfiguration {

    //Converter 轉換器,將 FlowRuleEntity 轉化成 FlowRule,以及反向轉化
    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder(){
        return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder(){
        return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    //注入 Nacos 服務 ConfigService
    @Bean
    public ConfigService nacosConfigService(NacosPropertiesConfiguration nacosPropertiesConfiguration) throws NacosException {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, nacosPropertiesConfiguration.getServerAddr());
        properties.put(PropertyKeyConst.NAMESPACE, nacosPropertiesConfiguration.getNamespace());
        return ConfigFactory.createConfigService(properties);
    }
}

7.2.3 新建 NacosConstants 類

  • 在該包下建立一個 Nacos 常量類 NacosConstants。分別表示預設的 GROUP_ID 和 DATA_ID 的字尾:
public class NacosConstants {
    public static final String DATA_ID_POSTFIX = "-sentinel-flow";
    public static final String GROUP_ID = "DEFAULT_GROUP";
}

7.2.4 新建 FlowRuleNacosProvider 類

  • 在該包下新建 FlowRuleNacosProvider 類實現動態從 Nacos 配置中心獲取流控規則;
@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {

    private static Logger logger = LoggerFactory.getLogger(FlowRuleNacosProvider.class);

    @Autowired
    private NacosPropertiesConfiguration nacosConfigProperties;

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<String, List<FlowRuleEntity>> converter;

    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        String dataID = new StringBuilder(appName).append(NacosConstants.DATA_ID_POSTFIX).toString();
        //通過ConfigServic.getConfig方法從Nacos Config Server中讀取指定配置資訊,並通過converter轉化為FlowRule規則
        String rules = configService.getConfig(dataID, nacosConfigProperties.getGroupId(), 3000);
        if (StringUtil.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}

7.2.5 新建 FlowRuleNacosPublisher 類

  • 在該包下新建 FlowRuleNacosPublisher(流控規則釋出類) 類,在 Sentinel 控制檯 上修改完配置之後,需要呼叫該釋出方法將資料持久化到 Nacos 中;
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {

    @Autowired
    private NacosPropertiesConfiguration nacosPropertiesConfiguration;

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<List<FlowRuleEntity>, String> converter;

    @Override
    public void publish(String appName, List<FlowRuleEntity> rules) throws Exception {
        AssertUtil.notEmpty(appName, "appName cannot be empty");
        if (rules == null) {
            return;
        }
        String dataID = new StringBuilder(appName).append(NacosConstants.DATA_ID_POSTFIX).toString();
        configService.publishConfig(dataID, nacosPropertiesConfiguration.getGroupId(), converter.convert(rules));
    }
}

7.3 修改 FlowControllerV2 類

  • 上面配置的兩個類(FlowRuleNacosPublisher 與 FlowRuleNacosProvider)注入進來,表示規則的拉取和規則的釋出統一用我們前面自定義的兩個例項;

修改 FlowControllerV2 類

7.4 修改 Nacos 客戶端

  • 對於應用程式來說,需要改動的地方比較少,只要注意配置檔案中 data-id 的命名要以 -sentinel-flow 結尾即可,因為在 Sentinel Dashboard 中我們寫了一個固定的字尾;
spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 
      config:
        server-addr: localhost:8848
        file-extension: yaml
    sentinel:
      transport:
        dashboard: localhost:28080 
        port: 8719
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848 
            # 修改下面這條
            dataId: ${spring.appliction.name}-sentinel-flow   
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

8. 自定義 URL 限流異常和 URL 資源清洗

8.1 自定義 URL 限流異常

8.1.1 問題描述

  • 在預設情況下,URL 觸發限流後會直接返回 Blocked by Sentinel (flow 1imiting)
  • 在實際應用中,大都採用JSON格式的資料,所以如果希望修改觸發限流之後的返回結果形式;

8.1.2 實現 UrlBlockHandler 並且重寫 blocked() 方法

  • 可以通過自定義限流異常來處理,實現 UrlBlockHandler 並且重寫 blocked 方法;
@Service public class CustomurlBlockHandler implements Ur1BlockHandler{
    @Override 
    public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpservletResponse, BlockException e)throws IOException{
    httpServletResponse.setHeader("Content-Type","application/json;charset=UTF-8");
    String message="{\"code\":999,\"msg\":\"訪問人數過多\"}";
    httpServletResponse.getWriter().write(message);
}
  • 如果觸發限流之後,我們希望直接跳轉到一個降級頁面,可以通過下面這個配置來實現:spring.cloud.sentinel.servlet.block-page-{url}

8.2 URL 資源清洗

8.2.1 問題描述

  • 預設情況下 Sentinel 會把所有的 URL 當作資源來進行流控;
  • 比如一個 URL /clean/{id} 帶有一個引數 id,這個引數有很多種屬性 {id可取整數},有多少個請求 Sentinel 預設統計多個,而我們期望是一個;

8.2.2 實現 UrICleaner 並重寫 clean() 方法

  • 對於 /clean/{id} 這個 URL,我們可以統一歸集到 /clean/* 資源下:
@Service 
public class CustomerUrlCleaner implements UrlCleaner{
    @Override 
    public String clean(String originurl){
        if(Stringutils.isEmpty(originur1)){
            return originUrl;
    }
    if(originUr1.startswith("/clean/")){
        return"/clean/*";
    }
    return originUrl;
}

最後

新人制作,如有錯誤,歡迎指出,感激不盡!
歡迎關注公眾號,會分享一些更日常的東西!
如需轉載,請標註出處!
微服務架構 | 5.2 基於 Sentinel 的服務限流及熔斷

相關文章