Skip to content

什么是令牌桶算法?工作原理是什么?使用它有哪些优点和注意事项? ​

  • 标签:后端、限流算法

🏛 业务架构师 ​

令牌桶(Token Bucket)是最常用的限流算法之一:以固定速率向桶里放令牌,桶有容量上限;请求到达时取一个令牌——取到则通过,桶空则拒绝/等待。

工作原理(两个参数定义行为):

  • 速率:每秒放入多少令牌 = 允许的平均 QPS;
  • 桶容量:桶里最多囤多少令牌 = 允许的突发量。

例:速率 100/s、桶容量 200——平时限 100 QPS;但流量突增至 250 QPS 时,囤积的令牌能先放行一批突发,超出部分才开始拒绝。

优点:

  1. 允许合理突发:真实业务流量天然有毛刺(整点活动、重试集中),漏桶的严格匀速会误杀正常突发,令牌桶平时限制、峰时容忍,更贴合业务形态;
  2. 支持积攒额度:低频期攒下的令牌可应对短时高峰;
  3. 实现简单、分布式友好(Redis + Lua 原子操作实现集群级令牌桶,Guava RateLimiter 实现单机版)。

注意事项(架构师的风险清单):

  1. 桶容量是双刃剑:容量给太大,突发直穿下游(数据库被瞬间打挂)——容量按下游真实承受力定,不是拍脑袋;
  2. 冷启动问题:空桶启动瞬间低通过率,可用预填充缓解(对应 Sentinel 的 Warm Up 模式思想);
  3. 集群一致性与时钟:Redis 实现依赖时钟,注意集群时间同步与 Redis 故障时的降级策略(Redis 挂了,限流要退化成单机限流而不是全放开);
  4. 被限流后的体验设计:429 响应、排队、友好文案——拒绝策略是业务决策。

一句话:令牌桶 = 平均限速 + 突发容忍,参数设定永远以下游真实容量压测数据为准。

🏗 系统架构师 ​

机制:桶容量 B,以速率 r 恒速往桶里放令牌,桶满丢弃;每个请求取走一个令牌,无令牌则拒绝或等待。长期平均速率等于 r,瞬时突发上限为 B。

与漏桶对比:漏桶恒定速率流出(绝对平滑、拒绝突发);令牌桶允许受控突发(先攒后用)。真实业务流量天然突发(开抢、批量重试),令牌桶对突发友好,是主流默认(Guava RateLimiter、网关 Redis 加 Lua、Sentinel 排队模式的近似)。

优点:参数语义清晰(r 定吞吐、B 定突发容忍)、允许平均速率约束下的弹性、实现简单可分布式化。

注意事项(工程实践):

  1. B 与 r 的容量含义:B 应覆盖下游可承受的瞬时脉冲——如 DB 容量 5000 QPS,配 r=4500 加适当余量;突发放行过量会打穿下游缓存与连接池;
  2. 重试风暴放大:失败重试叠加令牌桶突发,有效压力乘重试次数——重试必须带退避(backoff 加 jitter)并计入限流预算;
  3. 分布式一致性:Redis 加 Lua 保证取令牌原子性,但 Redis 延迟成为路径依赖——高并发可用本地预取令牌摊薄;
  4. 多租户共享桶要做 per-key 公平,防止大租户吃光令牌。

💻 全栈开发专家 ​

以恒定速率往桶里放令牌,桶有容量上限;请求到来时取一个令牌,桶空拿不到就拒绝或等待。两大参数:rate(生成速率 = 长期平均通过速率)、burst/容量(允许的瞬时突发上限)。

工作特性:

  • 允许突发:空闲期令牌攒满桶,突刺流量最多放行"桶容量"个,随后被速率削平——比漏桶(恒速出水)更贴合"偶尔突发是正常"的互联网流量
  • 长期平滑:长期通过率恒等于生成速率,不会超发

优点:兼顾平均速率控制 + 突发容忍,实现简单(计数器 + 时间戳补偿),适合 API/网关限流。

注意事项:

  1. 参数配合业务定:rate = 压测容量,burst = 可接受瞬时峰值(太小正常抖动也被限,太大失去保护意义)
  2. 单机令牌桶在分布式下各算各的,总量失控——集群限流要集中式实现:Redis + Lua 原子化"补发令牌 + 扣减",或 Gateway 的 RequestRateLimiter(Redis 令牌桶)、Sentinel 集群流控
  3. 与漏桶的区别:漏桶强制恒速输出(绝对平滑),令牌桶允许突发按需输出;保护脆弱下游(DB)用漏桶思路更稳

Gateway 实战:

yaml
- name: RequestRateLimiter
  args:
    redis-rate-limiter.replenishRate: 100    # 每秒补 100 个令牌
    redis-rate-limiter.burstCapacity: 200    # 桶容量 200,允许 2 倍瞬时突发

超量请求默认返回 HTTP 429。