算法与权衡
固定窗口简单,但允许在窗口边界突发。滑动窗口记录时间戳并统计最近区间,更准确但占内存。令牌桶按固定速率补充令牌并允许配置突发。漏桶以恒定速率出队,平滑输出而不是接纳突发。
class TokenBucket {
constructor(capacity, refillPerSecond) {
this.capacity = capacity;
this.tokens = capacity;
this.refillPerSecond = refillPerSecond;
this.updatedAt = Date.now();
}
take(count = 1) {
const now = Date.now();
const elapsed = (now - this.updatedAt) / 1000;
this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillPerSecond);
this.updatedAt = now;
if (this.tokens < count) return false;
this.tokens -= count;
return true;
}
}
定义契约
记录键(IP、API Key、用户)、限制、窗口、突发大小和失败响应。超限时返回 429 Too Many Requests 与 Retry-After,并在可用时返回限流头。被拒绝的请求应尽量便宜:在认证或数据库等昂贵工作之前,在边缘执行。
何时使用
对公共 API、登录、密码重置、搜索和昂贵计算使用限流。与防抖/节流相比,限流是服务端准入策略;与重试预算相比,它限制所有流量,而不仅是重试。
陷阱
分布式执行需要共享计数器或近似方案;按实例限制会乘以实例数。按 IP 限流对 NAT 和共享办公网络不公平。过严的限制阻断合法突发,过松的限制没有保护作用。应测量拒绝率和延迟,并随流量模式变化审查限制。