Sentinel熔斷限流真的太絲滑了

閱讀本文大概需要20分鐘,但是還是要動手實(shí)驗(yàn)的哈,實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)。感覺博主整理的不錯的可以給個關(guān)注,本號后續(xù)陸續(xù)推出干貨

流量控制&熔斷降級產(chǎn)品對比

Sentinel Hystrix Resilience4j
隔離策略 信號量隔離(并發(fā)線程數(shù)隔離) 線程池隔離/信號量隔離 信號量隔離
熔斷降級策略 基于響應(yīng)時間、異常比率、異常數(shù) 基于異常比率 基于異常比率、響應(yīng)時間
實(shí)時統(tǒng)計實(shí)現(xiàn) 滑動窗口 滑動窗口 Ring Bit Bufffer
動態(tài)規(guī)則配置 支持多種數(shù)據(jù)源 支持多種數(shù)據(jù)源 有限支持
擴(kuò)展性 多個擴(kuò)展點(diǎn) 插件的形式 接口的形式
基于注解的支持 支持 支持 支持
限流 基于QPS,支持基于調(diào)用關(guān)系的限流 有限的支持 Rate Limiter
流量整形 支持預(yù)熱模式、勻速器模式、預(yù)熱派對模式 不支持 簡單的Rate Limiter
系統(tǒng)自適應(yīng)保護(hù) 支持 不支持 不支持
控制臺 提供開箱即用的控制臺、可配置規(guī)則、查看秒級監(jiān)控、機(jī)器發(fā)現(xiàn) 簡單的監(jiān)控查看

Sentinel 介紹

Sentinel 概述

Sentinel是阿里巴巴出品的面向分布式服務(wù)架構(gòu)的輕量級流量控制組件,主要以流量為切入點(diǎn),從限流、流量整形、熔斷降級、系統(tǒng)負(fù)載保護(hù)等多個維度來保障微服務(wù)的穩(wěn)定性

Sentinel 組成

  • 核心庫:主要是指Java客戶端,不依賴任何框架、庫,能夠運(yùn)行與java7及以上的版本運(yùn)行時環(huán)境,同時對Dubbo、Spring Cloud等框架也有較好的支持
  • 控制臺:控制臺主要負(fù)責(zé)管理推送規(guī)則、監(jiān)控、集群限流分配管理、機(jī)器發(fā)現(xiàn)等

Sentinel 特性

Sentinel 相關(guān)概念

資源

資源是Sentinel的關(guān)鍵概念。它可以是Java應(yīng)用程序中的任何內(nèi)容,例如,由應(yīng)用程序提供的服務(wù),或由應(yīng)用程序調(diào)用的其它應(yīng)用提供的服務(wù),甚至可以是一段代碼。只要通過Sentinel API定義的代碼,就是資源、能夠被Sentinel 保護(hù)起來。大部分情況下,可以使用方法簽名,URL,甚至服務(wù)名稱作為資源名來標(biāo)示資源

規(guī)則

規(guī)則指的是圍繞資源的實(shí)時狀態(tài)設(shè)定的規(guī)則,可以包括流量控制規(guī)則、熔斷降級規(guī)則以及系統(tǒng)保護(hù)規(guī)則。所有規(guī)則可以動態(tài)實(shí)時調(diào)整

Sentinel 優(yōu)勢

  • 友好的控制面板
  • 支持實(shí)時監(jiān)控
  • 支持多種限流、支持QPS限流,線程數(shù)限流以及多種限流策略
  • 支持多種降級模式、支持按平均返回時間降級,按多種異常數(shù)降級、按異常比率降級等
  • 方便擴(kuò)展開發(fā),支持SPI模式對chain進(jìn)行擴(kuò)展
  • 支持鏈路的關(guān)聯(lián)、可以實(shí)現(xiàn)按照鏈路統(tǒng)計限流,系統(tǒng)保護(hù),熱門資源保護(hù)等

SpringBoot+Sentinel

SpringBoot 環(huán)境信息

  • SpringBoot 2.1.4.RELEASE
  • JDK8
  • Sentinel 1.7.2

內(nèi)置加載限流規(guī)則

  • pom加入sentinel依賴

          <dependency>
              <groupId>com.alibaba.csp</groupId>
              <artifactId>sentinel-core</artifactId>
              <version>1.7.2</version>
          </dependency>
    
  • 創(chuàng)建TestController

    @RestController
    public class TestController {
        @GetMapping("/hello")
        public String hello(){
        // 限流的資源名稱
            try (Entry entry = SphU.entry("hello")){
                return "hello sentinel";
            }catch (BlockException e){
                return "系統(tǒng)繁忙,請稍后";
            }
        }
        @PostConstruct
        public void initFlowRules(){
            List<FlowRule> list = new LinkedList<>();
            FlowRule rule = new FlowRule();
            // 限流資源名稱
            rule.setResource("hello");
            // 限流策略
            rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
            // 限流數(shù)量定義QPS每秒能通過的請求個數(shù)
            rule.setCount(2);
            list.add(rule);
            // 加載限流規(guī)則
            FlowRuleManager.loadRules(list);
        }
    }
    
  • 此時啟動應(yīng)用,訪問資源hello,每秒慢速點(diǎn)擊訪問可以正常訪問,如果快速點(diǎn)擊出現(xiàn)系統(tǒng)繁忙,請稍后

通過上面我們也能看到限流規(guī)則是在代碼中寫好的, 如果后續(xù)要修改的話,需要重新編譯非常的麻煩,現(xiàn)在Sentinel提供了一個控制臺,通過控制臺我們就能動態(tài)的修改限流規(guī)則,現(xiàn)在我們看下怎么使用Sentinel控制臺來實(shí)現(xiàn)限流策略

Sentinel 控制臺啟動

SpringBoot應(yīng)用與Sentinel控制臺綁定

  • pom加入依賴

          <dependency>
              <groupId>com.alibaba.csp</groupId>
              <artifactId>sentinel-transport-simple-http</artifactId>
              <version>1.7.2</version>
          </dependency>
    
  • JVM啟動時添加參數(shù)

    -Dcsp.sentinel.dashboard.server=localhost:8850
    -Dproject.name=SentinelQuickStartDemo
    
  • 重啟應(yīng)用并通過瀏覽器訪問 /hello接口幾次,查看控制臺中實(shí)時監(jiān)控效果

  • 動態(tài)設(shè)置限流規(guī)則

    • 首先取消代碼中的規(guī)則設(shè)置,注釋掉規(guī)則代碼
  • 打開控制臺設(shè)置限流規(guī)則
  • 訪問應(yīng)用接口,查看限流規(guī)則是否生效

Sentinel 定義資源的方式

Sentinel除了基本的定義資源的方式外,還有其它的定義資源的方式,具體如下:

  • 拋出異常的方式定義資源
  • 返回布爾值的方式定義資源
  • 異步調(diào)用支持
  • 注解方式調(diào)用支持
  • 主流框架的默認(rèn)適配

拋出異常的方式定義資源

Sentinel中的SphU包含try-catch風(fēng)格的api。用這種方式,當(dāng)資源發(fā)生限流之后會拋出BlockException。這個時候可以捕捉異常,進(jìn)行限流之后的邏輯處理,而我們在上面就使用了這種方式進(jìn)行定義資源,關(guān)鍵代碼如下:

// 使用限流規(guī)則監(jiān)控保護(hù)資源
try (Entry entry = SphU.entry("hello")){
// 被保護(hù)的資源
            return "hello sentinel";
        }catch (BlockException e){
        // 被限流或者降級的處理
            return "系統(tǒng)繁忙,請稍后";
        }

返回布爾值的方式定義資源

  • 定義資源保護(hù)訪問

    @RestController
    public class TestBooleanController {
        @GetMapping("/boolean")
        public boolean hello(){
            if (SphO.entry("Sentinel_Boolean")){
                try {
                    System.out.println("Hello Sentinel");
                    return true;
                }finally {
                    SphO.exit();
                }
            }else{
                // 限流降級的處理
                System.out.println("系統(tǒng)繁忙,請稍后");
                return false;
            }
        }
    
    }
    
  • 新增限流規(guī)則
  • 查看訪問結(jié)果

異步調(diào)用支持方式

  • 開啟異步支持

    // @EnableAsync 開啟異步支持
    @EnableAsync
    @SpringBootApplication
    public class SentinelApplication {
    
       public static void main(String[] args) {
          SpringApplication.run(SentinelApplication.class, args);
       }
    
    }
    
  • 添加異步訪問方法

    新建AcyncService方法

    @Service
    public class AsyncService {
        @Async
        public void hello(){
            System.out.println("異步開始======");
            try {
                Thread.sleep(5000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println("異步結(jié)束======");
        }
    }
    
    
  • 添加異步訪問資源

    @RestController
    public class TestAsyncController {
        @Autowired
        AsyncService asyncService;
        @GetMapping("/async")
        public void hello(){
            AsyncEntry asyncEntry = null;
            try {
                asyncEntry = SphU.asyncEntry("Sentinel_Async");
                asyncService.hello();
            }catch (BlockException e){
                System.out.println("系統(tǒng)繁忙,請稍后");
            }finally {
                if (asyncEntry != null ){
                    asyncEntry.exit();
                }
            }
            
        }
      
    }
    
  • 添加限流規(guī)則

  • 查看訪問效果

注解方式調(diào)用支持

  • 添加注解支持的依賴

          <dependency>
              <groupId>com.alibaba.csp</groupId>
              <artifactId>sentinel-annotation-aspectj</artifactId>
              <version>1.7.2</version>
          </dependency>
    
  • 創(chuàng)建Aspect配置類

    @Configuration
    public class AspectConfig {
        @Bean
        public SentinelResourceAspect sentinelResourceAspect(){
            return new SentinelResourceAspect();
        }
    }
    
  • 創(chuàng)建限流訪問資源代碼

    @RestController
    public class TestAnnController {
        @Autowired
        AsyncService asyncService;
        @SentinelResource(value = "Sentinel_Ann",blockHandler = "exceptionHandler")
        @GetMapping("/ann")
        public String hello(){
    
            return "Hello Sentinel";
        }
        public void exceptionHandler(BlockException e){
            e.printStackTrace();
            System.out.println("系統(tǒng)繁忙,請稍后");
        }
    
    }
    

主流框架的默認(rèn)適配

為了減少開發(fā)的復(fù)雜程度,對大部分的主流框架,例如Web Servlet,Dubbo、Spring Cloud、gRPC、Spring WebFlx、Reactor等都做了適配,只需要引入對應(yīng)的依賴就可以方便的整合Sentinel

SpringCloud+Sentinel

下面是整理本文時閱讀的一些資料匯總,有興趣的可以去看看

版本參考地址

SpringCloud

SpringCloud Alibaba

SpringCloud Alibaba 版本對應(yīng)關(guān)系

SpringCloud版本詳細(xì)對應(yīng)關(guān)系

Nacos-SpringCloud版本

步驟

  • springcloud 版本

     <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Greenwich.RELEASE</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <dependency>
                    <groupId>com.alibaba.cloud</groupId>
                    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                    <version>2.1.2.RELEASE</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
  • pom 加入sentinel依賴

            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
                <version>2.1.0.RELEASE</version>
            </dependency>
    
  • 創(chuàng)建限流資源代碼

    @RestController
    public class TestController {
        @SentinelResource(value = "Sentinel_SpringCloud",blockHandler = "exceptionHandler")
        @GetMapping("/ann")
        public String hello(){
            return "hello sentinel";
        }
        public String exceptionHandler(BlockException e){
            e.printStackTrace();
            return "系統(tǒng)繁忙,請稍后";
        }
    }
    
  • 配置項目鏈接控制臺

    spring.application.name=SpringCloudSentinel
    spring.cloud.sentinel.transport.dashboard=localhost:8850
    
  • 創(chuàng)建限流規(guī)則

Sentinel+Feign

  • 引入feign+sentinel的依賴

     <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
                <version>2.1.0.RELEASE</version>
                <exclusions>
                    <exclusion>
                        <groupId>io.github.openfeign</groupId>
                        <artifactId>feign-core</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>io.github.openfeign</groupId>
                <artifactId>feign-core</artifactId>
                <version>10.1.0</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
    
  • 定義服務(wù)提供類,ServiceProvider 中修改

    @EnableDiscoveryClient
    @SpringBootApplication
    public class ServerProviderApplication {
    
      public static void main(String[] args) {
          SpringApplication.run(ServerProviderApplication.class, args);
      }
      @Slf4j
      @RestController
      static class TestController {
          @GetMapping("/hello/{str}")
          public String hello(@PathVariable String str) {
              log.info("invoked name = " + str);
              return "hello " + str;
          }
          @GetMapping("/test")
          public String test() {
              return "hello sentinel";
          }
      }
    
    }
    
  • 增加feign客戶端的支持

    @SpringBootApplication
    // 開啟feign-client
    @EnableFeignClients
    public class SentinelApplication {
    
        public static void main(String[] args) {
    
            SpringApplication.run(SentinelApplication.class, args);
        }
    
    }
    
  • 定義遠(yuǎn)程調(diào)用接口

    @FeignClient(value = "alibaba-nacos-discovery-server",fallback = FallBackService.class)
    public interface FeignAgent {
        @GetMapping("/test")
        String hello();
    }
    
    
  • 定義回調(diào)降級類

    @Component
    public class FallBackService implements FeignAgent {
        @Override
        public String hello() {
            return "系統(tǒng)繁忙,請稍后";
        }
    }
    
  • 配置項目鏈接sentinel控制臺,開始sentinel對feign的支持

    spring.application.name=SpringCloudSentinel
    spring.cloud.sentinel.transport.dashboard=localhost:8850
    # feign-sentinel 支持
    feign.sentinel.enabled=true
    
  • 啟動注冊中心nacos,服務(wù)提供者serviceProvider和sentinel客戶端

  • 定義限流規(guī)則,Sentinel和Feign整合時,限流規(guī)則的編寫形式為:

    http請求方式:協(xié)議://服務(wù)名/請求路徑跟參數(shù)
    例:GET:http://alibaba-nacos-discovery-server/test
    

Sentinel+SpringCloud Gateway

  • 引入依賴

     <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-test</artifactId>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.2</version>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
                <version>2.1.0.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
                <version>2.1.0.RELEASE</version>
            </dependency>
    
    
  • 配置gateway信息,鏈接Sentinel控制臺

    spring:
      application:
        name: SpringCloudGateWay
      cloud:
        gateway:
          routes:
          # id: sentinel-feign-gateway 限流時route id使用這個
            - id: sentinel-feign-gateway
              uri: lb://SpringCloudSentinel:8002
              predicates:
              # Path=/feign/** 自定義API維度限流使用
                - Path=/feign/**
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
        sentinel:
          transport:
            dashboard: 127.0.0.1:8850
    
    server:
      port: 8003
    
  • 配置限流時返回內(nèi)容

    @Component
    public class GatewayConfiguration {
        @PostConstruct
        public void doInit(){
            GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {
                @Override
                public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                    return ServerResponse.status(200).syncBody("系統(tǒng)繁忙,請稍后");
                }
            });
        }
    }
    
  • 啟用服務(wù)發(fā)現(xiàn)

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

限流規(guī)則配置

  • route id
  • 自定義API維度

流量控制實(shí)現(xiàn)

Sentinel的所有規(guī)則都可以在內(nèi)存態(tài)中動態(tài)的查詢及修改,修改之后立即生效。同時Sentinel也提供API,選擇定制自己的規(guī)則策略

Sentinel主要支持以下幾種規(guī)則

  • 流量控制規(guī)則
  • 熔斷降級規(guī)則
  • 系統(tǒng)保護(hù)規(guī)則
  • 來源訪問控制規(guī)則
  • 動態(tài)規(guī)則擴(kuò)展

流量控制規(guī)則實(shí)現(xiàn)

流量控制(flow control),其原理是監(jiān)控應(yīng)用流量的QPS或并發(fā)線程數(shù)等指標(biāo),當(dāng)達(dá)到制定的閾值時對流量進(jìn)行控制,以避免被瞬時的流量高峰沖垮,從而保障應(yīng)用的高可用性

流量控制主要有兩種方式

  • 并發(fā)線程數(shù):并發(fā)線程數(shù)限流用于保護(hù)業(yè)務(wù)線程數(shù)不被耗盡

  • QPS:當(dāng)QPS超過某個閾值的時候,則采取措施進(jìn)行流量控制

一條限流規(guī)則主要由下面幾個元素組成,我們可以組成這些元素來實(shí)現(xiàn)不同的限流效果:

  • resource:資源名,即限流的對象

  • count:限流閾值

  • grade:限流閾值類型(QPS或并發(fā)線程數(shù))

  • limitApp:流控針對的調(diào)用來源,若為default則不區(qū)分調(diào)用來源

  • strategy:調(diào)用關(guān)系限流策略

  • controBehavior:流量控制效果(直接拒絕,Warm Up,勻速排隊)

    • 直接拒絕(RuleConstant.CONTROL_BRHAVIOR_DEFAULT)方式是默認(rèn)的流量控制方式,當(dāng)QPS超過任意規(guī)則的閾值后,新的請求被立即拒絕,拒絕方式為拋出FlowException.這種方式適用與對系統(tǒng)處理能力確切已知的請求下,比如通過壓測確定了系統(tǒng)的準(zhǔn)確水位時
    • Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即預(yù)熱,冷啟動方式,當(dāng)系統(tǒng)長期處于低水位的情況下,當(dāng)流量突然增加時,直接把系統(tǒng)拉升到高水位可能瞬間把系統(tǒng)壓垮。通過“冷啟動”,讓通過的流量緩慢增加,在一定時間內(nèi)逐漸增加到閾值上限,給冷系統(tǒng)一個預(yù)熱的時間。避免冷系統(tǒng)被壓垮
    • 排隊等待(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式會嚴(yán)格控制請求通過的間隔時間,也即是讓請求以均勻的速度通過,對應(yīng)的是漏桶算法

    同一個資源可以同時由多個限流規(guī)則,檢查規(guī)則時會依次檢查

熔斷降級

熔斷降級會在調(diào)用鏈路中某個資源出現(xiàn)不穩(wěn)定狀態(tài)時(例如調(diào)用超時或異常比例升高),對這個資源的調(diào)用進(jìn)行限制,讓請求快速失敗,避免影響到其它的資源而導(dǎo)致級聯(lián)錯誤。當(dāng)資源被降級后,在接下來的降級時間窗口之內(nèi),對該資源的調(diào)用都自行熔斷(默認(rèn)行為是拋出DegradeException)

Field 說明 默認(rèn)值
resource 資源名,即限流規(guī)則的對象
count 閾值
grade 熔斷策略,支持秒級RT/秒級異常比例/分鐘級異常數(shù) 秒級平均RT
timeWindow 降級的時間,單位為s
reSlowRequestAmount RT模式下1秒內(nèi)連續(xù)多少個請求的平均RT超出閾值方可觸發(fā)熔斷(1.7.0引入) 5
minRequestAmount 異常熔斷的觸發(fā)最小請求數(shù),請求數(shù)小于該值時及時異常比率超出閾值也不會熔斷(1.7.0引入) 5

同一個資源可以有多個降級規(guī)則

熔斷策略詳解

  • 平均響應(yīng)時間(DEGRADE_GRADE_RT):當(dāng)1s內(nèi)連續(xù)進(jìn)入N個請求,對應(yīng)時刻的平均響應(yīng)時間(秒級)均超過閾值(count,以ms為單位),那么接下來的時間(DegradeRule 中的timeWindow,以s為單位)之內(nèi),對這個方法的調(diào)用都會自動熔斷
  • 異常比例(DEGRADE_GRADE_EXCEPTION_RATIO):當(dāng)資源的每秒請求量>=N(可配置),并且每秒異常總數(shù)占通過量的比值超過閾值(DegradeRule中的count)之后,資源進(jìn)入降級狀態(tài),即在接下的時間(DegradeRule 中的timeWindow,以s為單位)之內(nèi),對這個方法調(diào)用都會自動的返回。異常比率的閾值范圍時[0.0,1.0],代表0%-100%
  • 異常數(shù)(DEGRADE_GRADE_EXCEPTION_COUNT):當(dāng)資源近一分鐘的異常數(shù)目超過閾值之后就會進(jìn)行熔斷。注意由于統(tǒng)計時間時分鐘級別的,若timeWindow小于60s,則結(jié)束熔斷狀態(tài)后仍可能再進(jìn)入熔斷狀態(tài)

熔斷降級設(shè)計理念

在限制的手段上,Sentinel和Hystrix采取完全不一樣的方法

Hystrix通過線程池隔離的方式,來對依賴(在Sentinel的概念中對應(yīng)資源)進(jìn)行了隔離,這樣做的好處是資源和資源之間做到了最徹底的隔離。缺點(diǎn)是除了增加了線程切換的成本(過多的線程池導(dǎo)致線程數(shù)目過多)還需要預(yù)先給各個資源做線程池大小的分配

Sentinel對這個問題采取了兩種手段

  • 通過并發(fā)線程數(shù)進(jìn)行限制

    和資源池隔離的方法不同,Sentinel通過限制資源并發(fā)線程的數(shù)量,來減少不穩(wěn)定資源對其它資源的影響。這樣不但沒有線程切換的損耗,也不需要您預(yù)先分配線程池的大小。當(dāng)某個資源出現(xiàn)不穩(wěn)定的情況下,例如響應(yīng)時間變長,對資源的直接影響就是會導(dǎo)致線程數(shù)的逐步累積,當(dāng)線程數(shù)在特定資源上堆積到一定的數(shù)量之后,對該資源的新請求就會被拒絕,堆積的線程完成任務(wù)后才開始繼續(xù)接收請求

  • 通過響應(yīng)時間對資源進(jìn)行降級

    除了對并發(fā)線程數(shù)進(jìn)行控制以外,Sentinel還可以通過響應(yīng)時間來快速降級不穩(wěn)定的資源。當(dāng)依賴的資源出現(xiàn)響應(yīng)時間過長后,所有對該資源的訪問都會被直接拒絕,直到過了指定的時間窗口之后才重新恢復(fù)

系統(tǒng)自適應(yīng)保護(hù)實(shí)現(xiàn)

Sentinel 系統(tǒng)自適應(yīng)限流從整體維度對應(yīng)用入口流量進(jìn)行控制,結(jié)合應(yīng)用的Load、CPU使用率、總體平均RT、入口QPS和并發(fā)線程數(shù)等幾個維度的監(jiān)控指標(biāo),通過自適應(yīng)的流控策略,讓系統(tǒng)的入口流量和系統(tǒng)的負(fù)載達(dá)到一個平衡,讓系統(tǒng)盡可能的保持最大吞吐量的同時保證系統(tǒng)整體的穩(wěn)定性

系統(tǒng)保護(hù)規(guī)則是應(yīng)用整體維度的,而不是資源維度的,并且僅對入口流量生效,入口流量指的是進(jìn)入應(yīng)用的流量(EntryType.IN),比如Web服務(wù)或者Dubbo服務(wù)端接收到的請求,都屬于入口流量

系統(tǒng)規(guī)則支持一下的模式

  • Load自適應(yīng)(僅對Linux/Unix-like機(jī)器生效):系統(tǒng)的load1作為啟發(fā)指標(biāo),進(jìn)行自適應(yīng)系統(tǒng)保護(hù),當(dāng)系統(tǒng)load1超過設(shè)定的啟發(fā)值,且系統(tǒng)當(dāng)前的并發(fā)線程數(shù)超過估算的系統(tǒng)容量是才會觸發(fā)系統(tǒng)保護(hù)(BBR階段).系統(tǒng)容量由系統(tǒng)的maxQps*minRt估算得出,設(shè)定參考值一般是CPU cores * 2.5
  • CPU usage (1.5.0版本):當(dāng)系統(tǒng)CPU使用率超過閾值即出發(fā)系統(tǒng)保護(hù)(取值范圍0.0-1.0)比較靈敏
  • 平均RT:當(dāng)單臺機(jī)器上所有入口流量的并發(fā)線程數(shù)達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)
  • 并發(fā)線程數(shù):當(dāng)單臺機(jī)器上所有入口流量的并發(fā)線程數(shù)達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)
  • 入口QPS:當(dāng)單臺機(jī)器上所有入口流量的QPS達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)

重要的屬性

Field 說明 默認(rèn)值
highestSystemLoad load1觸發(fā)值,用于觸發(fā)自適應(yīng)控制階段 -1(不生效)
avgRt 所有入口流量的平均響應(yīng)時間 -1(不生效)
maxThread 入口l巨量的最大并發(fā)數(shù) -1(不生效)
qps 所有入口資源的QPS -1(不生效)
highestCpuUsage 當(dāng)前系統(tǒng)的CPU使用率(0.0-1.0) -1(不生效)

代碼控制

@RestController
public class TestLoadController {

    @SentinelResource(entryType = EntryType.IN)
    @GetMapping("/rule")
    public String hello(){
        return "hello sentinel rule";
    }


//    @PostConstruct
//    public void initDegradeRule(){
//        List<SystemRule> rules = new LinkedList<>();
//       SystemRule rule = new SystemRule();
//       rule.setQps(2);
//       rules.add(rule);
//        SystemRuleManager.loadRules(rules);
//    }


}

控制臺配置

系統(tǒng)設(shè)置

授權(quán)控制

很多時候,我們需要根據(jù)調(diào)用來源來判斷請求是否允許放行,這時候可以使用Sentinel的來源訪問控制(黑白名單控制)的功能,來源訪問控制根據(jù)資源的請求來源(origin)判斷資源是否通過,若配置白名單則只有請求來源位于白名單內(nèi)時才可通過;若配置黑名單則請求來源位于黑名單時不通過,其余的請求通過

重要屬性

  • resource: 資源名,即限流規(guī)則的對象
  • limitApp: 請求來源,對應(yīng)的黑名單/白名單,多個用“,"分割
  • strategy:限制模式,AUTHORITY_WHITE為白名單模式,AYTHORITY_BLACK為黑名單模式,默認(rèn)為白名單模式

代碼配置

  • 黑白名單配置
@RestController
public class WhiteBlackController {

    @SentinelResource(value = "Sentinel_Rule",blockHandler = "exceptionHandler")
    @GetMapping("/rule")
    public String hello(){
        return "hello sentinel rule";
    }
    public String exceptionHandler(BlockException e){
        e.printStackTrace();
        return "系統(tǒng)繁忙,請稍后";
    }

    @PostConstruct
    public void initWhiteRules(){
       List<AuthorityRule> rules = new LinkedList<>();
       AuthorityRule rule = new AuthorityRule();
       rule.setResource("Sentinel_Rule");
       rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
       rule.setLimitApp("192.168.168.168");
       rules.add(rule);
        AuthorityRuleManager.loadRules(rules);
    }

    @PostConstruct
    public void initBlackRules(){
        List<AuthorityRule> rules = new LinkedList<>();
        AuthorityRule rule = new AuthorityRule();
        rule.setResource("Sentinel_Rule");
        rule.setStrategy(RuleConstant.AUTHORITY_BLACK);
        rule.setLimitApp("127.0.0.1");
        rules.add(rule);
        AuthorityRuleManager.loadRules(rules);
    }

}
  • 獲取IP配置

    @Component
    public class SentinelConfig {
        @PostConstruct
        public void init(){
            WebCallbackManager.setRequestOriginParser(new RequestOriginParser() {
                @Override
                public String parseOrigin(HttpServletRequest httpServletRequest) {
                    return httpServletRequest.getRemoteAddr();
                }
            });
        }
    }
    

動態(tài)規(guī)則設(shè)置

拉模式拓展

實(shí)現(xiàn)拉模式的數(shù)據(jù)源最簡單的方式是繼承AutoRefreshDataSource抽象類,然后實(shí)現(xiàn)readSource()方法,在該方法里從指定數(shù)據(jù)源讀取字符串格式的配置數(shù)據(jù)。

推模式拓展

實(shí)現(xiàn)推模式的數(shù)據(jù)源最簡單的方式是繼承 AbstractDataSource 抽象類,在其構(gòu)造方法中添加監(jiān)聽器,并實(shí)現(xiàn) readSource() 從指定數(shù)據(jù)源讀取字符串格式的配置數(shù)據(jù)。比如 基于 Nacos 的數(shù)據(jù)源。

推模式:使用Nacos配置規(guī)則

  • pom加入依賴

            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-datasource-nacos</artifactId>
                <version>1.7.1</version>
            </dependency>
    
  • 動態(tài)配置規(guī)則

    @Component
    public class SentinelDataSourceConfig {
        private static final String KEY = "TestResource";
        // nacos server ip
        private static final String remoteAddress = "localhost:8848";
        // nacos group
        private static final String groupId = "Sentinel_Demo";
        // nacos dataId
        private static final String dataId = "com.alibaba.csp.sentinel.demo.flow.rule";
        // if change to true, should be config NACOS_NAMESPACE_ID
        private static boolean isDemoNamespace = false;
        // fill your namespace id,if you want to use namespace. for example: 0f5c7314-4983-4022-ad5a-347de1d1057d,you can get it on nacos's console
        private static final String NACOS_NAMESPACE_ID = "${namespace}";
    
        @PostConstruct
        private static void loadRules() {
            ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(remoteAddress, groupId, dataId,
                    source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
                    }));
            FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
        }
    
        private static void loadMyNamespaceRules() {
            Properties properties = new Properties();
            properties.put(PropertyKeyConst.SERVER_ADDR, remoteAddress);
            properties.put(PropertyKeyConst.NAMESPACE, NACOS_NAMESPACE_ID);
    
            ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(properties, groupId, dataId,
                    source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
                    }));
            FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
        }
    }
    
  • 發(fā)送nacos配置,或者手動創(chuàng)建

        public static void main(String[] args) throws Exception {
            final String remoteAddress = "localhost:8848";
            final String groupId = "Sentinel_Demo";
            final String dataId = "com.alibaba.csp.sentinel.demo.flow.rule";
            final String rule = "[\n"
                    + "  {\n"
                    + "    \"resource\": \"Sentinel_SpringCloud\",\n"
                    + "    \"controlBehavior\": 0,\n"
                    + "    \"count\": 5.0,\n"
                    + "    \"grade\": 1,\n"
                    + "    \"limitApp\": \"default\",\n"
                    + "    \"strategy\": 0\n"
                    + "  }\n"
                    + "]";
            ConfigService configService = NacosFactory.createConfigService(remoteAddress);
            System.out.println(configService.publishConfig(dataId, groupId, rule));
        }
    

行了,到這就結(jié)束了,本來打算簡單整理一下,但是發(fā)現(xiàn)越整理越多,而且真的是沒有試驗(yàn)一切都是刷流氓,快去寫個demo體驗(yàn)下Sentinel的絲滑吧

本文由mdnice多平臺發(fā)布

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容