背景

在开始之前,先回顾一下 Sentinel 做系统负载的保护的目的:

  • 保证系统不被拖垮
  • 在系统稳定的前提下,保持系统的吞吐量
    长期以来,系统负载保护的思路是根据硬指标,即系统的负载 (load1) 来做系统过载保护。当系统负载高于某个阈值,就禁止或者减少流量的进入;当 load 开始好转,则恢复流量的进入。这个思路给我们带来了不可避免的两个问题:

  • load 是一个“果”,如果根据 load 的情况来调节流量的通过率,那么就始终有延迟性。也就意味着通过率的任何调整,都会过一段时间才能看到效果。当前通过率是使 load 恶化的一个动作,那么也至少要过 1 秒之后才能观测到;同理,如果当前通过率调整是让 load 好转的一个动作,也需要 1 秒之后才能继续调整,这样就浪费了系统的处理能力。所以我们看到的曲线,总是会有抖动。

  • 恢复慢。想象一下这样的一个场景(真实),出现了这样一个问题,下游应用不可靠,导致应用 RT 很高,从而 load 到了一个很高的点。过了一段时间之后下游应用恢复了,应用 RT 也相应减少。这个时候,其实应该大幅度增大流量的通过率;但是由于这个时候 load 仍然很高,通过率的恢复仍然不高。
    TCP BBR 的思想给了我们一个很大的启发。我们应该根据系统能够处理的请求,和允许进来的请求,来做平衡,而不是根据一个间接的指标(系统 load)来做限流。最终我们追求的目标是 在系统不被拖垮的情况下,提高系统的吞吐率,而不是 load 一定要到低于某个阈值。如果我们还是按照固有的思维,超过特定的 load 就禁止流量进入,系统 load 恢复就放开流量,这样做的结果是无论我们怎么调参数,调比例,都是按照果来调节因,都无法取得良好的效果。

Sentinel 在系统负载保护的做法是,用 load1 作为启动控制流量的值,而允许通过的流量由处理请求的能力,即请求的相应时间,以及当前系统正在处理的请求来决定。

原理

先用经典图来镇楼:

Drawing1.jpg

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

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

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

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

试验数据

用RT为50-70的随机数,机器负载为10为一个上限,来做一个实验。

触发限流之后,通过调节允许通过的入口请求数和系统负载的关系:

  • 新的算法:
    无标题.png

-老的算法:

2bc4fa7e767b3f8b1169b4af7e38826ef24afdd6.png

对比可以看到,load超过阈值之后,允许通过的请求数合理的稳定在大概80左右;而原来的做法的请求数量则会波动比较大;允许通过的请求也相对较少。

RT和load的关系

无标题.png

当下游系统好转之后,应用是否能够快速的自恢复

这个场景模拟的是,一个系统的处理能力恢复之后,是否可以快速的调高允许进入的QPS。比如说,应用A依赖了应用B,应用B在一段时间之后,处理的能力变快了,例如在下面表格的11:56:56的时候,处理能力从原来的400ms变成成了100ms,可以看到这个时候的允许通过的QPS也马上提升了。

无标题.png

对阈值的依赖

接下来的问题就是阈值的设定了。一般来说我们建议阈值的设定是CPU核数*2.5。但是这个阈值如果会上下浮动怎么办?

无标题.png

从实验数据来看,即使阈值设定的不精确,也不是完全根据阈值来限流的。我们可以看到进来的请求,RT都会逐渐的稳定,而系统的负载,会贴近这个曲线,但不是靠这个曲线来限制。

改进点

这个对低load的请求,它的效果是一个“兜底”的角色,即在最坏的情况下,它同时允许通过的请求。

对于不是应用本身造成的load高的情况,效果不明显。

原文: https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%B4%9F%E8%BD%BD%E4%BF%9D%E6%8A%A4