9.Spring Cloud Alibaba Sentinel流控熔斷元件

弓長日含發表於2020-12-02

Sentinel

Sentinel 是面向分散式服務架構的高可用防護元件,主要以流量為切入點,從流量控制、熔斷降級、系統自適應保護等多個維度來幫助使用者保障微服務的穩定性。

GitHub:Sentinel

官網:Sentinel

Sentinel 功能和設計理念
  • 流量控制

    任意時間到來的請求往往是不可控的,而系統的處理能力是有限的。這就需要根據系統的能力對流量進行控制。

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

    • 資源的呼叫關係(資源的呼叫鏈路,資源與資源之間的關係等)
    • 執行指標(QPS、執行緒池、系統負載等)
    • 控制的效果(直接限流、冷啟動、排隊等)

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

  • 熔斷降級

    Sentinel和Hystrix的原則是一致的:當呼叫鏈路中某個資源不穩定,則對這個資源的呼叫進行限制,並讓請求快速失敗,避免影響其他的資源,導致請求發生堆積,產生雪崩效應。

    在限制手段上,Hystrix通過執行緒池的方式,使資源和資源之間做到了徹底的隔離。缺點是增加了執行緒切換的成本,還需預先給各個資源做執行緒池大小分配。

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

    • 通過併發數進行限制

      當執行緒數在特定資源上堆積到一定數量,對該資源的新請求會被拒絕,堆積的執行緒完成任務後才開始繼續接收請求。

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

      當依賴的資源響應時間過長,所有對該資源的訪問都被直接拒絕,直到過了指定的時間視窗之後才恢復。

  • 系統負載保護

    當系統負載較高的時候,如果還持續讓請求進入,可能會導致系統崩潰,無法響應。在叢集環境下,網路負載會把本應這臺機器承載的流量轉發到其他機器上,如果此時其他機器也處在一個邊緣狀態,增加的流量就會導致這臺機器也崩潰,最終導致整個叢集不可用。針對這個情況,Sentinel提供了對應的保護機制,讓系統的入口流量和系統的負載達到一個平衡,保證系統在能力範圍內處理最多的請求。

Sentinel 的主要工作機制如下:

  • 對主流框架提供適配或者顯示的 API,來定義需要保護的資源,並提供設施對資源進行實時統計和呼叫鏈路分析。
  • 根據預設的規則,結合對資源的實時統計資訊,對流量進行控制。同時,Sentinel 提供開放的介面,方便您定義及改變規則。
  • Sentinel 提供實時的監控系統,方便您快速瞭解目前系統的狀態。

關於更加詳細的介紹,這裡就不在介紹了,直接去官網檢視官方文件,以及GitHub上面給的例子即可。

demo:sentinel-demo

文件:Sentinel使用者文件

上手使用

  1. 準備工作

    上篇部落格使用了Spring Cloud Alibaba Nacos作為服務註冊中心,並在Idea中使用Alibaba Cloud Tookit外掛建立專案。這裡沿用昨天的book-service服務,再新建student-service服務,並使用OpenFeign呼叫book-service的服務。關於OpenFeign宣告式服務呼叫在前面部落格也講解過,直接使用即可。引入OpenFeign依賴

    <dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    

    先在book-service中編寫demo

    在這裡插入圖片描述

    在student-service中編寫demo,並使用OpenFeign呼叫book-service中的服務。這裡要在啟動類新增@EnableFeignClients

    在這裡插入圖片描述

    在Nacos中確認兩個服務均已註冊。

    在這裡插入圖片描述

    補充一下:這裡為啥啟動類中沒有加@EnableDiscoveryClient?我們在檢視生成的專案中,有一個nacosdiscovery包中有一個NacosDiscoveryConfiguration,該類中新增了@EnableDiscoveryClient註解

    在這裡插入圖片描述

    執行student-service呼叫book-service服務

    在這裡插入圖片描述

  2. 安裝Sentinel Dashboard

    下載地址:Sentinel Dashboard

    下載完成以後,可以在本地執行。java -jar sentinel-dashboard-1.8.0.jar。啟動以後發現Sentinel Dashboard的預設埠為8080,佔用了別的埠,直接修改啟動埠:java -jar -Dserver.port=8888 sentinel-dashboard-1.8.0.jar

    在這裡插入圖片描述

    直接訪問ip+port 訪問(使用者名稱密碼均為sentinel)

    在這裡插入圖片描述

    這裡面暫時是沒有服務列表的。需要在服務中進行配置登記。
    在這裡插入圖片描述

  3. 服務呼叫方(student-service)新增Sentinel依賴。完整核心pom.xml

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.4.RELEASE</spring-boot.version>
        <spring-cloud-alibaba.version>2.2.2.RELEASE</spring-cloud-alibaba.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    

    在配置檔案中新增配置:

    spring:
      application:
        name: student-service
    
      cloud:
        nacos:
          discovery:
            username: nacos
            password: nacos
            server-addr: localhoost:8848
        sentinel:
          transport:
            dashboard: localhost:8888
    

    啟動專案呼叫服務後,重新整理Sentinel Dashboard頁面。

    在這裡插入圖片描述

    我們可以在這裡設定流控、降級。根據不同的情況設定。

    設定流控

    在這裡插入圖片描述

    設定降級

    在這裡插入圖片描述

    這裡簡單驗證:設定流控

    在這裡插入圖片描述

    頻繁重新整理以後

    在這裡插入圖片描述

    補充:以student-service為例,微服務如何與Sentinel Dashboard通訊?

    在微服務中引入Sentinel依賴以後,微服務中有一個sentinel-transport-simple-http客戶端,該客戶端預設在主機上開啟一個新的埠(並不會使用微服務的埠),預設從8719開始,如果埠被佔用,依次+1再次嘗試。

    在這裡插入圖片描述

    sentinel-transport-simple-http客戶端會獲取微服務的狀態等資訊,進行健康檢查(預設10秒向Sentinel Dashboard傳送心跳包通知健康狀態)。而如果我們在Sentinel Dashboard中設定了微服務的流控,降級,則Sentinel Dashboard會呼叫sentinel-transport-simple-http客戶端中提供的API進行設定。

    從Sentinel Dashboard中我們看到微服務student-service中的sentinel-transport-simple-http埠為8720(8719已經被佔用了)。我們直接訪問ip + port + /api

    在這裡插入圖片描述
    在這裡插入圖片描述

  4. 上面的方法是在Sentinel Dashboard中設定流控,降級規則,我們也可以在程式碼中實現:

    流量規則

    Field說明預設值
    resource資源名,資源名是限流規則的作用物件
    count限流閾值
    grade限流閾值型別,QPS 或執行緒數模式QPS 模式
    limitApp流控針對的呼叫來源default,代表不區分呼叫來源
    strategy呼叫關係限流策略:直接、鏈路、關聯根據資源本身(直接)
    controlBehavior流控效果(直接拒絕 / 排隊等待 / 慢啟動模式),不支援按呼叫關係限流直接拒絕
    private void initFlowRule(){
    	List<FlowRule> rules = new ArrayList<>();
    	FlowRule rule = new FlowRule();
    	//設定資源名稱
    	rule.setResource("/student/info/{stuNo}/{id}");
    	//設定限流閾值
    	rule.setCount(100);
    	//設定限流閾值型別
    	rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    	//設定策略
    	rule.setStrategy(RuleConstant.STRATEGY_DIRECT);
    	//設定流控效果 設為預熱
    	rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
    	//設定預熱時間
    	rule.setWarmUpPeriodSec(10);
    	//設定呼叫來源
    	rule.setLimitApp("default");
    	rules.add(rule);
    	//定義流量控制規則
    	FlowRuleManager.loadRules(rules);
    }
    

    熔斷降級規則

    Field說明預設值
    resource資源名,即規則的作用物件
    grade熔斷策略,支援慢呼叫比例/異常比例/異常數策略慢呼叫比例
    count慢呼叫比例模式下為慢呼叫臨界 RT(超出該值計為慢呼叫);異常比例/異常數模式下為對應的閾值
    timeWindow熔斷時長,單位為 s
    minRequestAmount熔斷觸發的最小請求數,請求數小於該值時即使異常比率超出閾值也不會熔斷(1.7.0 引入)5
    statIntervalMs統計時長(單位為 ms),如 60*1000 代表分鐘級(1.8.0 引入)1000 ms
    slowRatioThreshold慢呼叫比例閾值,僅慢呼叫比例模式有效(1.8.0 引入)
    private static void initDegradeRule() {
    	List<DegradeRule> rules = new ArrayList<>();
    	DegradeRule rule = new DegradeRule("/student/info/{stuNo}/{id}");
    	rule.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType())
    		.setCount(0.7)
    		.setMinRequestAmount(100)
    		.setStatIntervalMs(30000)
    		.setTimeWindow(10);
    	rules.add(rule);
    	DegradeRuleManager.loadRules(rules);
    }
    

    系統保護規則

    Field說明預設值
    highestSystemLoadload1 觸發值,用於觸發自適應控制階段-1 (不生效)
    avgRt所有入口流量的平均響應時間-1 (不生效)
    maxThread入口流量的最大併發數-1 (不生效)
    qps所有入口資源的 QPS-1 (不生效)
    highestCpuUsage當前系統的 CPU 使用率(0.0-1.0)-1 (不生效)
    private void initSystemProtectionRule() {
      	List<SystemRule> rules = new ArrayList<>();
      	SystemRule rule = new SystemRule();
      	rule.setHighestSystemLoad(1.0);
      	rules.add(rule);
      	SystemRuleManager.loadRules(rules);
    }
    

    更多講解,參見文件:基本使用 - 資源與規則

  5. Sentinel異常處理

    通過之前的測試,從頁面返回的資訊檢視不夠完整。

    在這裡插入圖片描述

    這裡新建異常處理類,實現BlockExceptionHandler

    /**
     * 自定義Sentinel異常返回資訊
     */
    @Component
    public class BlockHandler implements BlockExceptionHandler {
    
        @Override
        public void handle(HttpServletRequest httpServletRequest,
                           HttpServletResponse httpServletResponse, BlockException e) throws Exception {
            String message = null;
            if(e instanceof FlowException){
                message = "請求已被限流!";
            }
            if(e instanceof DegradeException){
                message = "熔斷!";
            }
            if(e instanceof ParamFlowException){
                message = "熱點資料限流";
            }
            if(e instanceof SystemBlockException){
                message = "系統規則異常";
            }
            if(e instanceof AuthorityException){
                message = "授權規則異常";
            }
            httpServletResponse.setStatus(500);
            httpServletResponse.setCharacterEncoding("utf-8");
            httpServletResponse.setContentType("application/json");
            //使用ObjectMapper將物件轉換成字串
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            objectMapper.writeValue(httpServletResponse.getWriter(),new Result(500,message));
        }
    }
    

    補充:統一返回結果類

    public class Result<T> {
    
        private Integer code;
        private String message;
        private T data;
    
        public Result(){}
    
        public Result(T data){
            this.code = 200;
            this.message = "success";
            this.data = data;
        }
    
        public Result(Integer code, T data) {
            this.code = code;
            this.data = data;
        }
    
        public Result(Integer code, String message, T data) {
            this.code = code;
            this.message = message;
            this.data = data;
        }
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    }
    

    設定限流規則以後再次嘗試重新整理

    在這裡插入圖片描述

  6. Sentinel與OpenFeign整合

    配置檔案中新增配置

    feign:
      sentinel:
        enabled: true
    

    編寫fallback回撥方法
    在這裡插入圖片描述

    @FeignClient(name = "book-service",fallbackFactory = BookServiceFallback.class)
    public interface BookService {
    
        @GetMapping("/book/info/{id}")
        Result<Book> bookMessage(@PathVariable("id")String id);
    }
    

    回撥
    在這裡插入圖片描述

    @Component
    public class BookServiceFallback implements FallbackFactory<BookService> {
    
        @Override
        public BookService create(Throwable throwable) {
            return new BookService() {
                @Override
                public Result<Book> bookMessage(String id) {
                    return new Result<>(500,throwable.getMessage(),null);
                }
            };
        }
    }
    

    劃重點:

    • 回撥方法實現FallbackFactory介面並新增對應的泛型。
    • 實現create方法,編寫回撥邏輯。
    • 新增@Component註解
  7. Sentinel在Nacos配置中心配置持久化

    在上述進行試驗驗證的過程中,會發現專案每次重啟以後,設定的限流策略就失效了,需要重新設定。Nacos作為配置中心的講解,後續在部落格中更新。

    訪問Nacos地址,在配置管理類-》配置列表中新建配置

    在這裡插入圖片描述

這裡的值與規則列表中的值一致。
在這裡插入圖片描述

引入依賴

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

配置檔案中新建配置

在這裡插入圖片描述

完整配置檔案

server:
  port: 8050


spring:
  application:
    name: student-service

  cloud:
    nacos:
      discovery:
        username: nacos
        password: nacos
        server-addr: localhost:8848

    sentinel:
      transport:
        dashboard: localhost:8888

      datasource:
        flow-bs: #自定義名稱
          nacos:
            server-addr: localhost:8848
            username: nacos
            password: nacos
            dataId: book-service-rule #data id
            groupId: DEFAULT_GROUP #分組
            ruleType: flow #flow代表流控規則,degrade代表熔斷規則

feign:
  sentinel:
    enabled: true

同理配置熔斷規則與限流規則一樣操作即可(在Nacos中新建配置)。
在配置檔案中新增配置:
在這裡插入圖片描述

驗證:

重啟專案,呼叫服務後,直接在Sentinel Dashboard流控規則中可看到。
在這裡插入圖片描述

相關文章