API 速率限制与节流专家 是做什么的?
使用令牌桶、滑动窗口和固定窗口算法,结合基于 Redis 的分布式计数器,实现公平、高效的速率限制。
触发条件
适用于: “速率限制”、“节流”、“令牌桶”、“滑动窗口”、“API 滥用”、“DDoS 防护”、“配额管理”、“429 Too Many Requests”、“请求限制”
不适用于: API 网关配置 → api-gateway-reverse-proxy-expert | 缓存策略 → cache-strategy-invalidation-expert | WAF/防火墙规则 → 相关安全技能
快速入门
- 定义限制 — 基于 API 密钥、IP 或用户层级的每分钟请求数
- 选择算法 — 滑动窗口日志(精确)、令牌桶(允许突发)、固定窗口(简单)
- 使用 Redis — 通过
MULTI/EXEC或 Lua 脚本实现原子操作,用于分布式速率限制 - 返回正确的请求头 —
X-RateLimit-Limit、X-RateLimit-Remaining、Retry-After - 区分层级 — 免费/专业/企业用户获得不同的限制
核心能力
| 领域 | 技术 |
|---|---|
| 算法 | 令牌桶、滑动窗口日志、滑动窗口计数器、固定窗口 |
| 存储 | Redis 7.4+、Valkey、DragonflyDB、内存(单节点) |
| 库 | rate-limiter-flexible, @upstash/ratelimit, express-rate-limit |
| 网关插件 | Kong rate-limiting, Nginx limit_req, Traefik ratelimit |
| 标准 | RFC 6585 (429)、RateLimit 请求头 (draft-ietf-httpapi-ratelimit) |
架构模式
滑动窗口计数器 (Redis Lua)
-- Redis Lua 脚本:滑动窗口速率限制器
-- KEYS[1] = 速率限制键
-- ARGV[1] = 窗口大小(秒)
-- ARGV[2] = 最大请求数
-- ARGV[3] = 当前时间戳
local key = KEYS[1]
local window = tonumber(ARGV[1])
local limit = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
-- 移除过期条目
redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
-- 计算当前窗口内的请求数
local count = redis.call('ZCARD', key)
if count < limit then
redis.call('ZADD', key, now, now .. '-' .. math.random(1000000))
redis.call('EXPIRE', key, window)
return {1, limit - count - 1} -- 允许,返回剩余数
else
return {0, 0} -- 拒绝,剩余 0
end
多层级速率限制
请求 → IP 速率限制 (100/分钟)
│ 通过
↓
认证检查 → API 密钥速率限制 (基于层级)
│ 免费: 60/分钟
│ 专业: 600/分钟
│ 企业: 6000/分钟
↓ 通过
端点速率限制 (按路由)
│ POST /upload: 10/分钟
│ GET /search: 120/分钟
↓ 通过
处理请求
响应头 (IETF 草案标准)
// 中间件:附加速率限制请求头
function rateLimitHeaders(limit: number, remaining: number, resetAt: number) {
return {
'RateLimit-Limit': limit.toString(),
'RateLimit-Remaining': Math.max(0, remaining).toString(),
'RateLimit-Reset': Math.ceil((resetAt - Date.now()) / 1000).toString(),
};
}
// 返回 429 响应时:
res.status(429).set({
...rateLimitHeaders(limit, 0, resetAt),
'Retry-After': Math.ceil((resetAt - Date.now()) / 1000).toString(),
}).json({ error: 'Too Many Requests', retryAfter: resetAt });
反模式
- 在分布式系统中使用内存计数器 — 速率限制必须在所有实例间共享;请使用 Redis 或等效方案
- 缺少 Retry-After 头 — 429 响应若不含
Retry-After,会导致客户端盲目重试,引发惊群效应 - 仅基于 IP 的速率限制 — IP 限制无法检测基于身份认证的滥用,并且会惩罚共享 IP(如 NAT、VPN);应与 API 密钥限制结合
- 无突发能力的硬性切断 — 令牌桶允许在维持平均速率的同时处理小规模突发;纯固定窗口过于僵化
- 内部 API 不设速率限制 — 行为不端的内部服务可能导致数据库级联故障;始终应加以限制
质量检查清单
- 速率限制使用分布式存储 (Redis/Valkey),而非内存
- 每个响应都包含
RateLimit-Limit、RateLimit-Remaining、RateLimit-Reset请求头 - 429 响应包含
Retry-After请求头 - 不同认证层级(免费/专业/企业)有不同限制
- 对高消耗操作(如上传、搜索)设置按端点限制
- 使用 Lua 脚本或原子管道进行计数器递增(避免竞态条件)
- 速率限制键同时包含身份标识(如 API 密钥)和作用域(如端点)
- 监控仪表盘显示命中率、拒绝次数和主要消费者
- 优雅降级:尽可能采用排队或减速而非直接拒绝