8、系统负载保护
8.1 背景
在开始之前,先回顾一下Sentinel 做系统负载的保护的目的:
- 保证系统不被拖垮
- 在系统稳定的前提下,保持系统的吞吐量 长期以来,系统负载保护的思路是根据硬指标,即系统的负载(load1) 来做系统过载保护。当系统负载高于某个阈值,就禁止或者减少流量的进入;当load 开始好转,则恢复流量的进入。这个思路给我们带来了不可避免的两个问题:
- load 是一个“果”,如果根据load 的情况来调节流量的通过率,那么就始终有延迟性。也就意味着通过率的任何调整,都会过一段时间才能看到效果。当前通过率是使load 恶化的一个动作,那么也至少要过1 秒之后才能观测到;同理,如果当前通过率调整是让load 好转的一个动作,也需要1 秒之后才能继续调整,这样就浪费了系统的处理能力。所以我们看到的曲线,总是会有抖动。
- 恢复慢。想象一下这样的一个场景(真实),出现了这样一个问题,下游应用不可靠,导致应用RT 很高,从而load 到了一个很高的点。过了一段时间之后下游应用恢复了,应用RT 也相应减少。这个时候,其实应该大幅度增大流量的通过率;但是由于这个时候load 仍然很高,通过率的恢复仍然不高。
TCP BBR 的思想给了我们一个很大的启发。我们应该根据系统能够处理的请求,和允许进来的请求,来做平衡,而不是根据一个间接的指标(系统load)来做限流。最终我们追求的目标是在系统不被拖垮的情况下,提高系统的吞吐率,而不是load 一定要到低于某个阈值。如果我们还是按照固有的思维,超过特定的load 就禁止流量进入,系统load 恢复就放开流量,这样做的结果是无论我们怎么调参数,调比例,都是按照果来调节因,都无法取得良好的效果。
Sentinel 在系统负载保护的做法是,用load1 作为启动控制流量的值,而允许通过的流量由处理请求的能力,即请求的相应时间,以及当前系统正在处理的请求来决定。
8.2 原理
先用经典图来镇楼:我们把系统处理请求的过程想象为一个水管,到来的请求是往这个水管灌水,当系统处理顺畅的时候,请求不需要排队,直接从水管中穿过,这个请求的RT 是最短的;反之,当请求堆积的时候,那么处理请求的时间则会变为:排队时间 最短处理时间。
- 推论一: 如果我们能够保证水管里的水量,能够让水顺畅的流动,则不会增加排队的请求;也就是说,这个时候的系统负载不会进一步恶化。
我们用T 来表示(水管内部的水量),用RT 来表示请求的处理时间,用P 来表示进来的请求数,那么一个请求从进入水管道到从水管出来,这个水管会存在P*RT个请求。换一句话来说,当T ≈ QPS * Avg(RT)的时候,我们可以认为系统的处理能力和允许进入的请求个数达到了平衡,系统的负载不会进一步恶化。
接下来的问题是,水管的水位是可以达到了一个平衡点,但是这个平衡点只能保证水管的水位不再继续增高,但是还面临一个问题,就是在达到平衡点之前,这个水管里已经堆积了多少水。如果之前水管的水已经在一个量级了,那么这个时候系统允许通过的水量可能只能缓慢通过,RT 会大,之前堆积在水管里的水会滞留;反之,如果之前的水管水位偏低,那么又会浪费了系统的处理能力。
- 推论二: 当保持入口的流量是水管出来的流量的最大的值的时候,可以最大利用水管的处理能力。 然而,和BBR 的不一样的地方在于,还需要用一个系统负载的值来激发这套机制启动。
8.3 试验数据
用RT 为50-70 的随机数,机器负载为10 为一个上限,来做一个实验。 1、触发限流之后,通过调节允许通过的入口请求数和系统负载的关系:
- 新的算法:
- 老的算法:
对比可以看到,load 超过阈值之后,允许通过的请求数合理的稳定在大概80左右;而原来的做法的请求数量则会波动比较大;允许通过的请求也相对较少。
- RT 和load 的关系
- 当下游系统好转之后,应用是否能够快速的自恢复 这个场景模拟的是,一个系统的处理能力恢复之后,是否可以快速的调高允许进入的QPS。比如说,应用A 依赖了应用B,应用B 在一段时间之后,处理的能力变快了,例如在下面表格的11:56:56 的时候,处理能力从原来的400ms 变成成了100ms,可以看到这个时候的允许通过的QPS 也马上提升了。
8.4 对阈值的依赖
接下来的问题就是阈值的设定了。一般来说我们建议阈值的设定是CPU 核数*2.5。但是这个阈值如果会上下浮动怎么办?从实验数据来看,即使阈值设定的不精确,也不是完全根据阈值来限流的。我们可以看到进来的请求,RT 都会逐渐的稳定,而系统的负载,会贴近这个曲线,但不是靠这个曲线来限制。
8.5 改进点
这个对低load 的请求,它的效果是一个“兜底”的角色,即在最坏的情况下,它同时允许通过的请求。 对于不是应用本身造成的load 高的情况,效果不明显。