限流算法

限流是高并发服务中非常有效和常用的解决方案,用来限制请求速率,避免服务过载。

常见的限流算法有很多种,如固定时间窗口、滑动时间窗口、漏桶、令牌桶等。

固定时间窗口算法

这是一种比较简单的限流算法,一定时间内,对请求进行计数,达到阈值则暂不接受请求,时间到达临界值则重新计数

比如我们要限制一分钟接受请求 60 次,从第一次收到请求起开始计时,在接下来的一分钟内,每收到一个请求计数就 +1 ,超过限制的 60 次后就开始拒绝请求,直到一分钟计时结束,重新开始计数。

这种算法优点是简单,缺点也很明显,我们没办法控制接收到的请求是按时间均匀分布的。

如果第一分钟的前半分钟只收到了 10 次请求,后半分钟收到了 50 次;第二分钟反过来,前半分钟收到了 50 次,后半分钟收到了 10 次。很明显,中间的两个半分钟(也就是一分钟)内一共收到了 100 次请求,超出了限制,这不是我们想要的。

还有一种情况,前十秒我们收到的请求数达到了最大数,后面五十秒的请求就只能全部拒绝,这种现象也叫**“突刺现象”**。

{% note info 突刺现象 %}

指在一定时间内的一小段时间内就用完了所有资源,后面大部分时间中无资源可用

{% endnote %}

滑动时间窗口算法(Sliding Window)

针对固定时间算法会在临界点存在瞬间大流量冲击的场景,滑动时间窗口计数器算法应运而生。

学过计算机网络的同学,应该都记得滑动时间窗口协议。这个协议是 TCP 协议的一种应用,用于网络数据传输时的流量控制,以避免拥塞的发生。这里的滑动时间窗口算法原理类似,只是应用场景不同。

它将时间窗口划分为更小的时间片段,每过一个时间片段,我们的时间窗口就会往右滑动一格,每个时间片段都有独立的计数器。我们在计算整个时间窗口内的请求总数时会累加所有的时间片段内的计数器。时间窗口划分的越细,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。

漏桶算法(Leaky Bucket)

漏桶算法控制输出速率而不管输入数据的突发性,当输入空闲的时候,该算法不执行任何动作。

就像一个漏水的桶,系统收到的请求就是流入桶里的水,处理请求就是让水从下面流出来,在这里输出的速率是恒定的。漏桶算法通过控制流量输出速率,平滑网络上的突发流量,实现流量整形,以保证网络服务的稳定。

令牌桶算法(Token Bucket)

系统以一定的速度向令牌桶中放置令牌,当接收到请求时,向令牌桶中申请令牌,如果能够申请到,请求正常进行,反之不响应请求。典型情况下,令牌桶算法用来控制发送到网络上的数据的数目,并允许突发数据的发送。

其实 golang 标准库中就有限流算法的实现,即 golang.org/x/time/rate 库,该限流器就是基于令牌桶实现的。

Reference