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

系统保护规则是应用整体维度的,而不是单个调用维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(埋点的 TrafficType 为 Inbound),比如 Web 服务或 gRPC provider 接收的请求,都属于入口流量。

目前系统规则支持以下的模式:

  • Load 自适应(仅对 Linux/Unix-like 机器生效):以系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发量超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps minRt 估算得出。设定参考值一般是 CPU cores 2.5 左右。
  • 平均响应时间:当单台机器上所有入口流量的平均响应时间(单位为 ms)达到阈值即触发系统保护。
  • 并发量:当单台机器上所有入口流量的并发量达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

原理

先用经典图来镇楼:

TCP-BBR-pipe

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

  • 推论一: 如果我们能够保证水管里的水量,能够让水顺畅的流动,则不会增加排队的请求;也就是说,这个时候的系统负载不会进一步恶化。

我们用 T 来表示(水管内部的水量),用RT来表示请求的处理时间,用P来表示进来的请求数,那么一个请求从进入水管道到从水管出来,这个水管会存在 P RT 个请求。换一句话来说,当 T ≈ QPS Avg(RT) 的时候,我们可以认为系统的处理能力和允许进入的请求个数达到了平衡,系统的负载不会进一步恶化。

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

  • 推论二: 当保持入口的流量是水管出来的流量的最大的值的时候,可以最大利用水管的处理能力。

然而,和 TCP BBR 的不一样的地方在于,还需要用一个启发因子(如 load1)来激发这套机制启动。

注:这种系统自适应算法的效果是一个“兜底”的效果。对于不是应用本身造成的负载高的情况(如其它进程导致的不稳定的情况),效果不明显。

示例

规则配置示例:

  1. import "github.com/sentinel-group/sentinel-golang/core/system"
  2.  
  3. // 自适应流控,启发因子为 load1 >= 8
  4. _, err := system.LoadRules([]*system.SystemRule{
  5. {
  6. MetricType:system.Load,
  7. TriggerCount:8.0,
  8. Strategy:system.BBR,
  9. },
  10. })