Sentinel服務限流、降級、熱點規則


git地址: https://github.com/alibaba/Sentinel 

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

   sentinel實際和hystrix的作用一樣,實現服務降級、熔斷等。但是hystrix的不足之處大概有:1.需要程序員手工搭建監控平台;2.沒有一套web界面可以給我們進行細粒度化的配置。Sentinel也是實現流量控制、速率控制、服務熔斷、服務降級。Sentinel有的優點如下:1.單獨的組件,可以獨立出來。2.直接界面化的細粒度統一配置。

1.下載安裝

1.下載jar包

 到git https://github.com/alibaba/Sentinel/releases  下載即可。例如我下載的是:

sentinel-dashboard-1.8.0.jar

2. 啟動

直接以jar包的方式運行即可。

java -jar ./sentinel-dashboard-1.8.0.jar

3.訪問即可

 默認的端口是8080,啟動后訪問,登錄:  sentinel/sentinel

 2. 新建項目使用sentinel初始化監控

  新建一個項目,注冊中心為nacos,端口8848; 熔斷流量監控使用sentinel,端口8080.

1.新建項目cloudalibaba-sentinel-service8401

2.修改pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud</artifactId>
        <groupId>cn.qz.cloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloudalibaba-sentinel-service8401</artifactId>

    <dependencies>
        <!--引入自己抽取的工具包-->
        <dependency>
            <groupId>cn.qz.cloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--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>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!-- SpringBoot整合Web組件+actuator -->
        <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>
        <!--日常通用jar包配置-->
        <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>

</project>

3.yml配置

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服務注冊中心地址
    sentinel:
      transport:
        dashboard: localhost:8080 #配置Sentinel dashboard地址
        # 默認就是8719端口,如果占用會依次+1開始掃描
        port: 8719

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

4.主啟動類

package cn.qz.cloud.alibaba;

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

/**
 * @Author: qlq
 * @Description
 * @Date: 22:06 2020/11/25
 */
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401 {

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

5.測試Controller

package cn.qz.cloud.alibaba.controller;

import cn.qz.cloud.utils.JSONResultUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: qlq
 * @Description
 * @Date: 22:07 2020/11/25
 */
@RestController
public class FlowLimitController {

    @GetMapping("/testA")
    public JSONResultUtil<String> testA() {
        return JSONResultUtil.successWithData("testA");
    }

    @GetMapping("/testB")
    public JSONResultUtil<String> testB() {
        return JSONResultUtil.successWithData("testB");
    }
}

6.啟動后到nacos查看服務列表

 7. 訪問sentinel

需要注意sentinel采用的是懶加載,如果直接啟動服務沒有訪問,sentinel是不會攔截到請求的。所以需要訪問幾次服務然后到sentinel中查看。sentinel控制台如下:

3. sentinel流控

1.實時監控

 可以實時的查看每個請求的訪問QPS情況。

2.簇點鏈路

  和上面1差不多。展示最近的訪問情況。

3.流控規則

新增流控規則的時候,界面如下:

 概念性問題:

  • 資源名:唯一名稱,默認請求路徑
  • 針對來源:Sentinel可以針對調用者進行限流,填寫微服務名,默認default(不區分來源)
  • 閾值類型/單機閾值

(1)QPS(每秒鍾的請求數):當掉該api達到閾值的時候,進行限流

(2)線程數: 當調用該API的線程數達到閾值時,進行限流

  • 是否集群:不需要集群
  • 流控模式

(1)直接:直接限流

(2)關聯:當關聯的資源達到閾值時,限流自己

(3)鏈路:只記錄指定鏈路上的流量(指定資源從入口資源進來的流量,如果達到閾值,就進行限流)【api級別的針對來源】

  • 流控效果

(1)快速失敗:直接失敗,拋異常

(2)Warm up:根據code Factor(冷加載印子,默認3)的值,從閾值/cedeFactor,經過預熱時長,才達到預設的QPS

(3)排隊等待: 勻速排隊,讓請求勻速的通過,閾值類型必須設置QPS。否則無效。

4. 流控模式使用

1. QPS 快速失敗

多次訪問查看結果如下:

 2. 線程數直接失敗

 這個是接收請求,但是當處理的線程數超過1的時候報上面的【Blocked by Sentinel (flow limiting)】。和上面不同的是這個允許請求進去,上面是達到閾值就不讓請求進去。

3.關聯流控

  關聯流控就是關聯的資源達到閾值,限流自己。比如如下:當B超過QPS為1之后,A限流:

 測試方法:

(1) 使用postman或者jmeter並發訪問testB,如下:加到collections中,然后沒300ms訪問一次,訪問30次

(2)訪問A發現A阻塞

 4. 鏈路流控規則

(1)查看簇點鏈路,/testA的資源入口是sentinel_web_servlet_context

 (2) 對A進行鏈路流控

 (3)頻繁訪問會報Blocked by Sentinel (flow limiting)

5. 流控效果

 1.直接快速失敗

默認的就是快速失敗,拋出阻塞信息。實現類是 com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController

2.warm up預熱: com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController.WarmUpController

根據code Factor(冷加載印子,默認3)的值,從閾值/cedeFactor,經過預熱時長,才達到預設的QPS。比如下面規則表示:初始的時候閾值是10/3 = 3 ;5s的預熱時間達到10。

 這種模式的應用場景是秒殺系統,開啟瞬間會有很多流量上來,很有可能把系統打死,預熱就是為了保護系統,讓流量緩慢的進來。

3. 排隊等待

勻速排隊,讓請求勻速的通過。這種很好理解,就是每秒鍾放行多少個請求。如下:每秒鍾處理1個,超過的話進行排隊;排隊時長超過2s報阻塞錯誤。源碼是com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController。采用的算法是令牌桶算法。

 4.sentinel熔斷降級

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

提供以下幾種熔斷策略:

(1)慢調用比例 (SLOW_REQUEST_RATIO):選擇以慢調用比例作為閾值,需要設置允許的慢調用 RT(即最大的響應時間),請求的響應時間大於該值則統計為慢調用。當單位統計時長(statIntervalMs)內請求數目大於設置的最小請求數目,並且慢調用的比例大於閾值,則接下來的熔斷時長內請求會自動被熔斷。經過熔斷時長后熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求響應時間小於設置的慢調用 RT 則結束熔斷,若大於設置的慢調用 RT 則會再次被熔斷。
(2)異常比例 (ERROR_RATIO):當單位統計時長(statIntervalMs)內請求數目大於設置的最小請求數目,並且異常的比例大於閾值,則接下來的熔斷時長內請求會自動被熔斷。經過熔斷時長后熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。異常比率的閾值范圍是 [0.0, 1.0],代表 0% - 100%。
(3)異常數 (ERROR_COUNT):當單位統計時長內的異常數目超過閾值之后會自動進行熔斷。經過熔斷時長后熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。

1. 慢調用比例模式

滿足兩個條件會觸發熔斷:

1》單位統計時長請求數大於設置的最小值。下面是每秒鍾5個。

2》慢請求達到設置的比例。

 

 (1)新增方法

    @GetMapping("/testD")
    public JSONResultUtil<String> testD() throws InterruptedException {
        TimeUnit.SECONDS.sleep(1);
        return JSONResultUtil.successWithData("testD");
    }

 

 

(2)jmeter 以每秒鍾10次請求訪問

(3)curl訪問

$ curl http://localhost:8401/testD
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    35    0    35    0     0    249      0 --:--:-- --:--:-- --:--:--   280Blocked by Sentinel (flow limiting)

 

(4)上面jmeter每秒鍾10次請求大於最小值5;每個請求處理時長超過200ms,滿足第二個條件,因此觸發降級。

 2. 異常比例模式

如下:這個也是需要兩個條件。

1》每秒鍾請求超過五個

2》異常比例超過50%觸發熔斷

 (1)新增方法

    @GetMapping("/testE")
    public JSONResultUtil<String> testE() {
        int i = 1 / 0;
        return JSONResultUtil.successWithData("testE");
    }

 

(2)jmeter每秒鍾十個訪問

(3)curl訪問:發現觸發降級

$ curl http://localhost:8401/testE
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    35    0    35    0     0    140      0 --:--:-- --:--:-- --:--:--   149Blocked by Sentinel (flow limiting)

 

 (4)停掉jmeter再次curl,直接爆程序錯誤。也就是沒觸發sentinel熔斷

$ curl http://localhost:8401/testE
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0{"timestamp":"2020-12-01T14:41:15.196+0000","status":500,"error":"Internal Server Error","message":"/ by zero","trace":"java.lang.ArithmeticException: / by zero\r\n\tat cn.qz.cloud.alibaba.controller.FlowLimitController.testE(FlowLimitController.java:43)\r\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\r\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\r\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\r\n\tat java.lang.reflect.Method.invoke(Method.java:498)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)\r\n\tat org.springframework.web.servl

 

3.異常數模式

和上面的區別是根據異常數判斷是否需要進行熔斷。

 

 5. 熱點規則

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

  這個和hystrix挺像的。@SentinelResource代替@HystrixCommand。

1.新增方法

    @GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")
    public JSONResultUtil<String> testHotKey(@RequestParam(value = "p1", required = false) String p1,
                                             @RequestParam(value = "p2", required = false) String p2) {
        return JSONResultUtil.successWithData("testHotKey");
    }

    public JSONResultUtil<String> deal_testHotKey(String p1, String p2, BlockException exception) {
        return JSONResultUtil.successWithData("deal_testHotKey");  //sentinel系統默認的提示:Blocked by Sentinel (flow limiting)
    }

 

解釋:

@SentinelResource注解的value是便於唯一標識,在后面sentinel限流配置的時候可以使用 /testHotKey ,也可以使用 testHotKey唯一標識

blockHandler  類似於hystrixCommand注解的兜底方法。

2.sentinel增加如下配置:

 上面的配置規則是:

(1)如果 testHotKey 方法不帶p1 參數不進行限流

(2)如果待了p1參數且值不為"1",閾值為2;如果值為"1", 閾值可達到20。 (這里需要注意例外項的參數類型只支持8種基本數據類型和String類型)

(3)p2參數不參數限流

 6. 系統規則

參考:https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81

  系統保護規則是從應用級別的入口流量進行控制,從單台機器的 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 達到閾值即觸發系統保護。

可以理解為系統規則是全局的限流配置,可以針對服務的全局QPS、機器的CPU等參數進行限流。

7.@SentinelResource

  這個注解類似於HystrixCommand注解,可以用於方法的降級處理。

1.常規用法

package cn.qz.cloud.alibaba.controller;

import cn.qz.cloud.utils.JSONResultUtil;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RateLimitController {

    @GetMapping("/byResource")
    @SentinelResource(value = "byResource", blockHandler = "handleException")
    public JSONResultUtil<String> byResource() {
        return JSONResultUtil.successWithData("byResource");
    }

    public JSONResultUtil<String> handleException(BlockException exception) {
        return JSONResultUtil.successWithData("handleException");
    }

    @GetMapping("/rateLimit/byUrl")
    @SentinelResource(value = "byUrl")
    public JSONResultUtil<String> byUrl() {
        return JSONResultUtil.successWithData("byUrl");
    }
}

(1)新建兩條限流規則:

第一條: 資源名是@SentinelResource 的value屬性

 第二條: 資源名是URL,等價於上面的@SentinelResource的value屬性

 (2)測試:

1》 byResource 超出會走自己指定的方法

 2》byUrl 超出后走默認的限流方法

 (3)存在的問題:

1》系統默認限流,滿足不了業務要求。

2》自定義的處理方法和業務代碼耦合在一起,不直觀。

3》每個方法都添加一個兜底的,代碼膨脹加劇。

4》全局統一的處理方法沒有體現。

 2. 自定義異常處理器

(1)新建異常處理器

package cn.qz.cloud.alibaba.myhandler;

import cn.qz.cloud.utils.JSONResultUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;

public class CustomerBlockHandler {

    public static JSONResultUtil<Object> handlerException(BlockException exception) {
        return JSONResultUtil.errorWithMsg("-1", "error");
    }

    public static JSONResultUtil<Object> handlerException2(BlockException exception) {
        return JSONResultUtil.errorWithMsg("-1", "error2");
    }
}

(2)Controller使用@SentinelResource注解指明異常處理器以及方法

    @GetMapping("/rateLimit/cusError")
    @SentinelResource(value = "cusError", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
    public JSONResultUtil<String> cusError() {
        return JSONResultUtil.successWithData("cusError");
    }

(3)新建流控

 (3) 測試超出限制

8.@SentnelResource 補充

主要屬性 fallback和blockHandler。fallback管運行異常,blockHandler管配置違規,包括流量超限等。IllegalArgumentException 可以指定忽略的異常。例如:

package cn.qz.cloud.alibaba.controller;

import cn.qz.cloud.bean.Payment;
import cn.qz.cloud.utils.JSONResultUtil;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
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") //blockHandler只負責sentinel控制台配置違規
    @SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler",
            exceptionsToIgnore = {IllegalArgumentException.class})
    public JSONResultUtil<Payment> fallback(@PathVariable Long id) {
        JSONResultUtil<Payment> result = restTemplate.getForObject(SERVICE_URL + "/list/" + id, JSONResultUtil.class, id);
        if (id == 4) {
            throw new IllegalArgumentException("IllegalArgumentException,非法參數異常....");
        } else if (result.getData() == null) {
            throw new NullPointerException("NullPointerException,該ID沒有對應記錄,空指針異常");
        }

        return result;
    }

    //本例是fallback
    public JSONResultUtil<Payment> handlerFallback(@PathVariable Long id, Throwable e) {
        JSONResultUtil<Payment> objectJSONResultUtil = JSONResultUtil.successWithData(null);
        objectJSONResultUtil.setMsg("handlerFallback: " + e);
        return objectJSONResultUtil;
    }

    //本例是blockHandler
    public JSONResultUtil<Payment> blockHandler(@PathVariable Long id, BlockException blockException) {
        JSONResultUtil<Payment> objectJSONResultUtil = JSONResultUtil.successWithData(null);
        objectJSONResultUtil.setMsg("blockHandler : " + blockException);
        return objectJSONResultUtil;
    }
}

nacos-payment-provider服務只有1、2、3 ID返回數據。測試:

(1)ID為4拋出非法參數異常,這種異常exceptionsToIgnore 忽略兜底

 (2)ID為4以后的走fallback

9.sentinel持久化 

  持久化需要借助nacos,就相當於配置信息存到nacos,然后后nacos讀取配置。

1.應用的yml增加如下配置:

spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        #配置Sentinel dashboard地址
        dashboard: localhost:8080
        #默認8719端口,假如被占用會自動從8719開始依次+1掃描,直至找到未被占用的端口
        port: 8719
        datasource:
          ds1:
            nacos:
              server-addr: localhost:8848
              dataId: nacos-order-consumer
              groupId: DEFAULT_GROUP
              data-type: json
              rule-type: flow

2.到nacos新建配置

這里需要注意,data Id 和 上面應用中配置的一樣。

 

 內容如下:

[
    {
        "resource": "/consumer/fallback/2",
        "limitApp": "default",
        "grade": 1,
        "count": 5,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]

解釋:

resource:資源名稱
limitApp:來源應用
grade:閾值類型,0表示線程數,1表示QPS
count:單擊閾值
strategy:流控模式,0表示直接,1表示關聯,2表示鏈路
controlBehavior:流控效果,0表示快速失敗,1表示Warm up,2表示排隊等待

 


免責聲明!

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



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