WangYu::Space

Study, think, create, and grow. Teach yourself and teach others.

常见限流算法

分类:算法创建时间:2024-04-14 17:07:05

背景

在网络应用程序中,用于控制请求的流量,防止系统过载,会使用限流算法对请求做限流。最近我参与的一个项目中需要限制最大 QPS,以及最大的读写带宽。这都需要使用到限流算法。 本文中我会系统地梳理了常见的限流算法,并分析其优缺点和适用场景。

固定窗口计数算法 (Fixed window counter algorithm)

固定窗口计数算法的基本思想是将时间轴分成多个固定大小的窗口,每个窗口内统计请求的次数。如果请求次数超过了阈值,就拒绝请求。比如窗口时间为 1 秒,限流 QPS 为 1000,每次请求到达时,将计数器加 1,如果计数器的值超过了 1000,则拒绝请求。

固定窗口计数算法的实现比较简单,存在较多问题。当流量较大时,在单位时间的刚开始的阶段,请求的次数可能就超过了阈值,在后续的时间里所有请求都会被拒绝,而在单位时间的前半段允许请求。这可能会导致系统的负载不均衡。

fixed window counter algorithm

如果突发的流量横跨两个时间窗口,比如下图中,在前前后两个时间窗口内均处理了 5 个请求,但在同样的时间范围实际处理了 10 个请求。

滑动窗口计数算法 (Sliding window counter algorithm)

滑动窗口算法是对固定窗口计数算法的改进,解决固定窗口计数算法中突发流量可能超过阈值的问题。在滑动窗口算法中,时间窗口是不断滑动的,窗口的起点是当前时间点减去窗口的大小。在判断是否拒绝请求时,需要判断当前时间窗口内的请求数量是否超过了阈值。

在计算滑动窗口内已经处理的请求数量时,会使用滑动窗口和前一个窗口重叠的比例。设重叠比例为 pp,前一个窗口的请求数量为 nn,当前窗口的请求数量为 mm,那么滑动窗口内已经处理的请求数量为 p×n+mp \times n + m

滑动窗口算法利用前一个固定窗口的请求数量,并使用重叠比例,来预估当前滑动窗口内可以处理的请求数量。这可以避免固定窗口计数算法中突发流量可能超过阈值的问题,可以让流量更加平滑地被处理。

滑动窗口日志算法 (Sliding window log algorithm)

滑动窗口日志算法记录了每个请求的时间戳,当请求到达时,使用当前时间减去窗口的大小,得到窗口的起点。先将所有小于窗口起点的时间戳从日志中移除。如果当前时间窗口的日志数量超过了阈值,就拒绝请求。否则,就将当前请求的时间戳放入日志中。

上图中,队列的容量为 5,时间窗口为 1 分钟。前两幅子图中,队列未满,请求被接受。而在第三幅子图中,队列已满,请求被拒绝。第四幅子图中,删除了队列头部已经过期的元素,队列中有了空间,请求被接受。

可以看到滑动窗口日志算法的实现很直观,就是将窗口内的请求的时间戳保存下来,这样可以始终精确地获知到当前滑动窗口内已经处理了多少个请求,这样就可以准确地实现限流,避免了固定窗口计数算法中突发流量可能超过阈值的问题。

因为需要维护一个日志数据结构,所以会占用额外的内存空间。尤其是限流阈值比较大时,这种算法就不适用了。但此算法可以严格保证,在单位时间内,请求的数量不会超过阈值。

令牌桶算法 (Token bucket algorithm)

令牌桶算法的基本思想是维护一个令牌桶,并以恒定的速率生成令牌放入令牌桶中。当令牌桶已满时,新生成的令牌会被丢弃。当请求到达时,需要从令牌桶中获取令牌。如果成功获取到令牌,则对请求进行处理;否则,对请求进行限流。

令牌桶算法的一个优势是可以平滑地处理流量,因为令牌桶以恒定的速率生成令牌,当令牌消耗完后,令牌生成的速率就可以控制请求处理的速率。而如果流量不均匀,有时会有突发的流量,令牌桶中堆积的令牌可以用于应对这些突发流量。

令牌桶算法有两个参数:

我认为令牌桶算法的精髓在于引入了令牌桶作为缓冲,以应对突发流量。如果没有令牌桶,而只是以恒定速率生成令牌,这就和固定窗口计数算法一样了。

漏桶算法 (Leaking bucket algorithm)

漏桶算法的基本思想是维护一个容量固定的桶,这个桶里装的是请求。当一个请求达到时,将请求放入桶中。如果桶满了,就拒绝请求。另外一边,系统会以恒定的速率从桶中取出请求进行处理。

漏桶算法中,请求处理的速度决定了限流的速度。如果请求处理的速度大于等于令牌生成的速度,那么就不会有请求被拒绝。如果请求处理的速度小于令牌生成的速度,那么待处理的请求就会堆积,这个时候部分请求会被拒绝。

总结

本文中我系统地梳理了常见的限流算法,并分析了各自的特点:

限流算法种类不少,但我认为令牌桶算法和固定窗口计数算法是最常用的两种限流算法。在我参与的项目中,对限流准确性要求不高的场景下,通常使用固定窗口计数算法,比如用来限制每秒最多打印的日志数量。而对限流准确性和流量处理的平滑度要求高的场景下,可以使用令牌桶算法,比如限制带宽、QPS 等等。

评论 (评论内容仅博主可见,不会公开显示)