Spring Cloud Alibaba Sentinel 流控、降级、热点、系统规则详解

    科技2022-08-07  108

    转载声明 : 该文章出处为 扛麻袋的少年

    本文目录:

    写在开头1.流控规则 1.1 阈值类型:QPS1.2 阈值类型:线程数1.3 流控模式:直接1.4 流控模式:关联1.5 流控模式:链路1.6 流控效果:快速失败1.7 流控效果:Warm Up1.8 流控效果:排队等待 2.降级规则 2.1 RT2.2 异常比例2.3 异常数 3.热点规则 3.1 何为热点3.2 何为热点限流3.3 热点规则 Ⅰ.基本配置Ⅱ.参数例外项配置 4.系统规则 1.系统规则支持以下模式2. 入口QPS配置


    写在开头

      接上一篇文章:Spring Cloud Alibaba Sentinel 介绍、简单使用。学习了Sentinel 的基本使用,接下来我们就对 Sentine 中的 那些规则 进行一一介绍,本文重点讲以下4个。

    1.流控规则

      流控规则,即:流量控制规则。可自行参考官网介绍:GitHub 流量控制。具体配置有 资源名、针对来源、阈值类型、是否集群、流控模式、单机阈值、流控效果 这几项,它们配合进行使用。每一项具体代表的什么含义,且听我娓娓道来。

    资源名:唯一路径,默认为请求路径(也可以是后续介绍的 @SentinelResource 注解的 value 属性值)   针对来源:Sentinel 可以针对调用者进行限流,填写微服务名。默认为 default(不区分来源)   是否集群:本文为单机测试,是否集群不选

    1.1 阈值类型:QPS

    QPS(每秒钟的请求数量):当调用该 API 的 QPS 达到阈值的时候,进行限流。

    配置:

    配置说明:

      /testA 服务,每秒只允许调用 1 次,超出阈值后,流控效果为:直接 → 快速失败(流控效果如果不选,默认为 直接 → 快速失败)。

    资源名称阈值类型单机阈值流控模式流控效果/testAQPS1直接快速失败

    业务代码:

    @RestController public class FlowLimitController { @GetMapping("/testA") public String testA() throws InterruptedException { return "-----testA"; } }

    测试:

      开始,每秒调用 /testA 服务 1次,服务正常。后续狂点刷新调用服务,显然超过 Sentinel 设定的 QPS = 1,就会进行流量控制:快速失败(流控 Sentinel 默认提示:Blocked by Sentinel(flow limiting))

    1.2 阈值类型:线程数

    线程数:当调用该 API 的 线程数 达到阈值的时候,进行限流。

    配置: 配置说明:

      /testA 服务,单个线程只允许调用 2 次,超出阈值后,流控效果为:直接 → 快速失败。

    资源名称阈值类型单机阈值流控模式流控效果/testA线程数2直接快速失败

    业务代码:

    /** * 使/testA休眠6s */ @RestController public class FlowLimitController { @GetMapping("/testA") public String testA() throws InterruptedException { TimeUnit.MILLISECONDS.sleep(6000); return "-----testA"; } }

    测试:

      开始,调用 /testA 服务 2 次,服务正常,但是 sleep 导致服务没有返回。当第 3 次调用时,显然超过 Sentinel 设定的 线程数 = 2,就会进行流量控制:快速失败 (本次以 Firefox 进行测试,Chrome 是多线程执行??总是出不来效果,和浏览器设计有关系吧,此处就不过多考虑)

    1.3 流控模式:直接

      流控模式:直接,已经介绍,字面理解即可。没啥可说的。

      这些选项都是配合使用的,理解意思即可

    1.4 流控模式:关联

    关联:当关联的资源达到阈值时,就限流自己。当与 A 资源关联的 B 资源达到阈值时,就限流自己(A),即:B惹事,A挂了

    应用场景:

      双十一,支付接口和下单接口关联。当支付接口达到阈值,就限流下单接口 配置: 配置说明:

      /testA 服务关联/testB 服务,1s 调用 1次,服务正常。当狂点刷新调用 /testB 服务,超出阈值 QPS = 1 后,此时 /testA 被限流了,这就是 B惹事,A挂了。

    资源名称阈值类型单机阈值流控模式关联资源流控效果/testAQPS1关联/testB快速失败

    业务代码:

    /** * 使/testA休眠6s */ @RestController public class FlowLimitController { @GetMapping("/testA") public String testA(){ return "-----testA"; } @GetMapping("/testB") public String testB(){ return "-----testB"; } }

    测试:

      开始,/testA 和 /testB 1次/s 可以正常调用,突然大批量访问打到 /testB 请求。由于关联的 /testB 请求超过设定的阈值 QPS = 1,导致 /testA 请求被限流了。

    1.5 流控模式:链路

    链路:当链路中的资源达到阈值时,就会对使用到该资源的链路进行流控。当 A01 资源达到设定阈值时,所有调用该服务的链路,都会被限流,即:A01 挂了,用到我的链路都得挂

      此处会用到 @SentinelResource 注解 value 属性值 作为资源名。此处只是使用一下。该注解的详细使用,请跳转链接:@SentinelResource 介绍

    模拟两条请求链路:

    A链路: A → A01 → A04 → A05B链路: B → A01 → A02 → A03

    配置:

    配置说明:

      对 testA01 服务进行 链路 流控,该服务关联有 A 和 B 两条链路。当 A 链路1s 调用 1次,服务正常。当该链路调用 超出阈值 QPS = 1 后,此时A链路都会被限流,同时因为B链路也调用 testA01,所以B链路也会同时被限流调用。

    资源名称针对来源阈值类型单机阈值流控模式入口资源流控效果testAdefaultQPS1链路sentinel_web_servlet_context快速失败

    业务代码:

    /** * Controller代码 */ @RestController @Slf4j public class FlowLimitController { @Resource(name = "commonService") public CommonService commonService; @GetMapping("/testA") @SentinelResource("testA") public String testA(){ commonService.testA01(); return "-----testA"; } @GetMapping("/testB") @SentinelResource("testB") public String testB(){ commonService.testA01(); return "-----testB"; } } /** * Service代码 */ @Service("commonService") @Slf4j public class CommonService { @SentinelResource(value = "testA01") public void testA01() { testA02(); testA04(); log.info("-----testA01"); } @SentinelResource(value = "testA02") public void testA02() { testA03(); log.info("-----testA02"); } @SentinelResource(value = "testA03") public void testA03() { log.info("-----testA03"); } @SentinelResource(value = "testA04") public void testA04() { testA05(); log.info("-----testA04"); } @SentinelResource(value = "testA05") public void testA05() { log.info("-----testA05"); } }

    测试:

      开始,对 /testA 请求 1次/s 可以正常调用,当 /testA 请求 QPS > 1 后,满足设定的 testA01链路流控 规则 ,所以 /testA 请求 会被限流。同时 /testB 请求 也会被限流。 (即:testA01 QPS=1,1s 只允许调用1次,超出之后,调用 testA01 的所有链路将都会被限流)

      手速原因,此处使用 postman 1s 发送 10个请求(/testA 和 /testB 各10个),测试图如下所示:

    1.6 流控效果:快速失败

      流控效果:快速失败,已经介绍,字面理解即可。没啥可说的。

      这些选项都是配合使用的,理解意思即可

    1.7 流控效果:Warm Up

      Warm Up:某个服务,日常访问量很少,基本为 0,突然1s访问量 10w,这种极端情况,会直接将服务击垮。所以通过配置 流控效果:Warm Up,允许系统慢慢呼呼的进行预热,经预热时长逐渐升至设定的QPS阈值。

    公式计算:

    公式:阈值/coldFactor(默认值为3)

    应用场景:

      秒杀系统。秒杀系统在开启的瞬间,会有很多的流量上来,很有可能将系统打死。预热方式就是为了保护系统,可以慢慢的将流量放进来,最终将阈值增长到指定的数值

    配置: 配置说明:

      /testA 服务,设置 QPS 单机阈值为 10,采用 Warm Up 预热的方式,预热时长为 5s。根据计算公式 10 / 3 = 3,前 5s 的阈值为 3,预热 5s 后阈值增长到 10。

    资源名称阈值类型单机阈值流控模式流控效果预热时长/testAQPS10直接Warm Up5s

    业务代码:

    /** * 使/testA休眠6s */ @RestController public class FlowLimitController { @GetMapping("/testA") public String testA(){ return "-----testA"; } @GetMapping("/testB") public String testB(){ return "-----testB"; } }

    测试:

      开始调用 /testA 服务,狂点刷新访问打到 /testA 请求。配置 Warm Up 流控效果,在前 5s 内,通过公式计算阈值为 10/3 = 3,访问超过 3 次便会被限流;5s 后,阈值增长到 10,此时访问超过 3 次也不会被限流,这就是 Warm Up 预热效果。

    1.8 流控效果:排队等待

    排队等待:让请求以均匀的速度通过,对应的是漏桶算法。这种方式主要用于处理间隔性突发的流量,例如消息队列。

    应用场景:

      在某一秒有大亮的请求到来,而接下来的几秒则处于空闲状态。我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

    配置: 配置说明:

      /testA 服务,设置 QPS 单机阈值为 2,每秒只接收 2 个请求。设置超时时间 5s。采用漏斗算法,让后台匀速的处理请求,而不是直接拒绝更多的请求。超时的请求则被抛弃,返回错误信息。

    资源名称阈值类型单机阈值流控模式流控效果预热时长/testAQPS2直接排队等待5000ms

    业务代码:

    /** * 使/testA休眠6s */ @RestController public class FlowLimitController { @GetMapping("/testA") public String testA(){ return "-----testA"; } }

    测试:

      手速不够,此处就使用 postman 一次发送 100 个请求,设置 QPS = 2,其他请求将会排队等待。每秒只接收 2 个请求。我们会看到动态监控,通过QPS为 2,拒绝QPS 为 0,平均响应时间为 480ms,我们设置超时时间为 5000ms,所以拒绝QPS 为 0,如果超时时间为 200ms,拒绝 QPS 就会上升。100个请求发送完成,随之通过QPS降下来了。

    postman 一次如何发送多个请求,参考:postman 点击一次连续发送多个请求

    2.降级规则

      降级规则。可自行参考官网介绍:GitHub 熔断降级。降级策略有 RT、异常比例、异常数 三种,每一项具体代表的什么含义,继续听我娓娓道来。

    资源名:唯一路径,默认为请求路径(也可以是后续介绍的 @SentinelResource 注解的 value 属性值)

    2.1 RT

    RT:即 平均响应时间(DEGRADE_GRADE_RT) 。官网介绍太笼统,此处不Copy 了,要看官网介绍来这里:RT 平均响应时间介绍

    注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。

    图示: 配置: 配置说明:

      /testA 服务,设置 降级策略为 RT,RT(平均响应时间)为 200ms,时间窗口为 5s。发送请求平均响应时间在 200ms 内,正常访问不会被熔断降级。当平均响应时间 > 200ms,便会被熔断降级。时间窗口5s内断路器处于打开状态,无法提供服务,时间窗口结束,服务恢复正常访问。就是服务会被熔断5s的意思(切记是平均响应时间

    资源名称降级策略RT(平均响应时间)时间窗口/testART200ms5s

    业务代码:

    /** * 使/testA休眠300ms */ @RestController public class FlowLimitController { @GetMapping("/testA") public String testA() throws InterruptedException { TimeUnit.MILLISECONDS.sleep(300); return "-----testA"; } }

    测试:

      RT降级规则,需要同时满足:1.QPS>=5 && 2.平均响应时长> 200 两个条件。手动发送请求操作看的明显,此时 1秒钟发送 >5 个请求,由于每个请求都 sleep 300ms,请求最终平均响应时间为 300+ ms,肯定 > 200ms。满足上述两个条件,执行 RT 降级规则,开始熔断后,时间窗口期5s内,断路器处于打开状态,此时为熔断阶段,时间窗口结束,断路器关闭,关闭服务熔断,服务可以正常访问。 如图所示:

    2.2 异常比例

    异常比例:QPS >= 5 && 异常比例超过设定的阈值,便会发生服务降级 。

    异常比例为 0.0~1.0 范围内值。时间窗口就是断路器开启时间长短(降级时间) 。要看官网介绍来这里:异常比例介绍

    图示: 配置: 配置说明:

      /testA 服务,设置 降级策略为 异常比例,异常比例设为 0.5,时间窗口为 5s。即:1s 发送6个请求,异常比例超过 50%,就会被熔断,断路器打开5s,5s后自动关闭,继续提供服务。

    资源名称降级策略异常比例(0.0-1.0)时间窗口/testA异常比例0.55s

    业务代码:

    /** * 自己造一个 by zero 异常 */ @RestController public class FlowLimitController { @GetMapping("/testA") public String testA(){ int i = 10/0; return "-----testA"; } }

    测试:

      我们配置的异常比例降级规则,需要同时满足:1.QPS>=5 && 2.异常比例> 50% 两个条件。手动发送请求操作看的明显,此时 1秒钟发送 >5 个请求,因为每个请求都是异常,异常比例 100%。

      如果1s发送6次请求,前3次报错,因为第4次访问后,异常比例 > 50%,第4次便会被熔断,报 Blocked by Sentinel(flow limiting)。5s后继续提供服务哦。测试结果如图所示:

    2.3 异常数

    异常数:指的是资源 近1分钟 的异常数目,超过阈值之后会进行熔断。

    重点注意:异常数,统计时间窗口是分钟级别,若 timeWindow 小于 60s,则结束熔断状态后仍可能再次进入熔断状态。推荐 时间窗口一定要>=60s

    官网介绍来这里看:异常数介绍

    图示: 配置: 配置说明:

      /testA 服务,设置 降级策略为 异常数,异常数设为 5,时间窗口为 60s。即:调用服务,当异常数超过5个时,开启断路器,执行熔断操作。60s 后,断路器关闭,服务恢复正常,如下图:

    资源名称降级策略异常数时间窗口/testA异常数560s

    业务代码:

    /** * 自己造一个 by zero 异常 */ @RestController public class FlowLimitController { @GetMapping("/testA") public String testA(){ int i = 10/0; return "-----testA"; } }

    测试:

      执行 /testA 服务请求,因为每个请求都是异常,前5次调用正常返回,只是报异常到前台;第6次服务调用时,便会被降级熔断。报 Blocked by Sentinel(flow limiting)。60s后继续提供服务哦。测试结果如图所示:

    3.热点规则

    3.1 何为热点

      (本段内容摘自:Github 热点规则官方介绍)

      何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:

    商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制

      热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。 热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。   Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。(详细介绍,参考:Github 热点规则官方介绍)

    3.2 何为热点限流

      一句话解释:根据 url 传递进来的参数进行限流。带这个参数就限流,不带就不限流。

    3.3 热点规则

      热点规则。可自行参考官网介绍:GitHub 热点参数限流,共有 资源名、限流模式(只支持QPS模式)、参数索引、单机阈值、统计窗口时长、是否集群 六种参数;高级选项还有额外一些参数,在用到时介绍。每一项具体代表的什么含义,来继续听我絮絮叨叨吧。

      此处会用到 @SentinelResource 注解 value 属性值 作为资源名。此处只是使用一下。该注解的详细使用,请跳转链接:@SentinelResource 介绍

    资源名:唯一路径,默认为请求路径。此处必须是 @SentinelResource 注解的 value 属性值,配置@GetMapping 的请求路径无效) 参数索引:参数索引(从0开始,0表示第一个参数、1表示第二个参数)

    Ⅰ.基本配置

    1.@SentinelResource 注解说明

      @SentinelResource 注解,与 @HystrixCommand 类似,也是用来定义服务降级 兜底方法 的注解。该注解有很多属性可以配置。在下一篇会系统介绍:@SentinelResource 介绍

    2.业务代码:

    @RestController public class FlowLimitController { @GetMapping("/testC") @SentinelResource(value = "testC") //此处value值,一般为@GetMapping属性值去掉斜杠 public String testC(@RequestParam(value = "id",required = false) Integer id, @RequestParam(value = "name",required = false) String name){ return "-----testC"; } }

    3.配置: 4.配置说明:

      对 /testC 服务,配置热点key限流。当 1.参数 name 存在2.一秒内调用 /testC 服务 > 5次,满足限流规则。服务将被熔断。断路器打开,5s 后服务恢复正常

    资源名称参数索引单机阈值统计窗口时长testC(不能是请求路径,否则无效)155s

    5.测试:

      调用 URL:http://localhost:8401/testC?id=1&name=James,1.参数 name 存在2.一秒内调用 /testC 服务 > 5次,满足限流规则。服务将被熔断。断路器打开,5s 后服务恢复正常。如下图: 6.友好处理

      被限流后,直接报错 java.lang.refletc.UndeclaredThrowableException。异常显示到前台用户界面,显然不是不友好。

      此处也是需要用到 @SentinelResource 的 blockHandler 属性,它是 Sentinel控制台违规的兜底方法配置(还会j介绍一个 fallback属性,它是@GetMapping请求Java 方法执行出现异常的兜底方法配置,要分清楚)

      兜底方法配置,在下一篇介绍:@SentinelResource 介绍

    Ⅱ.参数例外项配置

    1.需求:

      当 name 参数值为 Wade 时,限流阈值变更为 100。此时就需要对 参数例外项 进行配置了。

      参数类型 支持:int、double、String、long、float、char、byte 7种类型,参数值 指 name 参数的值,限流阈值 指该参数值允许的阈值。

    2.配置:

    3.测试:

      调用 URL:http://localhost:8401/testC?id=1&name=Wade,虽然 name 参数存在,但是配置了 Wade 限流阈值为 100。我手速没那么快,哈哈,所以不会进入熔断。如下图所示:

    4.系统规则

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

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

      Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

    1.系统规则支持以下模式

    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 达到阈值即触发系统保护。

      此处内容,CPU 使用率、总平均 RT、并发线程数 等也不好演示,理解意思知道有这块用于全局应用入口流量控制就可以了,详细介绍还是参考官网来吧:热点参数限流

    2. 入口QPS配置

      入口QPS,实用性还是比较危险的。 如果 sentinel 密码被修改,将你的整个系统 入口QPS 配置很小,那么整个系统就瘫痪了。

      但是 入口QPS 有总控的功能。最终选择是否使用,还是视情况而定吧

    配置: 配置说明:

       整个系统,每个请求 QPS = 1 正常访问,当该请求 QPS >1 就会被限流。

    测试: 本文代码不提供,将文中业务代码配合如下代码使用即可。下载地址:Spring Cloud Alibaba Sentinel 简单使用 (提取码:nfmb)

    下一篇:来聊聊@SentinelResource的用法

    Processed: 0.010, SQL: 8