跳到主要内容

限流算法与容量保护

限流把进入系统的请求速率或并发限制在可承受范围内。选择算法前要先确定保护对象:是每秒到达量、同时执行量、租户配额,还是下游某种昂贵资源。

1. 固定窗口计数实现简单

固定窗口把时间切成一分钟或一秒,窗口内计数达到上限后拒绝。它容易用本地原子计数或 Redis 实现,但窗口边界会允许突发:前一窗口末尾和后一窗口开头都打满时,短时间流量可接近两倍阈值。

它适合账单或粗粒度配额,不适合需要平滑保护的关键资源。

2. 滑动窗口提高时间精度

滑动日志记录窗口内每个请求时间,统计准确但内存和清理成本随请求量增长。滑动窗口计数器把时间分成多个小格,按重叠比例近似当前窗口,成本更可控。

格子越细,结果越接近精确滑动窗口,状态和协调成本也越高。限流精度应服务于容量目标,不必追求每个毫秒绝对准确。

3. 漏桶把输出整形成稳定速率

请求进入有界队列,处理方按固定速率取出;队列满时拒绝。它可以平滑突发,但会增加排队延迟,也无法立即利用后端刚释放的额外容量。

队列必须有长度和等待时限。没有边界的“削峰”最终会变成内存耗尽和过期请求堆积。

4. 令牌桶允许受控突发

系统按固定速率生成 token,桶容量限制最多积累多少。请求取得 token 后立即通过,没有 token 则等待或拒绝。

  • 生成速率控制长期平均流量。
  • 桶容量控制一次允许的突发量。

令牌桶适合下游能承受短突发的在线请求。每种请求成本不同,可以按预计 CPU、数据库查询或 token 消耗使用加权配额,而不是一律每请求一个令牌。

5. 并发限制保护在途资源

速率相同的请求,耗时从 20 ms 上升到 2 s 后,在途数量会增加百倍。只限制 QPS 不能防止线程、连接和内存被占满。

并发限制直接约束 active request 数。可根据 Little's Law 用到达率和目标延迟估算初值,再通过压测校准。自适应限制可以根据延迟和排队变化调整,但必须设置安全上下界。

6. 限流键决定公平性

全局限流保护整个服务,按租户、用户、API 或业务键限流则阻止单一来源占满容量。常见组合是:

  1. 入口全局上限。
  2. 租户配额和突发额度。
  3. 单接口或高成本操作上限。
  4. 实例本地并发保护。

限流键来自可信身份,不能只使用容易伪造或共享的 IP。多租户配额还要防止大量低频 key 让限流状态无限增长。

7. 分布式限流在精度与可用性间取舍

中心计数可以更准确执行全局配额,但每次请求远程访问限流服务会增加延迟,并形成新故障点。常见折中包括给各实例预分配 token、本地快速扣减,再周期补充;代价是短时间可能超出全局阈值。

中心服务不可用时,关键写接口可 fail-closed,普通读取则使用本地保守额度继续。无论哪种策略,本地并发上限都应独立存在。

8. 被限流请求尽快返回明确结果

在线请求通常返回 429 或明确业务错误,并在合适时提供 Retry-After。只有等待时间短于剩余 deadline 且队列有界时才排队。

限流阈值要根据压测、下游容量和 SLO 设置。错误率升高时临时降低放行量可以保护系统,但不能用限流掩盖长期容量不足。

9. 常见问题

9.1 令牌桶和漏桶的主要差异是什么

漏桶以固定速度处理队列,输出更平滑;令牌桶按固定速率积累额度,桶内有令牌时允许短时突发。两者都需要容量上限,选择取决于后端是否能承受突发和请求能否等待。

9.2 限制 QPS 后为什么线程池仍会满

请求耗时可能上升,使相同 QPS 对应更多并发任务;还可能有不同接口共享线程池。应同时限制在途并发、队列和下游连接,并观察服务时间。

10. 面试题

10.1 固定窗口、滑动窗口、漏桶和令牌桶如何选择

出现公司:字节跳动、阿里巴巴、滴滴

考察重点

  • 窗口边界、状态成本、平滑输出与突发额度。
  • 速率限制和并发限制的差异。
  • 全局配额、本地保护与失败策略。

相关内容:第 1 节“固定窗口计数实现简单”至第 8 节“被限流请求尽快返回明确结果”。

参考回答

固定窗口实现简单但边界会产生突发;滑动日志精确但状态大,滑动计数用小窗口近似。漏桶通过有界队列按固定速率输出,适合需要平滑处理但会增加等待;令牌桶按固定速率补充令牌,桶容量允许受控突发,更适合在线流量。

算法还要和保护目标匹配。QPS 限制不能防止请求变慢后在途任务增多,所以要同时限制并发、队列和连接。集群可用中心配额或预分配 token 平衡精度与可用性,并按租户和高成本接口保证公平;中心故障时仍保留实例本地保护。