限流算法详解(理论 + 大白话)

限流(Rate Limiting)的核心目的:限制单位时间内的访问次数,防止系统被突发流量打垮或被恶意攻击。

下面依次介绍四种经典算法:固定窗口、滑动窗口、漏桶、令牌桶。


一、固定窗口计数器算法

1. 理论介绍
  • 原理:将时间划分为固定的时间窗口(例如1秒)。每个窗口内维护一个计数器,每来一个请求计数器加1。如果计数器超过阈值(如100次/秒),则拒绝请求。时间进入下一个窗口时,计数器归零。
  • 示例:限制每秒最多5个请求。窗口 [0:00, 0:01) 内处理了4个请求 → 通过;第5个请求通过,计数器=5;第6个请求被拒绝;时间到0:01,计数器清零。
  • 优点:实现极其简单,内存占用小,性能高。
  • 缺点:临界突变问题。在窗口边界处,用户可以在上一秒的最后1毫秒发送100个请求,下一秒的第1毫秒再发送100个请求,导致系统在200毫秒内承受200个请求,可能瞬间崩溃。
  • 适用场景:对精度要求不高的简单防刷场景(如单机限制用户发帖频率)。
2. 大白话解释

餐厅说:“每1分钟只能进10个客人。”计时从12:00开始,到12:01重新算。结果有个客人卡在12:00:59带了10个人冲进去,又卡在12:01:01带了10个人冲进去。餐厅在2秒内实际接待了20个人,直接爆满。
问题:只在整点清零,会被“卡点突击”。


二、滑动窗口计数器算法

1. 理论介绍
  • 原理:对固定窗口的改进。将时间窗口分拆成更小的格子(例如把1秒分成10个100毫秒的格子)。算法始终统计当前时间点往前一个完整窗口长度内所有格子的请求总数。窗口随时间平滑滑动。
  • 示例:限制每秒最多5个请求,精度100毫秒。时间到达0:00:800时,统计窗口 [0:00:000, 0:00:800);到达0:00:850时,窗口滑动到 [0:00:050, 0:00:850)。格子越细,精度越高。
  • 优点:解决了固定窗口的边界突发问题,限流更平滑、精确。
  • 缺点:实现复杂,需要存储每个格子的计数,内存占用随精度增加。
  • 适用场景:对精度要求较高的API限流场景(许多云网关默认使用)。
2. 大白话解释

餐厅不按整分钟清零了,而是把1分钟切成6个10秒的小段。任何时候都看“刚才过去的那一整分钟”里总共有多少人。比如现在是12:00:55,就看12:00:00到12:00:55的客人总数。就算你在59秒冲进去,也要被算在最近这一分钟里,没法钻空子。
优点:更公平,难以“卡点突击”。


三、漏桶算法

1. 理论介绍
  • 原理:将请求看作水,桶容量固定。水先进入桶中,桶以恒定的速率从底部“漏出”并处理。若桶满,新请求被丢弃(溢出)。无论入口流量多么突发,出口速率恒定。
  • 示例:桶容量10,漏出速率2个/秒。瞬间来10个请求 → 全部进桶,以2个/秒的速度处理。瞬间来15个请求 → 10个进桶,5个被丢弃。
  • 优点:输出流量极其平滑、稳定,能强力保护下游系统。
  • 缺点:不支持任何突发流量。即使系统有空闲能力,也无法快速处理积压请求,可能增加延迟。
  • 适用场景:强制流量整形(如数据库保护、网络限速)。
2. 大白话解释

你拿一个桶接水,桶底有个小洞。上面你可以疯狂倒水(很多请求瞬间进来),但桶底只能一滴一滴往外流(处理速度固定)。如果水倒得太快、桶满了,多余的水就溢出去被倒掉(请求被拒绝)。
特点:绝对平滑,绝不接受突发。就像老式马桶水箱,进水快,但下水道就那么粗,必须慢慢流。


四、令牌桶算法

1. 理论介绍
  • 原理:系统以恒定的速率向桶中添加令牌(token)。桶容量固定。请求到达时,必须从桶中获取一个令牌。有令牌则放行,无令牌则拒绝或等待。令牌可以积攒,因此允许一定程度的突发。
  • 示例:生成速率10个/秒,桶容量10。初始桶满(10个令牌)。瞬间来10个请求 → 全部通过,桶空。第11个请求 → 拒绝。0.1秒后生成1个令牌 → 可再处理1个请求。
  • 优点:既限定平均速率,又允许合理的突发流量,比漏桶灵活。实现相对简单(可用原子变量+时间差懒计算)。
  • 缺点:实现比漏桶稍复杂;若桶容量设置过大,突发流量可能仍具破坏性。
  • 适用场景:最通用。适用于API限流、网关限流(Spring Cloud Gateway、Nginx)、微服务保护等。
2. 大白话解释

游乐园每秒往箱子里扔2张票(令牌),箱子最多放10张票。你想玩项目,必须交一张票。如果箱子里有票,你可以一口气拿10张票带10个人进去(允许一次小爆发)。票用完后,后面的人只能等新票扔进来(以每秒2人的速度进场)。
特点:允许临时冲刺,但又不会一直狂冲。比漏桶更灵活,像高速收费站,攒了ETC余额可以连过好几辆。


算法对比总结(理论 + 大白话)

算法理论核心突发处理平滑度实现复杂度大白话一句话典型应用
固定窗口单位时间计数清零有严重边界突发差极低整点重置,会被卡点突击简单计数防刷
滑动窗口细化时间片,滑动统计基本解决边界突发较好中等滚动看最近一分钟,很难钻空子精确API限流
漏桶恒定速率漏出不支持最佳低桶底漏水,出去速度恒定,满了就倒掉强制流量整形,如网络限速
令牌桶恒定速率生成令牌支持(桶容量内)好低攒票入场,允许偶尔快跑几步通用限流,网关、服务保护

分布式限流简介

以上算法通常用于单机限流。在分布式系统中,需要全局限流时,一般采用:

  • 集中式存储:使用 Redis + Lua 脚本实现上述算法。例如:
    • 固定窗口:INCR + 过期时间
    • 滑动窗口:ZSET 按时间戳存储
    • 令牌桶:用 String 存储令牌数,通过时间差计算补充
  • 缺点:每次限流需要网络调用(到 Redis),延迟和性能不如单机内存。Redis 也可能成为瓶颈。
  • 混合方案:按用户ID哈希到不同节点做单机限流(第一道防线),加上 Redis 全局限流(兜底),平衡性能与准确性。

结语

在实际工程中,令牌桶(兼顾平滑与突发)和滑动窗口(精确控制窗口总量)是应用最广泛的两种算法。选择时可根据业务需求:需要强制恒定速率选漏桶,需要允许合理突发选令牌桶,需要精确时间窗口总量选滑动窗口。

更多推荐