写点什么

Sentinel 熔断降级的规则及实现原理

  • 2023-06-12
    湖南
  • 本文字数:4943 字

    阅读完需:约 16 分钟

在微服务项目中,一个微服务常常需要调用内部其他服务的接口,即便是单体架构项目,也难免会调用一些第三方 API、访问数据库等。使用熔断降级功能可以有效避免因外部因素导致服务自身不可用甚至进程挂掉的情况发生。也许有些项目并不需要限流功能,但熔断降级功能却是微服务项目不可或缺的。


本篇给大家带来的是 Sentinel 熔断降级的实现原理。

旧版熔断降级

如果想要实现限流功能,则需要根据不同的硬件条件做好压测,压测出一个接口或一个服务在固定的硬件配置下所能承受的最大 QPS,然后根据 QPS 配置限流规则。在服务后期,需求可能会不断叠加,导致需要对接口重新做压测,或者根据线上的实际表现不断调整限流阈值,因此,限流功能可能很少被使用,或者限流阈值会被配置得比压测结果略大,这时就需要结合熔断降级做兜底容灾方案。


Sentinel 支持对同一个资源配置多个相同类型或不同类型的限流规则,在配置了限流规则的基础上,还可以为同一资源配置熔断降级规则。在接口的 QPS 未达到限流阈值却已经有很多请求超时的情况下,就可能达到熔断降级规则配置的阈值,从而触发熔断,这就能很好地保护服务自身。

熔断降级规则

Sentinel 围绕资源配置熔断降级规则,根据资源的实时指标数据与熔断降级规则实现熔断降级功能。

DegradeRule 规则类的源码如下:

  • count:限流阈值。

  • timeWindow:重置熔断的窗口时间,默认值为 0。

  • grade:熔断降级策略,支持按平均响应耗时(DEGRADE_GRADE_RT)、按失败比率(DEGRADE_GRADE_EXCEPTION_RATIO)和按失败次数(DEGRADE_GRADE_EXCEPTION_COUNT)3 种熔断降级策略。

  • rtSlowRequestAmount:当熔断降级策略被配置为按平均响应耗时时,该值表示超过限流阈值的慢请求数量。

  • minRequestAmount:当熔断降级策略被配置为按失败比率时,该值表示可触发熔断的最小请求数。

  • passCount:非配置字段,只在熔断降级策略为按平均响应耗时时使用,用于累加慢请求数,该值由一个定时任务重置,周期为 timeWindow(窗口时间大小)。

  • cut:非配置字段,用于记录当前是否已经触发熔断,当 passCount 的值大于或等于 rtSlowRequestAmount 的值时被设置为 true,由定时任务在 timeWindow 之后被重置为 false。


对于 count,其对应的不同熔断降级策略代表不同的阈值,如异常比率阈值、异常总数阈值、慢请求总数阈值。


对于 rtSlowRequestAmount,假设将该值配置为 5,规则的阈值为 100 毫秒,当连续 5 个请求的计算平均耗时都超过 100 毫秒时,后面的请求才会被熔断,且在下个时间窗口恢复。


对于 minRequestAmount,假设阈值配置为 10,在第一个请求就失败的情况下,失败率为 100%,而配置 minRequestAmount 可以避免这种情况的出现。

熔断降级的实现原理

DegradeSlot 是实现熔断降级的切入点,作为 ProcessorSlot 被插入 ProcessorSlotChain 中,并在 entry 方法中调用 Checker 来判断是否熔断当前请求,若需要熔断当前请求,则抛出 BlockException。


Checker 并不是一个接口,而是一种 check 行为。限流的 check 行为由 FlowRuleChecker 实现,熔断的 check 行为则由 DegradeRuleManager 实现。在熔断降级中,真正的 check 行为的逻辑由 DegradeRule 实现。


熔断降级的 check 行为流程如图所示:

从上图可知,当 DegradeSlot#entry 方法被调用时,由 DegradeSlot 调用 DegradeRuleManager#checkDegrade 方法检查当前请求是否达到某个熔断降级规则配置的阈值。因为熔断降级规则配置由 DegradeRuleManager 加载,所以 DegradeSlot 将 check 行为的逻辑交给 DegradeRuleManager 完成。


DegradeRuleManager#checkDegrade 方法的源码如下:

  1. 获取为当前资源配置的所有熔断降级规则。

  2. 遍历熔断降级规则,只要有一个熔断降级规则达到阈值,就抛出 DegradeException。


DegradeRuleManager 首先根据资源名称获取配置的熔断降级规则,然后遍历熔断降级规则,调用 DegradeRule#passCheck 方法将检查是否需要触发熔断的逻辑并交给 DegradeRule 完成。如果对一个资源配置多个熔断降级规则,则只要有一个熔断降级规则满足条件,就会触发熔断。


DegradeRule#passCheck 方法的源码如下:

  1. 根据资源名称获取统计该资源全局指标数据的 ClusterNode 实例。

  2. 如果熔断降级策略为 DEGRADE_GRADE_RT,则从 ClusterNode 实例中读取当前平均响应耗时,如果平均响应耗时小于阈值,则将慢请求计数器 passCount 重置为 0,放行请求;

若平均响应耗时超过阈值,并且超过阈值的慢请求数累计值已经大于 rtSlowRequestAmount 的值,则熔断当前请求,见第 5 条。

  1. 如果熔断降级策略为 DEGRADE_GRADE_EXCEPTION_RATIO,读取当前时间窗口的异常总数、成功总数和总请求数,若异常总数与成功总数的比值小于熔断降级规则配置的阈值,则放行请求;若异常总数与成功总数的比值大于或等于阈值,并且当前总请求数大于 minRequestAmount,则熔断当前请求,见第 5 条。

  2. 如果熔断降级策略为 DEGRADE_GRADE_EXCEPTION_COUNT,读取当前滑动窗口的异常总数,如果异常总数小于熔断降级规则配置的阈值,则放行请求,否则熔断当前请求,见第 5 条。

  3. 更改熔断标志为 true,使后续请求不需要重复判断,并且开启定时任务,用于重置熔断标志,在休眠 timeWindow 时长后重置熔断标志。


提示:当未配置或配置 timeWindow 为 0 时,cut 被立即重置,也就是不保存熔断判断的结果,每个请求都需要重新判断一次,这会影响性能。


官方文档在介绍 DEGRADE_GRADE_EXCEPTION_COUNT 熔断降级策略的地方加了使用注意说明,内容为:“注意,由于统计时间窗口是分钟级别的,若 timeWindow 小于 60 秒,则结束熔断状态后仍可能再次进入熔断状态。”这句话并不难理解,因为调用 ClusterNode 实例的 totalException 方法获取的是 1 分钟内的总异常数。


ClusterNode 类是 StatisticNode 类的子类。StatisticNode 类的 totalException 方法的作用是从分钟级滑动窗口中获取异常总数,其源码如下:

正因如此,DEGRADE_GRADE_EXCEPTION_COUNT 这个熔断降级策略的使用场景不多。由于 timeWindow、passCount 和 cut 是 Sentinel 出于性能考虑而添加的,在配置熔断规则时,建议不要将 timeWindow 配置为 0 或小于 0,可以将 timeWindow 配置为 1000 毫秒,即一个时间窗口大小,因为能减少一点计算量就能降低一点 Sentinel 对应用性能的影响。

新版熔断降级

Sentinel 1.8.0 发布于 2020 年 08 月 20 日,从官方文档来看,该版本的最大亮点是对熔断降级功能进行了重构。旧版本的熔断降级功能对慢调用并不友好,而新版本改善了这个问题。

旧版熔断降级的不足

Sentinel 1.7.x 支持以下 3 种熔断降级策略:

  • DEGRADE_GRADE_RT:按平均响应耗时。

  • DEGRADE_GRADE_EXCEPTION_RATIO:按失败比率。

  • DEGRADE_GRADE_EXCEPTION_COUNT:按失败次数。

1. 按平均响应耗时

当熔断降级策略为 DEGRADE_GRADE_RT 时,阈值为平均响应耗时,如果秒级滑动窗口统计的平均响应耗时超过熔断降级规则配置的阈值,则当连续 rtSlowRequestAmount 个请求之后,平均响应耗时依然超过阈值,则触发熔断。


将 1 秒内请求耗时的平均值与熔断降级规则配置的阈值进行比较,判断一个请求是否是慢请求。在接口耗时较长的情况下,如果 1 秒内统计的请求较为稀疏,会导致平均响应耗时容易受某个特别慢的请求影响,从而导致熔断效果不佳。


假设某接口调用的平均耗时正常为 100 毫秒,熔断的阈值为 500 毫秒,如果 1 秒内有 10 个请求,其中一个请求的耗时超过了 10 秒,就会将平均响应耗时延长到 1090 毫秒,则后续需要很多个平均耗时为 100 毫秒的请求才能将平均耗时降下来,这种情况下很容易触发熔断。

2. 按失败比率

当熔断降级策略为 DEGRADE_GRADE_EXCEPTION_RATIO 时,阈值为失败总数与成功总数的比值,如果秒级滑动窗口统计的失败总数与成功总数的比值大于或等于阈值,则触发熔断。


使用 1 秒内失败总数与成功总数的比值来判断是否达到熔断降级规则配置的阈值,这与按平均响应耗时一样,请求越稀疏,失败比率就越容易受单个异常请求的影响,从而提高失败比率,因此 DEGRADE_GRADE_EXCEPTION_RATIO 也不适用于慢调用场景。

3. 按失败次数

当熔断降级策略为 DEGRADE_GRADE_EXCEPTION_COUNT 时,阈值为失败次数,若分钟级滑动窗口统计的异常总数大于或等于阈值,则触发熔断。


使用 1 分钟内统计的失败请求总数作为阈值,即便将 timeWindow 配置为 1 秒,并在 timeWindow 秒之后关闭开关,开关也会立即被打开,因此大部分场景都不使用 DEGRADE_GRADE_EXCEPTION_COUNT,这个缺点也使其成了一个无意义的选项。

新版本的改进

Sentinel 1.8.0 使用的熔断降级策略(CircuitBreakerStrategy)如下:

  • SLOW_REQUEST_RATIO:按慢请求比率。

  • ERROR_RATIO:按失败比率。

  • ERROR_COUNT:按失败次数。


DegradeRule 类调整后的源码如下:

其与旧版本的区别如下:

  • grade:熔断降级策略,取值为 CircuitBreakerStrategy,并与旧版本取值兼容。

  • timeWindow:时间窗口大小,熔断器从打开状态到关闭状态的最小时间间隔。

  • slowRatioThreshold:慢请求比率,当熔断降级策略配置为 SLOW_REQUEST_RATIO 时使用。

  • statIntervalMs:统计时长,滑动窗口的周期,单位为毫秒。

1. 新版本支持的 3 种熔断降级策略均可自定义统计时长

DegradeRule 类的 statIntervalMs 字段用于指定资源指标数据统计时长,单位为毫秒,默认值为 1000。通过配置此字段来延长统计时长,可以将“稀疏”请求调整为“密集”请求。例如,若某接口的平均耗时为 1 秒,则可以将统计时长配置为 10 秒。

2. 对按平均响应耗时策略升级,改为按慢请求比率策略

由旧版本的按平均响应耗时策略改为按慢请求比率策略,统计慢请求数,使用慢请求数与总请求数的比值与阈值比较。


当熔断降级策略为 SLOW_REQUEST_RATIO 时,count 表示慢请求阈值,只有响应耗时超出 count 的请求才会被记为慢请求,是否为慢请求取决于当前请求的响应耗时,与平均耗时没有关系;slowRatioThreshold 则表示慢请求比率阈值,用于决定是否打开熔断器的阈值。

3. 引入半开启自动恢复支持,并提供熔断器事件监听器

从 Sentinel 1.8.0 开始,Sentinel 使用熔断器实现熔断降级功能,每个熔断降级规则对应生成一个熔断器。新版引入的熔断器支持半开启自动恢复,并且可以注册自定义事件监听器以感知熔断器的状态变化。


熔断器有 3 种状态,分别是开启状态、半开启状态和关闭状态。当熔断器状态为半开启状态时,直接拒绝请求;当熔断器状态为关闭状态时,放行请求;当熔断器状态为开启状态时,根据 timeWindow 尝试将开启状态改为半开启状态,若修改成功,则放行当前请求,否则拒绝当前请求。

熔断器的实现原理

熔断器模式(或断路器模式)源于 Martin Fowler 的 CircuitBreaker 一文。在 Hystrix 早期就已经采用熔断器实现熔断降级,而 Sentinel 在 Sentinel 1.8.0 中才采用熔断器模式。


熔断器状态的切换类似于线程状态的切换,如图所示:

熔断器初始状态为关闭状态,当达到熔断降级规则配置的阈值时,熔断器的状态从关闭状态变为开启状态。

熔断器不能直接从开启状态变为关闭状态,只有处在半开启状态时才能关闭。当熔断器从关闭状态变为开启状态的时间与当前时间的间隔超过 timeWindow 时,可尝试将熔断器变为半开启状态。


熔断器可以从半开启状态变为关闭状态,也可以从半开启状态变为开启状态,但不能从关闭状态变为半开启状态。


触发熔断器从半开启状态变为开启状态的时机有如下两个:

  1. 达到熔断降级规则配置的阈值时开启熔断器,如慢请求比率达到阈值。

  2. 当前请求被其他地方拒绝时开启熔断器,如对同一资源配置多个熔断器时,有其他规则的熔断器拒绝了请求,或者被限流器限流。


对于处于半开启状态的熔断器,只要当前请求调用正常即可关闭:当熔断降级策略为按失败比率或按失败次数时,只要当前请求正常即可关闭熔断器;当熔断降级策略为按慢请求比率时,只要当前请求不是慢请求即可关闭熔断器。


在状态改变的过程中,熔断器在通知监听器的同时会修改一些数据,如图所示:

当熔断器从关闭状态变为开启状态时,更新下一次允许将熔断器关闭的时间,即间隔 timeWindow 秒之后才可以将熔断器关闭。


当熔断器从半开启状态变为开启状态时,更新下一次允许熔断器关闭的时间。如果熔断器刚从开启状态变为半开启状态,此时想尝试关闭却发现又达到阈值了,就将其恢复为开启状态,并延长 timeWindow 秒之后再尝试关闭。


当熔断器从半开启状态变为关闭状态时,重置当前时间窗口统计的指标数据。每个熔断器都有一个独立的滑动窗口用于统计各自关心的指标数据。熔断器重置当前时间窗口的指标数据统计并非重置资源的指标数据统计。


重置熔断器统计的指标数据用于避免熔断器刚关闭又立即进入开启状态的现象出现。

用户头像

加VX:bjmsb02 凭截图即可获取 2020-06-14 加入

公众号:程序员高级码农

评论

发布
暂无评论
Sentinel熔断降级的规则及实现原理_Java_互联网架构师小马_InfoQ写作社区