spring cloud alibaba sentinel 運行及簡單使用


1.官網

英文:https://github.com/alibaba/Sentinel

中文:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

文檔:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel

 

2.簡要說明

  Sentinel是阿里開源的項目,提供了流量控制、熔斷降級、系統負載保護等多個維度來保障服務之間的穩定性。2012年,Sentinel誕生於阿里巴巴,其主要目標是流量控制。2013-2017年,Sentinel迅速發展,並成為阿里巴巴所有微服務的基本組成部分。 它已在6000多個應用程序中使用,涵蓋了幾乎所有核心電子商務場景。2018年,Sentinel演變為一個開源項目。2020年,Sentinel Golang發布。更詳細的還是看官網最好。

  

3.sentinel的組成

  Sentinel 的使用可以分為兩個部分:

  • 控制台(Dashboard):控制台主要負責管理推送規則、監控、集群限流分配管理、機器發現等。

  • 核心庫(Java 客戶端):不依賴任何框架/庫,能夠運行於 Java 7 及以上的版本的運行時環境,同時對 Dubbo / Spring Cloud 等框架也有較好的支持

4.sentinel的下載

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

  這里選擇的1.7.0版本

  

 

 

 4.運行

  下載下來是一個jar包,需要JDK1.8環境

   執行命令 java -jar sentinel-dashboard-1.7.0.jar

 

   默認端口是8080,注意端口沖突問題

5.訪問sentinel控制台

  http://localhost:8080/#/login

  

   默認賬號密碼都是sentinel

  出現此界面,表示sentinel下載運行成功

6.使用sentinel來保護項目

  上面,我們運行sentinel成功,下面,我們在項目中做一個最簡單的使用。注冊中心使用的nacos。

6.1創建一個模塊

  

 6.2依賴

  nacos依賴和sentinel依賴

  <!--服務注冊-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

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

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

完整

    <dependencies>

        <!--服務注冊-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

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

        <!--sentinel-->
        <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-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>

6.3配置文件

 

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service  #將會作為注冊到注冊中心的服務名稱
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos注冊中心的地址
    sentinel:
      transport:
        dashboard: localhost:8080     #sentinel控制台地址
        port: 8719 #默認8719,假如被占用了會自動從8719開始依次+1掃描。直至找到未被占用的端口
management:
  endpoints:
    web:
      exposure:
        include: '*'

這里,配置了nacos注冊中心地址,配置了sentinel的相關配置

6.4主啟動類

 

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @Classname PaymentMain9001
 * @Description TODO
 * @Date 2021/6/18 0018 下午 2:20
 * @Created by jcc
 */
@SpringBootApplication
@EnableDiscoveryClient
public class MainApp8401 {
    public static void main(String[] args) {
        SpringApplication.run(MainApp8401.class,args);
    }
}

 

6.5業務類

   業務類提供兩個簡單的接口

package com.atguigu.springcloud.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Classname FlowLimitController
 * @Description TODO
 * @Date 2021/6/24 0024 下午 4:11
 * @Created by jcc
 */

@RestController
public class FlowLimitController
 {
    @GetMapping("/testA")
    public String testA() {
        return "------testA";
     }
     
     @GetMapping("/testB")
    public String testB() {
       return "------testB";
    }
 }

 

6.6啟動

 啟動nacos,啟動sentinel,啟動該模塊,登錄sentinel控制台

  訪問testA和testB接口

 刷新 sentinel控制台

  

   此時,我們可以看到sentinel控制台出現了testA和testB。

  左側菜單提供了很多的功能,這個后面再一一講解

 7.流量控制

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

  流量控制可以對特定的請求資源進行流量控制,設置相應的規則,當訪問量超出規則時,會對這個請求進行流量控制,直接返回。

  下圖是sentinel控制台-流量控制頁面

 

 選擇流控規則,點擊新增流控規則,點擊高級選項

 

 7.1流控規則內容說明

   1)資源名

      唯一名稱,默認請求路徑。也可以通過注解配置

    2)針對來源

      針對哪個調用者進流量控制,填寫服務名稱即可,默認default-不區分來源,針對所有調用者

    3)閥值類型

      QPS:每秒請求數,但調用量大於這個值的時候,進行限流

      線程數:處理該請求的線程數,處理該API的線程數大於這個值的時候,進行限流

    4)是否集群

    5)流控模式

      直接:達到閥值時,直接限流

      關聯:本請求達到閥值,對和他相關的請求進行限流

      鏈路:只記錄指定鏈路上的流量,如果達到閥值,則限流。指定鏈路,比如/testA有很多地方調用了,我只限制testB對他的調用為1秒最多調用1次,其它的隊testA的調用則不管

    6)流控效果-閥值類型QPS才有這個選項

      快速失敗:達到閥值,直接失敗

      Warm Up:預熱。

      排隊等待:允許排隊,讓請求勻速通過

 

下面來一一測試不同的配置的效果

 

7.1.1流量控制-測試1

  對/testA進行流量控制

  閥值類型為QPS

  每秒請求書限制為1個

  流控模式為直接

  流控效果為快速失敗

  也就是設置testA1秒內訪問量限制為1次,超過直接返回

 

   添加成功后,訪問testA這個請求

   1)訪問方式:瀏覽器直接訪問一次

    成功

    

 

  2)訪問方式:一秒內快速訪問兩次

  第一次請求成功,第二次請求失敗,sentinel返回錯誤信息,這個錯誤信息是sentinel自帶的,如何自定義返回信息,后面再說

  

  因為我們設置了1秒內訪問一次的閥值,這里超過了閥值,所以被限流了

7.1.2流量控制-測試

  對/testA進行流量控制

  閥值類型為線程數

  線程數限制為1

  流控模式為直接

  流控效果無法選擇

  也就是設置testA訪問線程數為1,超出直接返回

   

 

   直接對上面的規則編輯就可以,編輯完成后,立即生效

  

  訪問testA這個請求

   1)訪問方式:瀏覽器1秒內直接訪問一次或者多次

    成功

    這就是線程數和QPS的區別,QPS是對請求數進行限制,線程數是對線程進行限制,只提供1個線程來處理這個請求,不管請求數多少,只要這個線程處理的過來,就會處理

    

  2)訪問方式:在瀏覽器1秒內快速訪問這個請求一次

    對接口testA做一點點修改,讓他等待2s  

 
         
@GetMapping("/testA")
public String testA() throws InterruptedException {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName());
return "------testA";
}
 

    

   3)訪問方式:在瀏覽器1秒內快速訪問這個請求多次

   

   第二次立馬返回失敗信息,因為我們設置了1個線程的閥值,超過這個閥值,就被限流了

 

7.1.3流量控制-測試

  對/testA進行流量控制

  閥值類型為QPS

  單機閥值限制為1

  流控模式為關聯-關聯對象為/testB

  流控效果快速失敗

  當testB超出閥值時,testA將被限制

  設置testB訪問1秒限制1次,超過的話限制testA,直接返回

  

 

   1)瀏覽器分別訪問testA 和testB一次

     把testA睡眠2秒的代碼去掉

  

    @GetMapping("/testA")
    public String testA() throws InterruptedException {
        System.out.println(Thread.currentThread().getName());
        return "------testA";
     }


     @GetMapping("/testB")
    public String testB() {
       return "------testB";
    }

 

    成功

    

 

 

 

    2)瀏覽器1秒內訪問testB多次,再訪問testA

    

    

  當testB的訪問超過設定的閥值1秒1次的訪問量時,testA被限流

 

 7.1.4流量控制-測試 

  對/testA進行流量控制

  閥值類型為QPS

  單機閥值限制為1

  流控模式為鏈路

  流控效果快速失敗

  對testB調用testA進行限制,1秒內testB最多允許調用testA1次,超多則限流,直接返回。其它的對testA的調用不做限制

  

 

 7.1.5流量控制-測試 

  對/testA進行流量控制

  閥值類型為QPS

  單機閥值限制為6

  流控模式為直接

  流控效果快速失敗

   預熱時長為5秒

  這里對testA進行限流,閥值為每秒6次,流控效果為warm up,時長為5秒。這里就有一個過程,最開始的閥值不是6,而是閥值6除以冷加載因子(默認為3),也就是說剛開始閥值是2,當請求量進來超過2時,超過的請求被限制直接返回。經過5秒后,閥值變為6。這種模式適合秒殺這種,訪問量平常很低或者沒有,某一刻突然大量的訪問。這樣保護服務,不讓它被突然的大量訪問壓死。

  

 

   1)瀏覽器訪問testA一次

  成功

  

   2)快速訪問testA,每秒大於2次,連續訪問8秒

    在前5秒的訪問中,每秒最多只能訪問兩次,超過的被限制直接返回,過了5秒后,每秒訪問量不超過6次,均可以返回。說明前5秒閥值是2,超過5秒后,閥值變為6了

 

 7.1.6流量控制-測試 

  對/testA進行流量控制

  閥值類型為QPS

  單機閥值限制為2

  流控模式為直接

  流控效果排隊等待

  超時時間為1秒

  這里限制testA每秒2次的訪問量,超過的,排隊等待,等待超時時間為1秒

  

 

  1)測試,使用測試軟件發送請求,發送10次請求,間隔時間為0.1秒

 

@GetMapping("/testA")
    public String testA() throws InterruptedException {
        System.out.println(System.currentTimeMillis() + "---" + Thread.currentThread().getName());
        return "------testA";
     }

控制台打印結果,每次打印時間間隔大致為0.5秒,說明請求以0.5秒一次的速度通過

1624867018539---http-nio-8401-exec-2
1624867019039---http-nio-8401-exec-1
1624867019537---http-nio-8401-exec-4
1624867020037---http-nio-8401-exec-5
1624867020539---http-nio-8401-exec-6
1624867021037---http-nio-8401-exec-7
1624867021538---http-nio-8401-exec-8
1624867022037---http-nio-8401-exec-9
1624867022538---http-nio-8401-exec-10
1624867023039---http-nio-8401-exec-3

 

 

 

 8.熔斷降級

   https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7

   除了流量控制以外,對調用鏈路中不穩定的資源進行熔斷降級也是保障高可用的重要措施之一。一個服務常常會調用別的模塊,可能是另外的一個遠程服務、數據庫,或者第三方 API 等。例如,支付的時候,可能需要遠程調用銀聯提供的 API;查詢某個商品的價格,可能需要進行數據庫查詢。然而,這個被依賴服務的穩定性是不能保證的。如果依賴的服務出現了不穩定的情況,請求的響應時間變長,那么調用服務的方法的響應時間也會變長,線程會產生堆積,最終可能耗盡業務自身的線程池,服務本身也變得不可用

      現代微服務架構都是分布式的,由非常多的服務組成。不同服務之間相互調用,組成復雜的調用鏈路。以上的問題在鏈路調用中會產生放大的效果。復雜鏈路上的某一環不穩定,就可能會層層級聯,最終導致整個鏈路都不可用。因此我們需要對不穩定的弱依賴服務調用進行熔斷降級,暫時切斷不穩定調用,避免局部不穩定因素導致整體的雪崩。熔斷降級作為保護自身的手段,通常在客戶端(調用端)進行配置。

 

 

 

8.1策略

  

 

   

 

     1)RT:1秒內,請求數目大於最小請求數目(默認為5),且平均的響應時長大於設置的時長,服務熔斷降級,時間窗口指的是熔斷的時間

      

    2)異常比例:1秒內,請求數目大於最小請求數目(默認為5),異常比例大於設置比例,服務熔斷降級,時間窗口指的是熔斷的時間

      

    3)異常數:1分鍾內,請求數大於最小請求數目(默認5),且請求異常數大於設置的異常數,服務熔斷降級,時間窗口指的是熔斷的時間

 

     

 

  8.熱點參數限流

  https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81

    前面流量控制是針對某個請求。這里可以進行進一步的細化。設置當某個請求附帶哪個參數的時候,才進行限流。

   

 

 

 

  8.1寫一個帶參數接口

  接口定義了兩個參數,id和name

  

     @GetMapping("/testD")
     @SentinelResource(value = "testD")
     public String testD(@RequestParam(value = "id",required = false) String id, @RequestParam(value = "name",required = false) String name) {
         return "------testD-----id=" + id + "----name=" + name ;
     }

 

  注意,這里使用到了@SentinelResource注解,這個注解這里value屬性是定義資源名字

 

  8.2配置規則

  配置對testD進行限流,只有當他帶有第一個參數id時,才進行限流,每秒限流1次訪問。注意,這里使用的資源名是testD(@SentinelResource注解配置的資源名),而不是/testD(資源路徑)。因為我發現不使用@SentinelResource注解定義資源名字,直接使用路徑配置,不生效。

 

 

1)測試1

    瀏覽器1秒內訪問testD接口多次,不帶參數,每次都能成功返回

 2)測試2

         瀏覽器1秒內訪問testD接口多次,攜帶參數name,每次都能成功返回

 3)測試3

         瀏覽器1秒內訪問testD接口多次,攜帶參數id,限流

 

8.3自定義限流時返回信息

    上面限流直接返回的是錯誤頁面,不友好,可以使用@SentinelResource設置返回信息

    在@SentinelResource注解添加了屬性blockHandler,這個屬性就是指當限流時,調用哪個方法去處理

     @GetMapping("/testD")
     @SentinelResource(value = "testD",blockHandler = "myResult")
     public String testD(@RequestParam(value = "id",required = false) String id, @RequestParam(value = "name",required = false) String name) {
         return "aaaa------testD-----id=" + id + "----name=" + name ;

     }

  限流時處理的方法,方法名是blockHandler屬性,該方法和上面testD的返回值類型需要一樣,參數列表需要和testD一樣,且需要多一個參數BlockException

public String myResult(String id, String name, BlockException exception){
        return "限流了a";

     }

    此時,在1秒內返回多次,頁面返回的就是處理方法的返回值

    

   8.4高級設置-設置參數值

  

           

 

         現在的規則是,請求testD帶有參數id且id不等於1的時候,1秒限制1次訪問,當id為1的時候,限制1秒訪問5次

      1)測試

        1秒內訪問testD兩次,id不等於1,第二次就被限流了

      

    2)測試

      1秒內訪問testD兩次,id=1,成功

    

     3)測試

    1秒內訪問testD多次(大於5),id=1,超過5次時,限流

 

  9.系統自適應限流

    前面講的限流都是針對某一個資源的,如果要針對整個系統,可以配置系統規則。

    

   

 

     

     這里具體的配置例子就不做了。

10.注解@SentinelResource

     上面用到了它的兩個屬性

    value:資源名

    blockHandler:處理方法

    下面會用到它的CustomerBlockHandler屬性

    下面有兩個接口testC和testD,他們的處理方法分別為myResult1myResult2,處理方法和業務代碼在一起

@GetMapping("/testC")
     @SentinelResource(value = "testC",blockHandler = "myResult1")
     public String testC(@RequestParam(value = "id",required = false) String id, @RequestParam(value = "name",required = false) String name) {
         return "------testC-----id=" + id + "----name=" + name ;
     }

     @GetMapping("/testD")
     @SentinelResource(value = "testD",blockHandler = "myResult2")
     public String testD(@RequestParam(value = "id",required = false) String id, @RequestParam(value = "name",required = false) String name) {
         return "aaaa------testD-----id=" + id + "----name=" + name ;

     }

     public String myResult1(String id, String name, BlockException exception){
         return "testC限流了";

     }

     public String myResult2(String id, String name, BlockException exception){
         return "testD限流了";

     }

   把處理方法分離出去

1)創建一個類,名字隨便取,把兩個處理方法移到里面去 

 方法必須是靜態

package com.atguigu.springcloud.controller;

import com.alibaba.csp.sentinel.slots.block.BlockException;

/**
 * @Classname MyHandler
 * @Description TODO
 * @Date 2021/6/29 0029 下午 4:38
 * @Created by jcc
 */
public class CustomerBlockHandler {
    public static String myResult1(String id, String name, BlockException exception){
        return "myhandler-testC限流了";
    }

    public static String myResult2(String id, String name, BlockException exception){
        return "myhandler-testD限流了";

    }
}

 

2)@SentinelResource添加屬性blockHandlerClass

  這樣子處理方法就可以先找到剛才創建的類CustomerBlockHandler .class,再找到里面的具體的處理方法。這樣子,處理方法和業務就分開了。

     @GetMapping("/testC")
     @SentinelResource(value = "testC",blockHandlerClass = CustomerBlockHandler.class ,blockHandler = "myResult1" )
     public String testC(@RequestParam(value = "id",required = false) String id, @RequestParam(value = "name",required = false) String name) {
        return "------testC-----id=" + id + "----name=" + name ;
    }


     @GetMapping("/testD")
     @SentinelResource(value = "testD",blockHandlerClass = CustomerBlockHandler.class ,blockHandler = "myResult2")
     public String testD(@RequestParam(value = "id",required = false) String id, @RequestParam(value = "name",required = false) String name) {
         return "aaaa------testD-----id=" + id + "----name=" + name ;

     }

 

下面我們介紹下fallback屬性和blockHandler屬性的區別

fallback是對系統runtimeexception作出處理,blockHandler是對違反sentinel配置的一系列規則作出處理

我創建兩個服務提供者9003和9004,一個服務調用者84

10.1創建服務提供者9003

10.1.1創建模塊

  

10.1.2依賴

 <dependencies>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>

10.1.4配置文件

 

server:
  port: 9003

spring:
  application:
    name: nacos-payment-provider  #將會作為注冊到注冊中心的服務名稱
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'

 

10.1.5啟動類

@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9003.class,args);
    }
}

 

10.1.6業務類

 

@RestController
public class PaymentController
        {
            @Value("${server.port}")
            private String serverPort;

            public static HashMap<Long, Payment> hashMap = new HashMap<>();
            static{
                hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181"));
                hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));
                hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
            }

            @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
                Payment payment = hashMap.get(id);
                CommonResult<Payment> result = new CommonResult(200,"from mysql,serverPort:  "+serverPort,payment);
                return result;
            }



        }

 

10.2 服務提供者9004的創建和9003除了端口都一樣

10.3服務消費者84的創建  

 10.3.1創建模塊

 

 10.3.2 依賴

    <dependencies>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </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>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>

10.3.3配置文件

 

server:
  port: 84

spring:
  application:
    name: nacos-order-consumer  #將會作為注冊到注冊中心的服務名稱
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址
    sentinel:
      transport:
        dashboard: localhost:8080     #sentinel控制台地址
        port: 8719 #默認8719,假如被占用了會自動從8719開始依次+1掃描。直至找到未被占用的端口
service-url:
  nacos-user-service: http://nacos-payment-provider

10.3.4主啟動類

 

@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain84 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain84.class,args);
    }
}

 

10.3.5業務類

@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

 

package com.atguigu.springcloud.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;


@RestController
@Slf4j
public class CircleBreakerController {
           
            public static final String SERVICE_URL = "http://nacos-payment-provider";

            @Resource
            private RestTemplate restTemplate;

            @RequestMapping("/consumer/fallback/{id}")
            //@SentinelResource(value = "fallback") //沒有配置
            //@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback只負責業務異常
             @SentinelResource(value = "fallback",blockHandler = "blockHandler",fallback = "handlerFallback") //blockHandler只負責sentinel控制台配置違規
//            @SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler",
//                    exceptionsToIgnore = {IllegalArgumentException.class})
            public CommonResult<Payment> fallback(@PathVariable Long id) {
                CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id, CommonResult.class,id);

                if (id == 4) {
                    throw new IllegalArgumentException ("IllegalArgumentException,非法參數異常....");
                }else if (result.getData() == null) {
                    throw new NullPointerException ("NullPointerException,該ID沒有對應記錄,空指針異常");
                }

                return result;
            }
          
            //fallback
            public CommonResult handlerFallback(@PathVariable  Long id,Throwable e) {
                Payment payment = new Payment(id,"null");
                return new CommonResult<>(444,"兜底異常handlerFallback,exception內容  "+e.getMessage(),payment);
            }
          
            //blockHandler
            public CommonResult blockHandler(@PathVariable  Long id,BlockException blockException) {
                Payment payment = new Payment(id,"null");
                return new CommonResult<>(445,"blockHandler-sentinel限流,無此流水: blockException  "+blockException.getMessage(),payment);
            }


        }
         
         

 10.4測試

  啟動9003和9004

      在sentinel配置規則1秒內只能訪問1次

  

 

  1)84的業務類里面的注解使用@SentinelResource(value = "fallback") //這里沒有配置fallback和blockHandler屬性

    #訪問http://localhost:84/consumer/fallback/1,正常

    

 

    #訪問http://localhost:84/consumer/fallback/4,返回非法參數異常報錯頁面

    

 

    #訪問http://localhost:84/consumer/fallback/5,返回空指針異常報錯頁面

    

 

     #1秒內訪問http://localhost:84/consumer/fallback/1多次,返回錯誤頁面

 

    

 

 2)84的業務類里面的注解使用@SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback負責業務異常      注意,修改這個配置,如果重啟了84模塊,需要重新在sentinel控制台添加配置1秒最多訪問一次

 

   #訪問http://localhost:84/consumer/fallback/1,正常

   #訪問http://localhost:84/consumer/fallback/4,返回fallback處理方法信息

    

       #訪問http://localhost:84/consumer/fallback/5,返回fallback處理方法信息

  

      #1秒內訪問http://localhost:84/consumer/fallback/1多次,返回錯誤頁面

 

  以上測試說明,fallback屬性對頁面異常進行了處理,對於超出sentinel規則沒有做出處理

 3)84的業務類里面的注解使用@SentinelResource(value = "fallback",blockHandler = "blockHandler")//blockHandler 負責超出sentinel規則的處理    

  #訪問http://localhost:84/consumer/fallback/1,正常

   #訪問http://localhost:84/consumer/fallback/4,返回錯誤頁面

  #訪問http://localhost:84/consumer/fallback/5,返回錯誤頁面

  #1秒內訪問http://localhost:84/consumer/fallback/1多次,返回blockHandler處理信息

  

 4)@SentinelResource(value = "fallback",blockHandler = "blockHandler",fallback = "handlerFallback")

  采用這樣的配置,業務異常和超出sentinel規則都會被處理

 5)@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler", exceptionsToIgnore = {IllegalArgumentException.class})

  在4的基礎上加了 exceptionsToIgnore屬性,就是說fallback對業務異常的處理,忽略IllegalArgumentException這個異常,也就是發生IllegalArgumentException異常時,fallback不做處理

   #訪問http://localhost:84/consumer/fallback/4,返回非法參數異常報錯頁面

    

          #訪問http://localhost:84/consumer/fallback/5,返回fallback處理方法信息  

10.5fallback處理方法和業務邏輯解耦

   blockHandler屬性使用blockHandlerClass屬性把處理方法放到一個專門的類里面,fallback屬性的處理的方法同樣可以通過fallbackClass屬性放到一個單獨的類里面,具體參考前面的blockHandler的處理。

 

11.sentinel配置的持久化

11.1簡要的持久化

  這種持久化,是吧sentinel的配置放到nacos去配置,而不是在sentinel控制台配置好,自動持久化到nacos,配置起來比較麻煩

  https://blog.csdn.net/wangjinb/article/details/107559200

11.2修改sentinel來實現持久化

  https://blog.csdn.net/weixin_42211601/article/details/113062732?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-1&spm=1001.2101.3001.4242

  膜拜大神操作-試着弄了一下,好用


免責聲明!

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



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