Sentinel 进阶教程 - 基础篇(2024 年)
大纲
- Sentinel 入门教程 - 基础篇(2020 年)
- Sentinel 入门教程 - 中级篇(2020 年)
- Sentinel 入门教程 - 整合篇(2020 年)
- Sentinel 进阶教程 - 基础篇(2024 年)
- Sentinel 进阶教程 - 中级篇(2024 年)
- Sentinel 进阶教程 - 整合篇(2024 年)
前言
官方资源
版本说明
在本文的所有案例中,各个组件统一使用以下版本:
组件 | 版本 | 说明 |
---|---|---|
Spring Boot | 3.2.0 | |
Spring Cloud | 2023.0.0 | |
Spring Cloud Alibaba | 2022.0.0.0 | |
Sentinel Dashboard | 1.8.7 |
代码下载
- 本文完整的案例代码可以从 这里 下载得到。
流量控制
官方文档
流量控制介绍
流量控制的概述
Sentinel 能够对流量进行控制,其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。FlowSlot 会根据预设的规则,结合 NodeSelectorSlot、ClusterBuilderSlot、StatisticSlot 统计出来的实时信息进行流量控制。限流的直接表现是在执行 Entry nodeA = SphU.entry(resourceName)
的时候抛出 FlowException 异常。FlowException 是 BlockException 的子类,开发者可以捕捉 BlockException 来自定义被限流之后的处理逻辑。
流量控制的类型
Sentinel 的流量控制主要有两种统计类型,一种是统计 QPS,另外一种则是统计并发线程数。类型由 FlowRule 类的 grade
字段来定义。其中,0
代表根据并发线程数来限流,1
代表根据 QPS 来进行流量控制。其中并发线程数、QPS 值,都是由 StatisticSlot 类实时统计获取的。
- 可以通过下面的命令查看某个资源的实时统计信息:
1 | curl http://localhost:8719/cnode?id=resourceName |
- 输出内容的格式如下:
1 | idx id thread pass blocked success total Rt 1m-pass 1m-block 1m-all exception |
thread
: 代表当前处理该资源的并发数;pass
: 代表一秒内到来到的请求;blocked
: 代表一秒内被流量控制的请求数量;success
: 代表一秒内成功处理完的请求;total
: 代表到一秒内到来的请求以及被阻止的请求总和;RT
: 代表一秒内该资源的平均响应时间;1m-pass
: 则是一分钟内到来的请求;1m-block
: 则是一分钟内被阻止的请求;1m-all
: 则是一分钟内到来的请求和被阻止的请求的总和;exception
: 则是一秒内业务本身异常的总和。
QPS 流量控制
当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:快速失败、Warm Up、排队等待,对应 FlowRule 类中的 controlBehavior
字段。
特别注意
若使用除了直接拒绝之外的流量控制效果,则调用关系限流策略(Strategy)会被忽略(失效)。
并发线程数控制
并发线程数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用(服务提供者)由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的开销比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发线程数控制通常在调用端(服务消费者)进行配置。
流控规则介绍
同一个资源可以创建多条限流规则,FlowSlot 会对该资源的所有限流规则依次遍历,直到有规则触发限流或者所有规则遍历完毕。一条限流规则主要由下面几个因素组成,开发者可以组合这些元素来实现不同的限流效果:
resource
:资源名,即限流规则的作用对象count
: 限流阈值grade
: 限流阈值类型(QPS 或者并发线程数)limitApp
: 流控针对的调用来源,若为default
则不区分调用来源strategy
: 调用关系限流策略controlBehavior
: 流量控制效果(快速失败、Warm Up、排队等待)
在 Sentinel 的控制台中,添加流控规则的步骤如下:
配置参数 | 配置说明 |
---|---|
资源名 | 资源的唯一名称,默认就可以是请求的接口路径(URL),可以自行修改。同一微服务应用内,资源名必须唯一。不同微服务应用之间,资源名可以重复。 |
针对来源 | 具体针对某个微服务进行限流,默认值为 default ,表示不区分来源,全部限流。 |
阈值类型 | QPS 表示通过 QPS 进行限流,并发线程数表示通过并发线程数限流。 |
单机阈值 | 与阈值类型组合使用。如果阈值类型选择的是 QPS,表示当调用接口的 QPS 达到阈值时,进行限流操作。如果阈值类型选择的是并发线程数,则表示当调用接口的并发线程数达到阈值时,进行限流操作。 |
是否集群 | 选中则表示集群环境,不选中则表示非集群环境。 |
三种流控模式
Sentinel 提供三种流控模式,包括直接、关联、链路。
直接流控
直接流控的概述
当接口达到限流条件时,直接开启限流功能,这是 Sentinel 默认的流控模式。
直接流控的使用
- Java 接口代码
1 | /** |
- Sentinel 添加流控规则
- 当快速多次调用
/testA
接口时,会触发限流,并默认返回Blocked by Sentinel (flow limiting)
的提示信息。
关联流控
关联流控的概述
当关联的资源达到访问阀值时,就限流当前接口。比如,当与 A 接口关联的 B 接口达到访问阀值后,就限流 A 接口自己。
具有关系的资源流量控制
当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db
和 write_db
这两个资源分别代表数据库读写,我们可以给 read_db
设置限流规则来达到写优先的目的:设置 strategy
为 RuleConstant.STRATEGY_RELATE
同时设置 refResource
为 write_db
。这样当写库操作过于频繁时,读数据的请求会被限流。
关联流控的使用
- Java 接口代码
1 | /** |
- Sentinel 添加流控规则
- 通过 JMeter 让多个线程并发调用
/testB
接口,然后再简单调用一次/testA
接口,发现/testA
接口会返回Blocked by Sentinel (flow limiting)
的提示信息。这说明大量线程高并发访问/testB
接口时,导致/testA
接口也触发了限流。
链路流控
链路流控的概述
当来自不同链路的请求对同一个资源(目标接口)进行访问时,实施针对性的不同限流措施。比如,针对指定的 A 资源,C 请求来访问就限流,D 请求来访问就放行。
根据调用链路入口限流
NodeSelectorSlot 类中记录了资源之间的调用链路,这些资源通过调用关系,相互之间构成一棵调用树。这棵树的根节点是一个名字为 machine-root
的虚拟节点,调用链的入口都是这个虚拟节点的子节点。一棵典型的调用树如下图所示:
1 | machine-root |
上图中来自入口 Entrance1 和 Entrance2 的请求都调用到了资源 NodeA,Sentinel 允许只根据某个入口的统计信息对资源限流。比如,可以设置 strategy
为 RuleConstant.STRATEGY_CHAIN
,同时设置 refResource
为 Entrance1 来控制只有从入口 Entrance1 的调用才会记录到 NodeA 的限流统计当中,而不关心经 Entrance2 到来的调用。Sentinel 调用链的入口(上下文)是通过 API 方法 ContextUtil.enter(contextName)
定义的,其中 contextName
即对应调用链路入口名称。
链路流控的使用
- Java 测试代码
1 | /** |
1 | /** |
- YML 配置内容,这里需要将
web-context-unify
参数为false
1 | spring: |
- Sentinel 添加流控规则
- 通过 JMeter 让多个线程并发调用
/testC
接口,发现/testC
接口会返回Blocked by Sentinel (flow limiting)
的提示信息,即/testC
接口被 Sentinel 限流了。但是,同样让多个线程并发调用/testD
接口,发现所有请求都可以被正常处理。简而言之,/testC
请求来访问common
资源就限流,/testD
请求来访问common
资源就放行。
三种流控效果
Sentinel 有三种流控效果,包括快速失败、Warm Up、排队等待。
快速失败
快速失败的概述
快速失败(直接拒绝)是默认的流量控制方式,当 QPS 超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出 FlowException,并返回 Blocked by Sentinel (flow limiting)
提示信息。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
快速失败的使用
- Java 接口代码
1 | /** |
- Sentinel 添加流控规则
- 当多次调用
/testA
接口,发现/testA
接口会返回Blocked by Sentinel (flow limiting)
的提示信息,即/testA
接口被 Sentinel 直接限流了。
Warm Up
Warm Up 的概述
Warm Up 方式,即预热 / 冷启动方式。当系统长期处于低水位的情况下,如果流量突然增加,很可能直接把系统拉升到高水位,导致系统瞬间被压垮。通过 “冷启动”,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。这种方式适用于突发流量较大的场景,比如:秒杀系统在开启的瞬间,会有很多流量涌进来,很有可能把系统压垮,预热方式就是为了保护系统,慢慢地将流量放进来,慢慢地将阈值增长到预先设置的单机阈值。详细文档可以参考 流量控制 - Warm Up 官方文档。
通常冷启动的过程系统允许通过的 QPS 曲线如上图所示。Sentinel 默认的冷却因子 coldFactor
为 3,即请求 QPS 从 threshold / 3
开始,经预热时长逐渐升至设定的 QPS 阈值。
限流 - 冷启动
当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的。这个场景主要用于启动需要额外开销的场景,比如建立数据库连接等。它的底层是在 Guava 的算法的基础上实现的。然而,和 Guava 的场景不同,Guava 的场景主要用于调节请求的间隔,即 Leaky Bucket(漏桶),而 Sentinel 则主要用于控制每秒的 QPS,即满足每秒通过的 QPS 即可,不需要关注每个请求的间隔,换言之,Sentinel 更像一个 Token Bucket(令牌桶)。更详细的介绍,请参考 流量控制 - Warm Up 官方文档。
Warm Up 的使用
这里演示的案例为:单机阈值为 10,预热时长设置 5 秒。系统初始化的阈值为 10 / 3
约等于 3,即单机阈值刚开始为 3(人工设置的单机阈值是 10,Sentinel 计算后 QPS 判定为从 3 开始);然后过了 5 秒后,阀值才慢慢升高并恢复到人工设置的单机阈值 10。
- Java 接口代码
1 | /** |
- Sentinel 添加流控规则
- 刚开始多次调用
/testA
接口,发现/testA
接口会返回Blocked by Sentinel (flow limiting)
的提示信息,即/testA
接口被 Sentinel 限流了。过了 5 秒后,接口慢慢可以被更多的请求正常调用,直到达到单机阀值 10。
排队等待
排队等待的概述
排队等待(匀速排队)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法,详细文档可以参考 流量控制 - 匀速器模式。这种方式主要用于处理间隔性突发的流量,比如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。Sentinel 匀速排队等待策略是漏桶算法结合虚拟队列等待机制实现的。特别注意,匀速排队模式暂时不支持 QPS 大于 1000 的场景。
排队等待的使用
这里演示的案例为:设置单机阀值为 1,即一秒钟只允许通过一个请求;设置超时时间为 10 秒,即 10 秒内还可以有 10 * threshold
个(这里也就是 10 个)请求排队等待被处理,而 10 秒后所有未被处理的请求都作为超时处理(放弃处理)。
- Java 接口代码
1 | /** |
- Sentinel 添加流控规则
- 通过 JMeter 在 1 秒内启动 20 个线程(请求)来调用
/testE
接口
- 控制台打印日志显示有 11 个请求调用成功
1 | 1729762031993 testE 成功调用 |
- JMeter 显示有 9 个请求未被处理
流控效果与并发线程数
特别注意,在添加流控规则时,如果选择的 阀值类型
是 “并发线程数”,那么默认的 流控效果
就是 “快速失败”,而且不能选择 “Warm Up” 和 “排队等待” 这两种流控效果。
提示
使用 JMeter 模拟多个线程 + 循环来调用接口时,JMeter 会将接口打满,我们自己手动发送的大部分请求都无法被处理;但是偶尔会因 JMeter 发生线程切换,导致系统判断为没访问量,这时候我们自己手动发送的请求才可以得到正常处理。简而言之,在添加流控规则的时候,通常会选择 阀值类型
为 "QPS",而不是选择 "并发线程数",因为基于 QPS
的流量控制更精准。
熔断降级
官方文档
熔断降级介绍
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果,复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的服务雪崩。值得一提的是,熔断降级作为保护自身的手段,通常在调用端(服务消费者)进行配置或实现。
特别注意
本文针对 Sentinel 1.8.0
及以上版本进行编写。由于 Sentinel 1.8.0
版本对熔断降级特性进行了全新的改进升级,请使用最新版本以更好地利用熔断降级的能力。
熔断策略介绍
Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(比如调用超时、异常比例升高),对这个资源的调用进行限制,目的是让请求快速失败,避免影响到其它的资源而导致级联故障。当资源被降级后,在接下来的降级时间窗口内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。Sentinel 提供以下几种熔断策略:
慢调用比例(SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(
statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则在接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断状态(恢复正常),若大于设置的慢调用 RT 则会再次被熔断。异常比例(ERROR_RATIO):当单位统计时长(
statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则在接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断状态(恢复正常),否则会再次进入熔断状态。异常比率的阈值范围是[0.0, 1.0]
,代表0% - 100%
。异常数(ERROR_COUNT):当单位统计时长(
statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常数量大于阈值,则在接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断状态(恢复正常),否则会再次进入熔断状态。
特别注意
- 异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。为了统计异常比例或异常数,需要调用
Tracer.trace(ex)
记录业务异常。 - 对于开源整合模块,如 Sentinel Dubbo Adapter、Sentinel Web Servlet Filter 或者
@SentinelResource
注解会自动统计业务异常,无需手动调用Tracer.trace(ex)
记录业务异常。
熔断规则介绍
熔断规则(DegradeRule)包含下面几个重要的属性:
属性 | 说明 | 默认值 |
---|---|---|
resource | 资源名,即规则的作用对象 | |
grade | 熔断策略,支持慢调用比例 / 异常比例 / 异常数策略 | 慢调用比例 |
count | 慢调用比例模式下为慢调用临界 RT(超出该阀值则认为是慢调用);异常比例 / 异常数模式下为对应的阈值 | |
timeWindow | 熔断时长,单位为秒 | |
minRequestAmount | 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(从 1.7.0 版本开始引入) | 5 |
statIntervalMs | 统计时长(单位为 ms),如 60 * 1000 代表分钟级(从 1.8.0 版本开始引入) | 1000 ms |
slowRatioThreshold | 慢调用比例阈值,仅慢调用比例模式有效(从 1.8.0 版本开始引入) |
在 Sentinel 的控制台中,添加熔断规则的步骤如下:
三种熔断策略
慢调用比例
慢调用比例的概述
选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则在接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断状态(恢复正常),若大于设置的慢调用 RT 则会再次被熔断。
Sentinel 进入熔断状态的判断依据:在统计时长内,实际请求数目 > 设定的最小请求数,且实际慢调用比例 > 比例阈值,开始进入熔断状态。
术语说明:
调用
:一个请求发送到服务器,服务器返回响应,一个响应就是一个调用。最大 RT
:即请求的最大响应时间,指系统对请求作出响应的最大业务处理时间。慢调用
:处理业务逻辑的实际时间 > 设置的最大 RT 时间,这个调用就叫做慢调用。比例阈值
:即慢调用比例阀值,慢调用占有实际的比例 = 慢调用次数 ➗ 总调用次数熔断时长
:触发熔断后,控制多长时间内都处于熔断状态统计时长
:采集统计样本数据的时间最小请求数
:熔断触发的最小请求数,请求数小于该值时即使慢调用比例超出阈值也不会熔断
状态切换:
- 熔断状态(类似保险丝跳闸断电,不可访问):在指定的熔断时长内,请求会自动被熔断。
- 探测恢复状态(HALF-OPEN 状态):在熔断时长结束后,会进入探测恢复状态。
- 结束熔断(类似保险丝闭合恢复,可以访问):在探测恢复状态下,如果接下来的一个请求响应时间小于设置的慢调用 RT,则结束熔断状态,否则继续熔断。
慢调用比例的使用
- Java 接口代码
1 | /** |
- Sentinel 添加熔断规则
- 通过 JMeter 在 1 秒内启动 10 个线程(请求)来调用
/testF
接口,按照上面的 Java 代码和 Sentinel 熔断规则配置,会触发熔断
- 当进入熔断状态期间(5 秒内),单独用浏览器访问
/testF
接口,发现接口会返回Blocked by Sentinel (flow limiting)
提示信息,说明接口被熔断了
1 | Blocked by Sentinel (flow limiting) |
- 当结束熔断状态后(5 秒后),再次通过 JMeter 在 1 秒内启动 10 个线程(请求)来调用
/testF
接口,由于熔断器处于探测恢复状态(允许放行一个请求来探测服务是否恢复正常),只有 1 个请求可以正常调用/testF
接口,剩余的 9 个请求都调用失败
异常比例
异常比例的概述
当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则在接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断状态(恢复正常),否则会再次进入熔断状态。异常比率的阈值范围是 [0.0, 1.0]
,代表 0% - 100%
。
特别注意
在使用异常比例熔断规则时,要检查一下项目中是否有全局异常处理器,如果业务异常被全局异常处理器捕获处理了,那么即使 Sentinel 配置了熔断规则,接口也不会有熔断效果。
异常比例的使用
- Java 接口代码
1 | /** |
- Sentinel 添加熔断规则
- 通过 JMeter 在 1 秒内启动 10 个线程(请求)来调用
/testG
接口,按照上面的 Java 代码和 Sentinel 熔断规则配置,会触发熔断
- 当进入熔断状态期间(5 秒内),单独用浏览器访问
/testG
接口,发现接口会返回Blocked by Sentinel (flow limiting)
提示信息,说明接口被熔断了
1 | Blocked by Sentinel (flow limiting) |
- 当结束熔断状态后(5 秒后),单独用浏览器访问
/testG
接口,由于熔断器处于探测恢复状态(允许放行一个请求来探测服务是否恢复正常),所以该请求可以正常调用/testG
接口,同时接口会返回错误页面内容(如下图所示),这是因为在调用/testG
接口时抛出了异常
异常数
异常数的概述
当单位统计时长(statIntervalMs
)内请求数目大于设置的最小请求数目,并且异常数量大于阈值,则在接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断状态(恢复正常),否则会再次进入熔断状态。
异常数的使用
- Java 接口代码
1 | /** |
- Sentinel 添加熔断规则
- 通过 JMeter 在 1 秒内启动 10 个线程(请求)来调用
/testH
接口,按照上面的 Java 代码和 Sentinel 熔断规则配置,会触发熔断
- 当进入熔断状态期间(5 秒内),单独用浏览器访问
/testH
接口,发现接口会返回Blocked by Sentinel (flow limiting)
提示信息,说明接口被熔断了
1 | Blocked by Sentinel (flow limiting) |
- 当结束熔断状态后(5 秒后),单独用浏览器访问
/testH
接口,由于熔断器处于探测恢复状态(允许放行一个请求来探测服务是否恢复正常),所以该请求可以正常调用/testH
接口,同时接口会返回错误页面内容(如下图所示),这是因为在调用/testH
接口时抛出了异常