标准答案

  1. 保存上次更新时间和当前令牌数,按经过时间补充但不超过容量。
  2. 请求需要令牌才能通过,不足时拒绝或等待;等待必须有截止时间。
  3. 分布式限流要使用原子脚本或集中式计数,并处理时钟、网络和故障。

题目解析

令牌桶用补充速率控制长期平均通过量,用桶容量控制最多能积累的突发额度。请求消耗令牌,令牌不足时应拒绝或在剩余 deadline 内等待。

实现要基于单调时间计算经过时长,并把令牌数限制在容量以内。分布式场景需要原子地读取、补充和扣减,否则多个实例会同时放行超过额度的请求。

桶容量不是越大越好:它允许更大的瞬时压力进入下游。还要定义成本不同的请求是否消耗不同令牌、时钟异常和限流存储不可用时的默认行为。

代码示例

单机算法核心:

TypeScript
const elapsed = now - updatedAt
tokens = Math.min(capacity, tokens + elapsed * ratePerMs)
updatedAt = now
if (tokens < cost) return false
tokens -= cost
return true

常见误区

  • 误区:只按固定时间窗计数却称为令牌桶。改正:令牌桶必须基于时间补充令牌,并由速率和容量共同控制。
  • 误区:补充令牌不限制最大容量。改正:每次补充都取 min(capacity, tokens + refill)。
  • 误区:各实例本地限流却声称是全局限流。改正:明确是单实例近似,或使用共享原子计数和故障策略。

作者信息