Sentinel降級服務


Sentinel降級簡介

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

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

image-20201105132234324

RT(平均響應時間,秒級)

平均響應時間 超出閾值且在時間窗口內通過的請求>=5,兩個條件同時滿足后觸發降級窗口期過后關閉斷路器

RT最大4900(最大的需要通過-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)

異常比例(秒級)

QPS>=5且異常比例(秒級統計)超過閾值時,觸發降級;窗口期結束后,關閉降級。

異常數

異常數(分鍾統計)超過閾值時,觸發降級;時間窗口期結束后,關閉降級。

Sentinel降級-RT

修改子項目(8401)的FlowLimitController

 @GetMapping("/testD")
    public String testD() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "**********testD***********";
    }

在sentinel的窗口,設置降級

image-20201106093629292

當一秒鍾打進來十個線程來調用testD,我們希望200毫秒處理完本次任務,如果超過200毫秒還沒處理完,在未來1秒鍾的時間窗口內,斷路器打開,服務不可用。

Sentinel降級-異常比例

異常比例:當資源的每秒請求量>=5,並且每秒異常總數占通過量的比值超過閾值之后,資源進入降級狀態,即在接下來的時間窗口之內,對這個方法的調用都會自動返回,異常比例的閾值范圍[0.0,1.0],代表0%-100%。

修改子項目(8401)的FlowLimitController

@GetMapping("/testD")
    public String testD() {
        /*try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }*/
        int age = 10/0;
        return "**********testD異常比例***********";
    }

image-20201106095040745

Sentinel降級-異常數

異常數:當資源近1分鍾的異常數目超過閾值滯后進行熔斷。由於統計時間窗口是分鍾級別的,若時間窗口小於60s,則結束熔斷狀態后仍可能再進入熔斷狀態。

時間窗口一定要大於60秒

修改子項目(8401)的FlowLimitController

@GetMapping("/testE")
    public String testE() {
        int age = 10/0;
        return "********testE異常數********";
    }

運行報錯。

設置異常數

image-20201106100140248

image-20201106100244080

Sentinel熱點key

熱點參數限流會統計參數中的熱點參數,並根據配置的限流閾值與模式,對包含熱點參數的資源調用進行限流,熱點參數限流可以看作是一種特殊的流量控制,僅包含熱點參數的資源調用生效。

修改子項目(8401)的FlowLimitController

@GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
    public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                             @RequestParam(value = "p2",required = false) String p2) {
        return "*****testHotKey successful**********";
    }
    public String deal_testHotKey(String p1, String p2, BlockException ex){
        return "********deal_testHotKey**********";

    }

運行image-20201106102634048

配置熱點規則

image-20201106102717337

參數索引:對訪問的第一個參數進行限流

當每秒訪問多次的時候就會進行服務熔斷。

image-20201106102829509

當把blockHandler參數去掉,一秒一次就會顯示成功頁面,當一秒訪問多次的時候就會直接彈出錯誤頁面(Error Page)。

參數例外項

普通:超過1秒鍾一個之后,達到閾值1后馬上被限流

我們希望p1參數當它是摸個特殊值時,它的限流值和平時不一樣

假如當p1的值等於5時,它的閾值可以達到200

配置參數例外項

image-20201106104603378

image-20201106104706670

當p1的值不等於5時,閾值是1

當p1的值等於5時,閾值為200

image-20201106104822773

當代碼中有錯誤它不會去報deal_testHotKey,而是會直接顯示錯誤界面。

Sentinel系統規則

系統自適應限流

Sentinel系統自適應限流從整體維度對應用入口流量控制,結合應用的Load、CPU使用率、總體平均RT、入口QPS和並發線程數等幾個維度的監控指標,通過自適應的流控策略、讓系統的入口流量和系統的負載達到一個平衡,讓系統盡可能跑在最大吞吐量的同時保證系統整體的穩定性。

image-20201106121713542

Load自適應(僅對Linux/Unix-like機器生效):系統的load作為啟發指標,進行自適應系統保護。當系統load超過設定的啟發值,且系統當前的並發線程數超過估算的系統容量時才會觸發系統保護(BBR階段)。系統容量有系統的maxQps*minRt估算得出,設定參考值一般是CPU cores * 2.5。

CPU usage(1.5.0+版本):當系統CPU使用率超過閾值即觸發系統保護(取值范圍0.0-1.0),比較靈敏。

平均RT:當單台機器所有入口流量的平均RT達到閾值即觸發系統保護,單位是毫秒

並發線程數:當單台機器所有入口流量的並發線程數達到閾值即觸發系統保護

入口QPS:當單台機器上所有入口流量的QPS達到閾值即觸發系統保護

image-20201106122520361

SentinelResource配置

修改一個RateLimitController

@GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public CommonResult byResource() {
        return new CommonResult(200,"按資源名稱限流測試OK",new Payment(2020L,"serial001"));
    }
    public CommonResult handleException(BlockException exception){
        return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服務不可用");
    }

運行8401

image-20201106183123864

設置限流規則

image-20201106183205638

image-20201106183238199

按照Url地址限流

修改RateLimitController

 //按照URl限流
    @GetMapping("/rateLimit/byUrl")
    @SentinelResource(value = "byUrl")
    public CommonResult byUrl() {
        return new CommonResult(200,"按Url限流測試OK",new Payment(2020L,"serial002"));
    }

image-20201106191827389

設置限流規則

image-20201106192031488

image-20201106192105862

上面兜底方案面臨的問題

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

修改一個RateLimitController

    //CustomerBlockHandler
    @GetMapping("/rateLimit/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "handlerException1")
    public CommonResult customerBlockHandler() {
        return new CommonResult(200,"按客戶自定義",new Payment(2020L,"serial003"));
    }

新建一個CustomerBlockHandler.java

public class CustomerBlockHandler {
    public static CommonResult handlerException1(BlockException exception){
        return new CommonResult(444,"按客戶自定義,global handlerException-------1");
    }
    public static CommonResult handlerException2(BlockException exception){
        return new CommonResult(444,"按客戶自定義,global handlerException-------2");
    }
}

配置限流規則

image-20201106194631706

image-20201106194647996

image-20201106194701695

Sentinel服務熔斷Ribbon環境

新建子項目(cloudalibaba-provider-payment9003)

pom.xml

 <dependencies>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- SpringBoot整合Web組件 -->
        <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>
        <!--引入自己定義的api通用包-->
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </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>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

新建配置文件application.yml

server:
  port: 9003
spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848   #配置nacos地址

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

新建主啟動類

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

新建業務類PaymentController

@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,"從入門到放棄"));
        hashMap.put(2L,new Payment(2L,"從刪庫到跑路"));
        hashMap.put(3L,new Payment(3L,"從進門到坐牢"));
    }
    @GetMapping("/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;
    }
}

新建子項目(cloudalibaba-provider-payment9003)

步驟按照上述

新建子項目(cloudalibaba-consumer-nacos-order84)

pom.xml

 <dependencies>
        <dependency>
            <groupId>com.atguigu.springcloud</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>
        <!-- SpringBoot整合Web組件 -->
        <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>
        <!--SpringCloud ailibaba sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</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>

新建application.yml配置文件

server:
  port: 84

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

service-url:
  nacos-user-service: http://nacos-payment-provider

創建主啟動類OrderNacosMain84

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

創建配置類ApplicationContextConfig

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

創建業務類CircleBreakerController

@RestController
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("非法參數,異常");
        }else if(result.getData()==null) {
            throw new NullPointerException("該ID沒有對應的記錄,空指針異常");
        }
        return result;
    }
}

Sentinel服務熔斷配置fallback

在子項目84上業務類CircleBreakerController加上兜底方法

@RestController
public class CircleBreakerController {
    public static final String SERVICE_URL = "http://nacos-payment-provider";
    @Resource
    private RestTemplate restTemplate;
    @RequestMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback",fallback = "handerFallback")
    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("非法參數,異常");
        }else if(result.getData()==null) {
            throw new NullPointerException("該ID沒有對應的記錄,空指針異常");
        }
        return result;
    }
    public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
        Payment payment = new Payment(id, null);
        return new CommonResult<>(444,"兜底異常,異常內容:"+e.getMessage(),payment);
    }
}

image-20201107110444452

fallback只負責業務異常。

Sentinel服務熔斷配置blockhandler

修改子項目84上業務類CircleBreakerController

@RestController
public class CircleBreakerController {
    public static final String SERVICE_URL = "http://nacos-payment-provider";
    @Resource
    private RestTemplate restTemplate;
    @RequestMapping("/consumer/fallback/{id}")
    //@SentinelResource(value = "fallback",fallback = "handerFallback") //fallback只處理業務出現異常
    @SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只負責sentinel控制台配置違規
    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("非法參數,異常");
        }else if(result.getData()==null) {
            throw new NullPointerException("該ID沒有對應的記錄,空指針異常");
        }
        return result;
    }
    /*public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
        Payment payment = new Payment(id, null);
        return new CommonResult<>(444,"兜底異常,異常內容:"+e.getMessage(),payment);
    }*/
    public CommonResult handlerFallback(@PathVariable Long id, BlockException blockException) {
        Payment payment = new Payment(id, null);
        return new CommonResult<>(445, "blockHandler-sentinel限流,無此流水:" + blockException.getMessage(), payment);
    }
}

添加降級規則

image-20201107141637276

image-20201107141736762

image-20201107141818351

blockHandler只負責sentinel控制台配置違規

Sentinel服務熔斷配置fallback和blockhandler

若blockHandler和fallback都進行了配置,則被限流降級而拋出BlockException時只會進入blockHandler處理邏輯。

Sentinel服務熔斷exceptionsTolgnore

修改子項目84上業務類CircleBreakerController

@RestController
public class CircleBreakerController {
    public static final String SERVICE_URL = "http://nacos-payment-provider";
    @Resource
    private RestTemplate restTemplate;
    @RequestMapping("/consumer/fallback/{id}")
    //@SentinelResource(value = "fallback",fallback = "handerFallback") //fallback只處理業務出現異常
    //@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只負責sentinel控制台配置違規
    //@SentinelResource(value = "fallback",fallback = "handerFallback",blockHandler = "blockHandler")
    @SentinelResource(value = "fallback",fallback = "handerFallback",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("非法參數,異常");
        }else if(result.getData()==null) {
            throw new NullPointerException("該ID沒有對應的記錄,空指針異常");
        }
        return result;
    }
    public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
        Payment payment = new Payment(id, null);
        return new CommonResult<>(444,"兜底異常,異常內容:"+e.getMessage(),payment);
    }
    public CommonResult handlerFallback(@PathVariable Long id, BlockException blockException) {
        Payment payment = new Payment(id, null);
        return new CommonResult<>(445, "blockHandler-sentinel限流,無此流水:" + blockException.getMessage(), payment);
    }
}

運行

image-20201107143205986

假如4報異常,不再有fallback方法兜底,沒有降級效果了。

Sentinel服務熔斷OpenFeign

修改子項目(84)

pom.xml

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

修改application.yml

# 激活Sentinel對Feign的支持
feign:
  sentinel:
    enabled: true

在主啟動類上面加上注解,開啟Feign

@EnableFeignClients

創建service接口

@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService {

    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

創建接口實現類PaymentFallbackService

@Component
public class PaymentFallbackService implements PaymentService {
    @Override
    public CommonResult<Payment> paymentSQL(Long id) {
        return new CommonResult<>(444,"服務降級返回,----PaymentFallbackService",new Payment(id,"errorService"));
    }
}

在業務類CirleBreakerController添加

    @Resource
    private PaymentService paymentService;
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
        return paymentService.paymentSQL(id);
    }

運行

當9003停止運行后,84會

image-20201107145607764

Sentinel持久化規則

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

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

修改子項目(8401)

application.yml

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080       #配置Sentinel dashboard地址
        port: 8719      #默認8719端口,如果被占用會自動從8719開始依次+1,直到找到未被占用的端口
                       #8719端口是應用和Sentinel控制台交互的端口
        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: '*'

在nacos里面新建配置

image-20201107153103560

resource:資源名稱;

limitApp:來源應用;

grade:閾值類型,0表示線程數、1表示QPS;

count:單機閾值;

strategy:流控模式,0表示直接,1表示關聯,2表示鏈路;

controlBehavior:流控效果,0表示快速失敗,1表示Warm up,2表示排隊等待;

clusterMode:是否集群;

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

image-20201107153544722


免責聲明!

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



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