在高并发系统中,限流是一种重要的技术手段,用于保护系统免受过载流量的影响。通过限制请求的速率,限流算法可以确保系统的稳定性和可用性。

固定窗口计数器

  1. 将时间划分为固定的时间窗口,如1秒、1分钟等。
  2. 在每个时间窗口内,记录请求的数量。
  3. 当请求数量超过预设阈值时,触发限流。
  4. 该窗口时间结束后,计数器清零,重新开始计数。
  • 一段时间内(不超过时间窗口)系统服务不可用。如窗口大小为1s,限流大小为100,然后恰好在某个窗口的第1ms来了100个请求,然后第2ms-999ms的请求就都会被拒绝,这段时间用户会感觉系统服务不可用。
  • 窗口切换时可能会产生两倍于阈值流量的请求。即:瞬时流量的临界问题,在最坏的情况下,会让通过的请求量是限制数量的两倍。如窗口大小为1s,限流大小为100,然后恰好在某个窗口的第999ms来了100个请求,窗口前期没有请求,所以这100个请求都会通过。再恰好,下一个窗口的第1ms有来了100个请求,也全部通过了,那也就是在2ms之内通过了200个请求,而我们设定的阈值是100,通过的请求达到了阈值的两倍。

优点:简单易懂,易于实现,内存占用小
缺点:流量曲线可能不够平滑,有“突刺现象”

滑动窗口计数器

滑动窗口算法在固定窗口的基础上,将一个计时窗口分成了若干个小窗口,然后每个小窗口维护一个独立的计数器。
当请求的时间大于当前窗口的最大时间时,则将计时窗口向前平移一个小窗口。平移时,将第一个小窗口的数据丢弃,然后将第二个小窗口设置为第一个小窗口,同时在最后面新增一个小窗口,将新的请求放在新增的小窗口中。同时要保证整个窗口中所有小窗口的请求数目之后不能超过设定的阈值。
滑动窗口计数器通过记录一段时间内的请求数量,当请求数量超过预设阈值时,触发限流。该算法能够适应流量波动的场景。

  1. 将时间划分为细粒度的区间,每个区间维持一个计数器,每进入一个请求则将计数器加一。
  2. 多个区间组成一个时间窗口,每流逝一个区间时间后,则抛弃最老的一个区间,纳入新区间。
  3. 若当前窗口的区间计数器总和超过设定的限制数量,则本窗口内的后续请求都被丢弃。
  • 避免了计数器固定窗口算法固定窗口切换时可能会产生两倍于阈值流量请求的问题
  • 当窗口中流量到达阈值时,流量会瞬间切断

优点:比固定窗口更平滑,能够更好地控制流量。
缺点:实现复杂,内存占用较高。

漏桶算法

漏桶算法允许一定量的请求以恒定的速率通过,超出速率的请求将被丢弃。该算法通过控制请求的流出速率,确保系统不会因为请求过多而崩溃。
优点:流量输出平稳,适合流量整形。
缺点:无法应对突发流量。

令牌桶算法 token bucket

令牌桶算法允许一定数量的请求以恒定的速率通过,并通过增加令牌来允许更多的请求通过。该算法通过动态调整令牌的生成速率,适应不同的流量波动。
Wikipedia的描述:

  1. 每秒会有r个令牌放入桶中,或者说,每过1/r秒桶中会放入一个令牌。
  2. 桶中最多存放b个令牌,如果桶满了,新放入的令牌会被丢弃。
  3. 当一个n字节的数据包到达时,消耗n个令牌,然后发送该数据包。
  4. 如果桶中少于n个令牌,则该数据包将被缓存(或丢弃)。
    优点:支持突发流量,适合流量控制和限速。
    缺点:实现复杂,需要维护令牌数量。

总结

算法 特点 适用场景 |
固定窗口计数器 实现简单,但窗口切换时可能导致流量突增 简单的限流场景
滑动窗口计数器 比固定窗口更平滑,但实现复杂 需要平滑限流的场景
漏桶算法 流量输出平稳,适合流量整形 流量整形、平滑限流
令牌桶算法 支持突发流量,适合流量控制和限速 突发流量处理、API 限流
如果需要简单实现,可以选择固定窗口计数器。
如果需要平滑限流,可以选择滑动窗口计数器或漏桶算法。
如果需要支持突发流量,可以选择令牌桶算法。