限流算法 滑动窗口 VS 令牌桶


滑动窗口保证“短期高峰精确控制”。 而令牌桶提供“平均速率 + 突发能力”控制。

“滑动窗口”适合做统一指标计算
多维度限流例子: key = userId + api , key = tenantId + api, key = api

你问得非常关键,这涉及到 限流设计的精度、性能和业务灵活性权衡。我们可以从几个角度分析为什么 Sentinel 选择 滑动窗口统计 + 令牌桶 的组合,而不是单一实现。


1. 令牌桶算法的特点与局限

令牌桶(Token Bucket)的核心思想:

  • 系统以固定速率生成令牌(token),每个请求消耗一个令牌。
  • 如果没有令牌,请求被拒绝或等待。
  • 可以允许短时间突发流量(令牌积累)。

优点

  • 支持平均速率控制 + 突发流量。
  • 实现简单,适合单机限流。

缺点

  • 精度受令牌生成频率影响:如果生成间隔较长,瞬时峰值控制可能不够精细。
  • 对复杂场景(如多维度、动态阈值、集群限流)支持有限。
  • 无法直接统计历史访问数据(滑动统计),难以判断短时间 QPS 高峰的趋势。

2. 滑动窗口统计的特点与局限

滑动窗口(Sliding Window)核心思想:

  • 将时间划分为若干小窗口(如 100ms),统计每个小窗口的访问量。
  • 请求通过时检查最近 N 个窗口的累计访问量是否超过阈值。

优点

  • 精确统计短时间内 QPS(高频访问)的峰值。
  • 可以精细控制瞬时流量,避免突发流量超限。
  • 对复杂规则(如动态阈值、多维流控)支持更好。

缺点

  • 对平均速率和突发能力的支持不如令牌桶直观。
  • 实现稍复杂,维护窗口数据结构需要额外内存/CPU。

3. 为什么要结合两者

在实际业务中:

  1. 希望控制平均流量 → 令牌桶天生支持。
  2. 希望控制短时峰值,避免瞬间流量冲击 → 滑动窗口更精确。
  3. 希望支持多维限流(如按用户 + API)和集群模式 → 结合滑动窗口统计和令牌桶可以统一指标计算和令牌发放。

组合优势

  • 令牌桶提供“平均速率 + 突发能力”控制。
  • 滑动窗口保证“短期高峰精确控制”。
  • 在集群模式下,窗口统计可以作为统一阈值判断,令牌发放保证分布式一致性。
  • 避免单一算法在极端场景下出现过量放行或过度拒绝。

4. 业务示例

假设一个接口 QPS 阈值为 100:

  • 单令牌桶:短时间内可能瞬间放行 200 请求,因为令牌积累。
  • 单滑动窗口:控制瞬时 QPS 精确,但不允许短暂突发。
  • 结合方式:令牌桶允许突发,滑动窗口保证整体高峰不超限 → 平衡用户体验和系统保护。

总结

算法 瞬时精度 平均速率控制 突发支持 集群可控性
令牌桶
滑动窗口
组合

因此,结合两者可以兼顾精度、突发控制、平均速率和分布式可控性,这是单一实现难以兼顾的。


如果你希望,我可以画一个 “滑动窗口 + 令牌桶限流示意图”,直观展示 Sentinel 内部是如何结合这两种机制的。

你希望我画吗?

为什么“滑动窗口”适合做统一指标计算

这个问题本质上是在问:为什么在“多维限流 + 集群限流”场景下,单纯用令牌桶或单纯用滑动窗口都会变得别扭,而两者结合反而更自然。我们分三层来解释。


一、先看两个难点到底是什么

1. 多维限流的难点

多维限流的典型形式是:

  • user + API
  • app + API
  • 租户 + API + 资源类型

难点不在限流本身,而在“指标统计与复用”

  • 同一请求,会同时命中多条规则

  • 每条规则都需要:

    • 统一的 QPS / 并发等指标定义
    • 可复用、可组合的统计口径

如果每一维都搞一套“令牌生成逻辑”,复杂度会迅速失控。


2. 集群限流的难点

在集群模式下,核心问题是:

谁来决定“这个请求能不能过”?

如果每个实例自己算:

  • 会出现 超卖(每台机器都以为还有额度)

所以必须:

  • 有一个全局视角的指标
  • 有一个集中或逻辑集中的放行决策

二、为什么“滑动窗口”适合做统一指标计算

1. 滑动窗口本质是“事实统计”

滑动窗口做的事情只有一件:

在某个维度上,精确统计最近一段时间发生了多少真实请求

这有几个关键好处:

  1. 天然支持多维

    • key = userId + api
    • key = tenantId + api
    • key = api
      同一套统计逻辑,只是 key 不同
  2. 指标可复用

    • QPS
    • 并发
    • 失败率
      都可以基于同一套窗口数据派生
  3. 集群友好

    • 在 Cluster 模式下,所有请求最终汇聚到:

      • Token Server
      • 或聚合节点
    • 滑动窗口可以作为“全局事实账本”

一句话总结
👉 滑动窗口更适合当“统一计量体系”。


三、为什么“令牌桶”适合做放行(发令牌)

1. 放行决策必须“快、简单、可分发”

在集群中,如果每个请求都要做复杂统计判断:

  • 网络 RTT 高
  • 吞吐下降
  • Token Server 成为瓶颈

令牌桶在这里的优势是:

  • 放行判断是 O(1):有没有令牌
  • 结果是离散的:给 / 不给
  • 天然适合下发给客户端

在 Sentinel 集群模式中:

  • Token Server:

    • 基于滑动窗口判断当前是否还能放行
    • 决定发多少 token
  • Client:

    • 只做一件事:拿 token → 放行请求

四、两者如何“协同工作”

可以把职责划分得非常清楚:

1. 滑动窗口:算“还能不能放”

  • 站在 全局视角

  • 基于多维 key

  • 精确判断:

    • 当前 QPS 是否接近阈值
    • 是否需要收紧流量

2. 令牌桶:管“怎么放”

  • 站在 执行层

  • 用 token 表达“放行权”

  • 支持:

    • 本地快速判定
    • 一定程度的突发缓冲
    • 批量下发,减少网络调用

五、用一句类比帮你“顿悟”

可以把整个系统类比为 高速公路收费站

  • 滑动窗口 = 中控室的实时车流监控系统

    • 统计每条高速、每个入口、每类车辆的流量
  • 令牌桶 = 发给各收费站的“通行票”

    • 收费站只负责验票
    • 不需要自己判断全局拥堵情况

👉 统计(事实)和放行(权利)被彻底解耦


六、为什么单一实现会很别扭

方案 问题
只用令牌桶 多维 key 爆炸、集群难以校准真实 QPS
只用滑动窗口 每次请求都要集中判断,集群性能差
滑动窗口 + 令牌桶 统计精确 + 放行高效

最核心的一句话

滑动窗口负责“统一、精确地算事实”,令牌桶负责“高效、可分发地给权限”。
在多维 + 集群场景下,这两件事必须分离,否则复杂度和性能都会失控。

如果你愿意,下一步我可以用 Sentinel Cluster Flow 的实际调用链(Client → Token Server)把这个过程逐步拆开,对应到具体类和数据结构。

posted @ 2025-12-25 15:09  向着朝阳  阅读(68)  评论(0)    收藏  举报