接口限流有哪些常见算法?令牌桶、漏桶、滑动窗口怎么选?

结论先行:限流的目标是让系统在超预期流量下“可控地变慢或被拒”,而不是被击穿。经典算法:固定窗口(计数器实现,简单但窗口边界有突刺)、滑动窗口(把窗口细分时间片,平滑且更精确)、漏桶(恒定速率流出,削峰强但不允许突发)、令牌桶(按速率补充令牌,允许一定突发)。接口层一般选令牌桶(允许突发、实现成熟);下游要求恒定速率时用漏桶;多实例分布式限流用 Redis + Lua 做原子计数或令牌。

算法对比表

算法能否突发实现复杂度代表实现
固定窗口边界可能双倍放行计数器
滑动窗口平滑、接近均速时间片统计
漏桶不允许突发恒定出队速率
令牌桶允许突发Guava RateLimiter

令牌桶示例

// Guava 令牌桶:平均 QPS 上限 100,允许短暂突发
RateLimiter limiter = RateLimiter.create(100.0);

public Response handle() {
    if (!limiter.tryAcquire(1, 200, TimeUnit.MILLISECONDS)) {
        throw new TooManyRequestsException(); // 拿不到令牌快速失败,可映射 429
    }
    return doBusiness();
}

固定窗口与滑动窗口的实现差异

  • 固定窗口:每秒一个计数器;第 59 秒与下一秒初的请求分属两个窗口,边界可能瞬间双倍放行;
  • 滑动窗口:把时间切成小格(如 1 秒分 10 格),统计“当前时刻往前一个窗口”的格子总和,突刺被抹平;
  • 代价:格子越多存储与计算开销越大,分布式场景还需 Lua 保证读写原子性。

分布式限流要点

  1. Redis 方案要“单条 Lua 脚本完成读、判、扣”,避免竞态导致超限;
  2. 令牌补充按“当前时间 - 上次补充时间”计算,再原子扣减;
  3. 被限流的请求返回明确错误码(如 429),方便调用方识别与调整策略;
  4. 阈值与触发量要有监控告警,限流规则随容量评估动态调整。

常见追问 / 记忆点

  • 追问:单机限流在多实例下还准吗?答:不准,每个实例各自计数;全局限流需要 Redis + Lua 原子地扣减或取令牌。
  • 追问:固定窗口为什么有边界突刺?答:窗口边界两侧各能放满一整窗口的流量,瞬间可能两倍流量穿过去。
  • 记忆点:令牌桶“攒令牌、容突发”,漏桶“恒速、强削峰”;能接受小突发选令牌桶,下游要恒速选漏桶。
笔记加载中…