【SpringCloud】SpringCloud Alibaba Sentinel實現熔斷與限流


SpringCloud Alibaba Sentinel實現熔斷與限流

限流與降級

限流 blockHandler
降級 fallback
降級需要運行時出現異常才會觸發,而限流一旦觸發,你連運行的機會都沒有,當然就不會降級。
也就是說,兩者如果同時觸發,那么一定是限流觸發(降級連機會都沒有)。

Sentiel

官網

https://github.com/alibaba/Sentinel
中文:https://github.com/alibaba/Sentinel/wiki/介紹

是什么

一句話解釋就是我們之前講過的hystrix

去哪下

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

能干嘛

怎么玩

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

服務中的各種問題

  • 服務雪崩
  • 服務降級
  • 服務熔斷
  • 服務限流

安裝Sentiel控制台

sentinel組件由兩部分構成

  • 后台
  • 前台8080

安裝步驟

下載

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

運行命令

前提

java8環境OK
8080端口不能被占用

命令

java -jar sentinel-dashboard-1.7.0.jar

訪問sentinel管理界面

http://localhost:8080

登錄賬號密碼均為sentinel

初始化演示功能

啟動Nacos8848成功

http://localhost:8848/nacos/#/login

Module

cloudalibaba-sentinel-service8401

POM

<dependencies>
    <!-- SpringCloud ailibaba nacos-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    <!-- SpringCloud ailibaba 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>
</dependencies>

YML

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinal-service
  cloud:
    nacos:
      discovery:
        #Nacos服務注冊中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        #配置Sentin dashboard地址
        dashboard: localhost:8080
        # 默認8719端口,假如被占用了會自動從8719端口+1進行掃描,直到找到未被占用的 端口
        port: 8719

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

主啟動

@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401 {

    public static void main(String[] args) {
        SpringApplication.run(MainApp8401.class,args);
    }
}

業務類FlowLimitController

public class FlowLimitController {

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

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

啟動Sentinel8080

java -jar sentinel-dashboard-1.7.0.jar

啟動微服務8401

啟動8401微服務后台查看sentinel控制台

空空如也,啥也沒有

Sentinel采用懶加載說明

執行一次訪問

http://localhost:8401/testA
http://localhost:8401/testB

效果

結論

sentinel8080正在監控微服務8401

流控規則

基本介紹

進一步解釋說明


流控模式

直接(默認)

直接->快速失敗

系統默認

配置及說明

測試

  • 快速點擊訪問http://localhost:8401/testA
  • 結果:Blocked by Sentinel(flow limiting)
  • 思考???
    直接調用默認報錯信息,技術方面ok,but,是否應該有我們自己的后續處理
    類似有個fallback的兜底方法

關聯

應用場景:比如支付時達到閾值,可以從源頭上比如購買界面,進行限流
類比:下流洪災,上流關水

是什么

  • 當關聯的資源達到閾值時,就限流自己
  • 當與A關聯的資源B達到閾值后,就限流自己
  • B惹事,A掛了

配置A

postman模擬並發密集訪問testB

訪問B成功

postman里新建多線程集合組

將訪問地址添加進新線程組

RUN

大批量線程高並發訪問B,導致A失效了

運行后發現testA掛了

  • 點擊訪問A
  • 結果:Blocked by Sentinel(flow limiting)

鏈路

  • 多個請求調用同一個微服務
  • 家庭作業試試

流控效果

直接->快速失敗(默認的流控處理)

  • 直接失敗,拋出異常
    Blocked by Sentinel(flow limiting)
  • 源碼
    com.alibaba.csp.sentinel.slots.block.controller.DefaultController

預熱

說明

公式:閾值除以coldFactor(默認值為3),經過預熱時長后才會達到閾值

官網

  • 默認coldFactor為3,即請求QPS從threshold/3開始,經預熱時長逐漸升至設定的QPS閾值
  • 限流 冷啟動
    https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8

源碼

com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController

WarmUp配置

多次點擊http://localhost:8401/testB

剛開始不行,后續慢慢OK

應用場景

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

排隊等待

勻速排隊,閾值必須設置為QPS

官網

源碼

com.ailibaba.csp.sentinel.slots.block.controller.RateLimiterController

測試

降級規則

官網

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

基本介紹

進一步說明

Sentinel的斷路器是沒有半開狀態的

Sentinel熔斷隆級會在調用鏈路中某個資源出現不穩定狀態時(例如調用超時或異常比例升高) ,對這個資源的調用進行限制,讓請求快速失敗,避免影響到其它的資源而導致級聯錯誤。

當資源被降級后,在接下來的降級時間窗口之內,對該資源的調用都自動熔斷(默認行為是拋出DegradeException)。

半開的狀態系統自動去檢測是否請求有異常,沒有異常就關閉斷路器恢復使用,有異常則繼續打開斷路器不可用,具體參考Hystrix

復習Hystrix

降級策略實戰

RT

是什么


測試

代碼

配置

jmeter壓測
結論


異常比例

是什么


測試

代碼

配置

jmeter

結論

異常數

是什么


異常數是按照分鍾統計的

測試

代碼

同異常比例

配置

jmeter

熱點key限流

基本介紹

是什么

官網

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

承上啟下復習start

SentinelResource

兜底方法
分為系統默認和客戶自定義,兩種

之前的case,限流出問題后,都是用sentine|系統默認的提示: Blocked by Sentinel (flow limiting)

我們能不能自定?類似hystrix,某個方詘問題了,就找對應的兜底降級方法?

結論
從HystrixCommand到@SentinelResource

代碼

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

配置

配置

1

  • @SentinelResource(value = "testHotKey")
  • 異常打到了前台用戶界面看到,不友好

2

  • @SentinelResource(value = "testHotKey",blockHandler="dealHandler_testHotKey")
  • 方法testHotKey里面第一個參數只要QPS超過每秒一次,馬上降級處理
  • 用了我們自己定義的

測試

  • × error
    http://localhost:8401/testHotKey?p1=abc
  • × error
    http://localhost:8401/testHotKey?p1=abc&p2=33
  • √ right
    http://localhost:8401/testHotKey?p2=abc

參數例外項

上述案例演示了第一個參數p1,當QPS超過1秒1次點擊后馬上被限流

特殊情況

普通

  • 超過1秒鍾一個后,達到閾值1后馬上被限流
  • 我們期望p1參數當它是某個特殊值時,它的限流值和平時不一樣
  • 特例:假如當p1的值等於5時,它的閾值可以達到200

配置


添加
按鈕不能忘

測試

  • √ http://localhost:8401/testHotKey?p1=5
  • × http://localhost:8401/testHotKey?p1=3
  • 當p1等於5的時候,閾值變為200
  • 當p1不等於5的時候,閾值就是平常的1

前提條件

熱點參數的注意點,參數必須是基本類型或者String

其他

手賤添加異常看看o(╥﹏╥)o
后面講

@SentinelResource
處理的是Sentinel控制台配置的違規情況,有blockHandler方法配置的兜底處理;

RuntimeException
int age = 10/0, 這個是java運行時報出的運行時異常RunTimeException, @SentinelResource不管

總結
@SentinelResource主管配置出錯,運行出錯該走異常走異常

系統規則

是什么

https://github.com/alibaba/Sentinel/wiki/ 系統自適應限流

各項配置說明

配置全局QPS

不合適,使用危險,一竹竿打死一船人

@SentinelResource

按資源名稱限流+后續處理

啟動nacos成功

啟動Sentinel成功

Module

cloudalibaba-sentinel-service8401

pom

<dependency><!-- 引用自己定義的api通用包,可以使用Payment支付Entity -->
    <groupId>com.eiletxie.springcloud</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>${project.version}</version>
</dependency>

yml

業務類RateLimitController

@RestController
public class RateLimitController {

    @GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public CommonResult byResource() {
        return  new CommonResult(200,"按照資源名稱限流測試",new Payment(2020L,"serial001"));
    }

    public CommonResult handleException(BlockException exception) {
        return  new CommonResult(444,exception.getClass().getCanonicalName() + "\t 服務不可用");
    }
}

主啟動

配置流控規則

配置步驟

圖形配置和代碼關系

表示1秒鍾內查詢次數大於1,就跑到我們自定義的限流處,限流

測試

  • 1秒鍾點擊1下,OK
  • 超過上述,瘋狂點擊,返回了自己定義的限流處理信息,限流發生

額外問題

  • 此時關閉服務8401看看
  • Sentinel控制台,流控規則消失了?
    臨時?持久?

按照Url地址限流+后續處理

通過訪問URL來限流,會返回Sentinel自帶默認的限流處理信息

業務類RateLimitController

訪問一次

Sentinel控制台配置

測試

  • 瘋狂點擊http://localhost:8401/rateLimit/byUrl
  • 結果

上面兜底方案面臨的問題

  1. 系統默認的, 沒有體現我們自己的業務要求。
  2. 依照現有條件,我們自定義的處理方法又和業務代碼耦合在-塊,不直觀。
  3. 每個業務方法都添加一個兜底的,那代碼膨脹加劇。
  4. 全局統一的處理方法沒有體現。

客戶自定義限流處理邏輯

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

自定義限流處理類

CustomerBlockHandler

RateLimitController

啟動微服務后再調用一次

http://localhost:8401/rateLimit/customerBlockHandler

Sentinel控制台配置

測試后我們的自定義出來了

進一步說明

更多注解說明

https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81

多說一句

Sentinel主要有三個核心Api

  • sphU定義資源
  • Tracer定義統計
  • ContextUtil定義了上下文

服務熔斷功能

sentinel整合ribbon+openFeign+fallback

Ribbon系列

啟動nacos和sentinel

提供者9003/9004

新建cloudalibaba-provider-payment9003/9004

POM

<dependencies>
    <!-- SpringCloud ailibaba nacos-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!-- SpringCloud ailibaba sentinel-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <dependency><!-- 引用自己定義的api通用包,可以使用Payment支付Entity -->
        <groupId>com.eiletxie.springcloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>${project.version}</version>
    </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>
</dependencies>

YML

server:
  port: 9003

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

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

主啟動

@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003 {

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

業務類

@RestController
public class PaymentController {

    @Value("${server.port}")
    private  String serverPort;

    public static HashMap<Long, Payment > map = new HashMap<>();
    static {
        map.put(1L,new Payment(1L,"1111"));
        map.put(1L,new Payment(2L,"2222"));
        map.put(1L,new Payment(3L,"3333"));
    }


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

測試地址

http://localhost:9003/paymentSQ/

消費者84

新建cloudalibaba-consumer-nacos-order84

POM

與提供者pom一致

YML

server:
  port: 84

spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719

#消費者將去訪問的微服務名稱
server-url:
  nacos-user-service: http://nacos-payment-provider

主啟動

@EnableDiscoveryClient
@SpringBootApplication
public class OrderMain84 {

    public static void main(String[] args) {
        SpringApplication.run(OrderMain84.class,args);
    }
}

業務類

ApplicationContextConfig
@Configuration
public class ApplicationContextConfig {

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

    public static  final  String SERVICE_URL = "http://nacos-payment-provider";

    @Resource
    private RestTemplate restTemplate;

    @RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback")
    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("IllegalArgument ,非法參數異常...");
        }else if(result.getData() == null) {
            throw new NullPointerException("NullPointerException,該ID沒有對應記錄,空指針異常");
        }

        return  result;
    }
}
修改后請重啟微服務
  • 熱部署對java代碼級生效及時
  • 對@SentinelResource注解內屬性,有時效果不好
目的
  • fallback管運行異常
  • blockHandler管配置違規
測試地址

http://localhost:84/consumer/fallback/3

沒有任何配置

直接給用戶error界面。,,不太友好

只配置fallback
  @RequestMapping("/consumer/fallback/{id}")
//    @SentinelResource(value = "fallback")
    @SentinelResource(value = "fallback",fallback = "handlerFallback")
    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("IllegalArgument ,非法參數異常...");
        }else if(result.getData() == null) {
            throw new NullPointerException("NullPointerException,該ID沒有對應記錄,空指針異常");
        }

        return  result;
    }


    public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
        Payment payment = new Payment(id,"null");
        return new CommonResult(444,"異常handlerFallback,exception內容: " + e.getMessage(), payment);
    }

結果

只配置blockHandler

編碼

  @RequestMapping("/consumer/fallback/{id}")
//    @SentinelResource(value = "fallback")
//    @SentinelResource(value = "fallback",fallback =
    @SentinelResource(value = "fallback",blockHandler = "blockHandler")
    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("IllegalArgument ,非法參數異常...");
        }else if(result.getData() == null) {
            throw new NullPointerException("NullPointerException,該ID沒有對應記錄,空指針異常");
        }

        return  result;
    }
 
public CommonResult blockHandler(@PathVariable Long id,BlockException e) {
    Payment payment = new Payment(id,"null");
    return new CommonResult(444,"blockHandler-sentinel 限流,BlockException: " + e.getMessage(), payment);
}

記得配置一個qps的控制

結果

同時配置fallback和blockHandler

異常忽略

Feign系列

修改84模塊

  • 84消費者調用提供者9003
  • Feign組件一般是消費端

POM

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

YML

業務類

  • 帶@FeignClient注解的業務接口
  • fallback = PaymentFallbackService.class
  • Controller

主啟動

添加@EnableFeignClients啟動Feign的功能

http://localhost:84/consumer/paymentSQL/2

測試84調用9003,此時故意關閉9003微服務提供者,看84消費自動降級,不會被耗死

熔斷框架比較

Sentinel Hystrix resilience4j
隔離策略 信號量隔離(並發線程數限流) 線程池隔離/信號量隔離 信號量隔離
熔斷降級策略 基於響應時間、異常比率、異常數 基於異常比率 基於異常比率、響應時間
實時統計實現 滑動窗口(LeapArray) 滑動窗口(基於RxJava) Ring Bit Buffer
動態規則配置 支持多種數據源 支持多種數據源 有限支持
擴展性 多個擴展點 插件的形式 接口的形式
基於注解的支持 支持 支持 支持
限流 基於QPS.支持基於調用關系的限流 有限的支持 Rate Limiter

規則持久化

是什么

一旦我們重啟應用,sentinel規則消失,生產環境需要將配置規則進行持久化

怎么玩

將限流規則持久進Nacos保存,只要刷新8401某個rest地址,sentinel控制台的流控規則就能看得到,只要Nacos里面的配置不刪除,針對8401上的流控規則持續有效

步驟

修改cloudalibaba-sentinel-server8401

POM

<!-- SpringCloud ailibaba sentinel-datasource-nacos 持久化需要用到-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

YML

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinal-service
  cloud:
    nacos:
      discovery:
        #Nacos服務注冊中心地址
        server-addr: localhost:8848
    sentinel:
      transport:
        #配置Sentin dashboard地址
        dashboard: localhost:8080
        # 默認8719端口,假如被占用了會自動從8719端口+1進行掃描,直到找到未被占用的 端口
        port: 8719
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow

management:
  endpoints:
    web:
      exposure:
        include: '*'
 
 feign:
  sentinel:
    enabled: true #激活Sentinel 對Feign的支持

添加Nacos業務規則配置

內容解析

[
    {
        "resource": "/rateLimit/byUrl",
        "limitApp": "default",
        "grade": 1,
        "count": 1,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]

這一步的作用是每次消費者微服務啟動時在nacos中定義sentinel的流控規則,從而做到持久化的效果

啟動8401刷新sentinel發現業務規則變了

快速訪問測試接口

http://localhost:8401/rateLimit/byUrl
默認

停止8401再看sentinel

重新啟動8401再看sentinel

  • 咋一看還是沒有了,稍等一會兒
  • 多次調用
    http://localhost:8401/rateLimit/byUrl
  • 重新配置出現了,持久化驗證通過


免責聲明!

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



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