十五、Sentinel实现限流降级(2)

文章目录

  • 一、降级规则
    • 1. 基本介绍
      • 1.1进一步说明
      • 1.2 复习Hystrix
    • 2. 降级策略实战
      • 2.1RT是什么
      • 2.2 异常比例
      • 2.3 熔断数
  • 二、@SentinelResource
    • 1. 兜底方法
    • 2. value属性测试
    • 3. blockHandler
    • 4. 自定义处理类
    • 5. 统一处理
  • 三、热点key限流
    • 1 配置
      • 1.1普通版
      • 1.2 高级版
  • 四、系统规则

一、降级规则

官网

gibhub降级

1. 基本介绍

十五、Sentinel实现限流降级(2)_第1张图片

  • RT(平均响应时间,秒级)

平均响应时间超出阈值且在时间窗口内通过的请求>=5,两个条件同时满足后触发降级窗口期过后关闭断路器

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

异常比列(秒级)
QPS >=5且比例(秒级统计)超过阈值时,触发降级,时间窗口结束后,关闭降级

1.1进一步说明

Sentinel的断路器是没有半开状态的

Sentinel熔断隆级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高) ,对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。

当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出DegradeException)。

半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用,具体参考Hystrix

1.2 复习Hystrix

十五、Sentinel实现限流降级(2)_第2张图片

2. 降级策略实战

2.1RT是什么

  • 平均响应时间( DEGRADE_GRADE_RT ):当1s内持续进入5个请求,对应时刻的平均响应时间(秒级)均超过阈值( count ,以ms为单位),那么在接下的时间窗口( DegradeRule中的
    timewindow,以s为单位)之内,对这个方法的调用都会自动地熔断(抛出
    DegradeException )。注意Sentinel默认统计的RT上限是4900 ms,超出此阈值的都会算作4900 ms,若需要变更此上限可以通过启动配置项-Dcsp.sentine1.statistic.max. rt=xxx来配置。

十五、Sentinel实现限流降级(2)_第3张图片

测试
配置降级规则
十五、Sentinel实现限流降级(2)_第4张图片

    @GetMapping("/testD")
    public String testD() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("testD 测试RT");
        return "----testD";
    }

用Jmeter压测
然后浏览器http://127.0.0.1:8401/testD发现无法访问了
代码

  • 按照上述配置,

永远一秒钟打进来10个线程(大于5个了)调用testD,我们希望200毫秒处理完本次任务,
如果超过200毫秒还没处理完,在未来1秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了
后续我停止jmeter,没有这么大的访问量了,断路器关闭(保险丝恢复),微服务恢复OK

2.2 异常比例

介绍

异常比例( DEGRADE_GRADE_EXCEPTION_RATIO ):当资源的每秒请求量>=5,并且每秒异常总数占通过量的比值超过阈值( DegradeRule中的count )之后,资源进入降级状态,即在接下的时间窗口( DegradeRule中的timewindow,以s为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是[0.0,1.0],代表0%- 100%。
十五、Sentinel实现限流降级(2)_第5张图片
测试
十五、Sentinel实现限流降级(2)_第6张图片

@GetMapping("/testD")
    public String testD()
    {

        log.info("testD 测试RT");
        int age = 10/0;
        return "------testD";
    }

运行Jmeter测试
浏览器中输入http://127.0.0.1:8401/testD
已经熔断十五、Sentinel实现限流降级(2)_第7张图片

2.3 熔断数

异常数( DEGRADF_GRADE_EXCEPTION_cOuNT ):当资源近1分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timewindow小于60s,则结束熔断状态后仍可能再进入熔断状态。时间窗口一定要大于60s
十五、Sentinel实现限流降级(2)_第8张图片
测试
十五、Sentinel实现限流降级(2)_第9张图片

controller控制层代码

@GetMapping("/testE")
public String testE()
{
    log.info("testE 测试异常数");
    int age = 10/0;
    return "------testE 测试异常数";
}

浏览器http://localhost:8401/testE
http://localhost:8401/testE,第一次访问绝对报错,因为除数不能为零,
我们看到error窗口,但是达到5次报错后,进入熔断后降级。

二、@SentinelResource

1. 兜底方法

兜底方法分两种

  • 系统默认 :

    之前的例子,限流出问题后,都是用sentinel系统默认的提示: Blocked by Sentinel (flow limiting)
    我们能不能自定?类似hystrix,某个方法出问题了,就找对应的兜底降级方法?

  • 客户自定义

    从HystrixCommand到SentinelResource
    从HystrixCommand到哨兵资源

兜底方法

@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) {
    //int age = 10/0;
    return "------testHotKey";
}
 
//兜底方法
public String deal_testHotKey (String p1, String p2, BlockException exception){
    return "------deal_testHotKey,o(╥﹏╥)o";  
}

测试代码

@RestController
public class RateLimitController {

    @GetMapping("/byResource")
    @SentinelResource(value = "byResource", blockHandler = "handleException")
    public CommonResult byResource() {
        return new CommonResult(200, "按照资源名称限流测试", new Payment(2020L, "serial001"));
    }

    @GetMapping("/byResource/exection")
    @SentinelResource(value = "byResourceForExection", blockHandler = "handleException")
    public CommonResult byResourceForExection() {
        int i = 10 / 0;
        return new CommonResult(200, "按照异常数做降级", new Payment(2020L, "serial001"));
    }

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

    @GetMapping("/rateLimit/byUrl")
    @SentinelResource(value = "byUrl")
    //注意如果使用url进行限流降级那么SentinelResource配置的blockHandler将无效
    public CommonResult byUrl() {
        return new CommonResult(200, "按照byUrl限流测试", new Payment(2020L, "serial002"));
    }

    //CustomerBlockHandler
    @GetMapping("/rateLimit/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",
            blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handlerException2")
    public CommonResult customerBlockHandler() {
        return new CommonResult(200, "按照客户自定义限流测试", new Payment(2020L, "serial003"));
    }
}

2. value属性测试

配置
十五、Sentinel实现限流降级(2)_第10张图片
访问

注意资源名称要和@SentinelResource()的value属性相同才会限流

3. blockHandler

包括限流降级等都可以使用 注意 要根据@SentinelResource中的value属性进行配置规则 根据url不会有效果

替换默认显示Blocked by Sentinel (flow limiting)为我们自定义的
参考上边代码 返回值参数要一致并且在参数中添加BlockException exception

4. 自定义处理类

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

/**
 * @author 夏天
 */
public class CustomerBlockHandler {

    public static CommonResult handlerException(BlockException exception) {
        return new CommonResult(444, "按照客户自定义的Glogal 全局异常处理 ---- 1", new Payment(2020L, "serial003"));
    }

    public static CommonResult handlerException2(BlockException exception) {
        return new CommonResult(444, "按照客户自定义的Glogal 全局异常处理 ---- 2", new Payment(2020L, "serial003"));
    }
}

使用处理类

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

配置
十五、Sentinel实现限流降级(2)_第11张图片

测试

http://127.0.0.1:8401/rateLimit/customerBlockHandler
当点击过快时候触发限制

5. 统一处理

你知道Sentinel限流、降级的统一处理吗?

# 只包含主要配置其余省略
spring:
  cloud:
    sentinel:
      servlet:
        blockPage: https://www.baidu.com/ #配置统一处理页面

三、热点key限流

官方介绍:github热点key限流

1 配置

1.1普通版

十五、Sentinel实现限流降级(2)_第12张图片
十五、Sentinel实现限流降级(2)_第13张图片
注意

资源名要对应 @SentinelResource中的value才会有效果
浏览器继续使用@getMapper中的路径进行访问 http://127.0.0.1:8401/testHotKey?p1=1

  @GetMapping("/testHotKey")
    @SentinelResource(value = "testHotKeyB", blockHandler = "deal_testHotKey")
    //value 唯一即可 规范一般为方法名 blockHandler如果不配置页面直接抛出信息
    public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
                             @RequestParam(value = "p2", required = false) String p2) {
        return "----testHotKey";
    }

    //返回值 请求参数一致 并且添加上 BlockException exception 异常
    public String deal_testHotKey(String p1, String p2, BlockException exception) {
        return "----deal_testHotKey, o(╥﹏╥)o"; // sentinel的默认提示都是: Blocked by Sentinel (flow limiting)
    }

测试

http://127.0.0.1:8401/testHotKey?p1=1
根据配置1秒中的QPS的阈值为1次 所以点击过快会出现错误提示,这个提示是通过@SentinelResource注解自定义的
注意
资源名要对应 @SentinelResource中的value才会有效果 请注意看边的配置

十五、Sentinel实现限流降级(2)_第14张图片

1.2 高级版

上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流

需求

我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样
假如当p1的值等于5时,它的阈值可以达到200

实现

十五、Sentinel实现限流降级(2)_第15张图片
结论

当p1等于5的时候,阈值变为200
当p1不等于5的时候,阈值就是平常的1
前提条件:热点参数的注意点,参数必须是基本类型或者String

手贱添加异常看看…

@sentinelResource
处理的是sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理;

RuntimeException
int age = 10/0,这个是java运行时报出的运行时异常RunTimeException(其他异常也不管),@SentinelResource不管

总结
@sentinelResource主管配置出错,运行出错该走异常走异常

四、系统规则

先看看官网介绍:gibhub系统自适应限流
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。

系统规则支持以下的模式:

  • 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 达到阈值即触发系统保护。
    原理
    先用经典图来镇楼:
    十五、Sentinel实现限流降级(2)_第16张图片

我们把系统处理请求的过程想象为一个水管,到来的请求是往这个水管灌水,当系统处理顺畅的时候,请求不需要排队,直接从水管中穿过,这个请求的RT是最短的;反之,当请求堆积的时候,那么处理请求的时间则会变为:排队时间 + 最短处理时间。

  • 推论一: 如果我们能够保证水管里的水量,能够让水顺畅的流动,则不会增加排队的请求;也就是说,这个时候的系统负载不会进一步恶化。
    我们用 T 来表示(水管内部的水量),用RT来表示请求的处理时间,用P来表示进来的请求数,那么一个请求从进入水管道到从水管出来,这个水管会存在 P * RT 个请求。换一句话来说,当 T ≈ QPS * Avg(RT) 的时候,我们可以认为系统的处理能力和允许进入的请求个数达到了平衡,系统的负载不会进一步恶化。

接下来的问题是,水管的水位是可以达到了一个平衡点,但是这个平衡点只能保证水管的水位不再继续增高,但是还面临一个问题,就是在达到平衡点之前,这个水管里已经堆积了多少水。如果之前水管的水已经在一个量级了,那么这个时候系统允许通过的水量可能只能缓慢通过,RT会大,之前堆积在水管里的水会滞留;反之,如果之前的水管水位偏低,那么又会浪费了系统的处理能力。

  • 推论二: 当保持入口的流量是水管出来的流量的最大的值的时候,可以最大利用水管的处理能力。
    然而,和 TCP BBR 的不一样的地方在于,还需要用一个系统负载的值(load1)来激发这套机制启动。

注:这种系统自适应算法对于低 load 的请求,它的效果是一个“兜底”的角色。对于不是应用本身造成的 load 高的情况(如其它进程导致的不稳定的情况),效果不明显。

配置
十五、Sentinel实现限流降级(2)_第17张图片
配置
十五、Sentinel实现限流降级(2)_第18张图片
测试
十五、Sentinel实现限流降级(2)_第19张图片

你可能感兴趣的:(spring,cloud)