← 中文资源

简体中文版本

返回标准限流响应头

在每个响应中输出 RateLimit-Limit、Remaining、Reset,并在 429 时返回 Retry-After,让客户端主动退避。

阅读英文原文

概述

只有 429 而没有上下文,会迫使客户端盲目重试或猜测退避时间。RateLimit 响应头三件套分别给出配额上限、剩余额度和重置秒数,行为良好的客户端可以在被拒绝前主动降速。Retry-After 仍然是重试时机的权威信号。

步骤

  1. 使用固定窗口计数,并在任何提前返回之前写入响应头:
const used = await redis.incr(`rl:${req.apiKey}:${windowStart}`);
const remaining = Math.max(0, 100 - used);
res.set({
  'RateLimit-Limit': '100',
  'RateLimit-Remaining': String(remaining),
  'RateLimit-Reset': String(60 - (Math.floor(Date.now() / 1000) % 60)),
});
if (used > 100) {
  res.set('Retry-After', '30');
  return res.status(429).json({ error: 'rate_limited' });
}
  1. 网关层自行拒绝请求时也要输出相同响应头,避免客户端看到互相矛盾的配额。
  2. 用循环请求验证:for i in $(seq 1 3); do curl -s -D - -o /dev/null https://api.example.com/v1/items | grep -i -E "ratelimit|retry-after"; done。
  3. 在 OpenAPI 中声明这些响应头,使生成的 SDK 能暴露剩余额度。

验证

预期每次响应的 Remaining 依次递减,Reset 在 1 到 60 之间;第 101 次请求返回 429,且 Retry-After 与 Reset 一致。网关层触发限流时同样应包含这些头,而不是裸 429。

注意事项

  • 响应头名称来自 Internet-Draft,未来可能调整,应在文档中标注版本。
  • 固定窗口在边界允许突发,相邻两个窗口合计可能达到两倍配额;需要平滑时改用令牌桶或滑动窗口。
  • 计数器必须放在共享 Redis 或集中式网关,否则每个副本各自计数,实际配额会成倍放大。
  • 提前返回的分支最容易漏设响应头,应在统一中间件中构造。