Neo's Blog

不抽象就无法深入思考
不还原就看不到本来面目!

0%

高性能服务器设计-限流模型

服务的处理能力是有客观上限的。当请求速度超过服务的处理速度时,服务就会过载。

如果服务持续过载,会导致越来越多的请求积压,最终所有的请求都必须等待较长时间才能被处理,从而使整个服务处于瘫痪状态。

与之相对的,如果直接拒绝掉一部分请求,反而能够让服务能够”及时”处理更多的请求。 我们称这种做法为限流。限流是过载保护的手段之一,目的在于保护系统不被超过服务吞吐能力的流量(或突发)打死。

常见的限流手段有固定窗口、滑动窗口、令牌桶、漏桶、BBR、brpc自适应限流等。

下面挨个介绍每一种限流算法的原理以及优缺点。

传统的基于配置的单机限流算法

第一,固定窗口

原理:维护一个窗口的两个信息:窗口开始时间、经过窗口的请求数。如果窗口内超出限制数则拒绝。如果已经进入新的窗口,则reset计数。

不足:存在临界点(窗口reset前后的瞬时)的qps会飙高,从而打死系统。

第二,滑动窗口

原理:将总的时间窗口划分为N个小格,并单独维护每一个小格的计数。通过这种方式,可以把过去一段时间内的计数信息也用上,从而让限流的统计更精确一些。

不足:窗口不可能无限划分

第三,漏桶

原理:漏桶具有固定容量,出水速率是固定常量(流出请求)如果桶是空的,则不需流出水滴。可以以任意速率流入水滴到漏桶(流入请求)如果流入水滴超出了桶的容量,则流入的水滴溢出(新请求被拒绝)

不足:无法应对突发流量

20201222223148

第四,令牌桶

原理:假如用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中。假设桶最多可以存发b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃。当一个n个字节的数据包到达时,就从令牌桶中删除n个令牌,并且数据包被发送到网络。如果令牌桶中少于n个令牌,那么不会删除令牌,并且认为这个数据包在流量限制之外。

算法允许最长b个字节的突发,但从长期运行结果看,数据包的速率被限制成常量r。

20201222223820

自适应限流

上面的几种算法,一定程度上确实能够保护系统不被拖垮, 但不管漏斗桶还是令牌桶, 其防护思路都是设定一个指标, 当超过该指标后就阻止或减少流量的继续进入,当系统负载降低到某一水平后则恢复流量的进入。但其通常都是被动的,其实际效果取决于限流阈值设置是否合理,但往往设置合理不是一件容易的事情。具体有如下几个弊端:

  1. 集群增加机器或者减少机器限流阈值是否要重新设置?
  2. 设置限流阈值的依据是什么?
  3. 人力运维成本是否过高?
  4. 只针对局部服务端的限流,无法掌控全局资源,
  5. 当调用方反馈限流错误时, 这个时候重新设置限流, 其实流量高峰已经过了。重新评估限流是否有意义?

第五,B站BBR限流

底层原理:并发 = qps * latency

自适应限流能动态调整服务的最大并发,在保证服务不过载的前提下,让服务尽可能多的处理请求。

具体逻辑:如果cpu > 800 && inflight > rt * max_qps,则启用限流

为什么要用 CPU\IOPS 作为启发值呢?

因为自适应限流与 TCP 拥塞控制还存在不同之处,TCP 中客户端可以控制发送率,从而探测到 maxPass,但是 RPC 线上无法控制流量的速率,所以必须以 CPU 作为标准,当 CPU 快满载的时候再开启,这时我们认为之前探测到的 maxPass 已经接近了系统的瓶颈,乘以 minRtt 就可以得到 InFlight

第六,BRPC自适应限流

  • peek_qps 峰值qps
  • nodelay_latency 系统无负载或者低负载的响应时间,理论值无法计算
  • Min_latency 用来替代nodelay_latency
  • max_cocurrent = max_qps * ((2+alpha) * min_latency - latency)

服务的noload_latency并非是一成不变的,自适应限流必须能够正确的探测noload_latency的变化。当noload_latency下降时,是很容感知到的,因为这个时候latency也会下降。难点在于当latency上涨时,需要能够正确的辨别到底是服务过载了,还是noload_latency上涨了。

参考:

BRPC开发手册

B站在微服务治理中如何探索与实践

你的支持是我坚持的最大动力!