API 限流算法设计指南(免费版)
一、它解决什么问题
不限流的系统,一次突发就可能被打垮:
1. 客户端重试风暴:上游超时后无限重试,请求成倍放大;
2. 恶意刷接口:撞库、爬虫、薅券,单用户占满资源;
3. 突发流量:活动/热点事件,连接池、线程池、数据库被打满 → 雪崩。
限流(Rate Limiting):在入口按预设速率放行,超额请求快速失败(返回 429),
牺牲少量超额请求,保住整体可用性。常与熔断、降级配合。
二、三种主流算法
1. 令牌桶(Token Bucket)— 允许突发
- 桶里以固定速率放入令牌,请求到来取走令牌,无令牌则拒绝;
- 特点:桶容量内允许瞬时突发(攒下的令牌可一次用掉),长期受补充速率约束;
- 适合:大多数 API、允许合理突发的对外接口。
2. 漏桶(Leaky Bucket)— 速率恒定
- 请求像水倒入漏桶,以固定速率漏出(处理),桶满则溢出(拒绝);
- 特点:输出速率绝对恒定,能把抖动流量"削平";
- 适合:需要严格保护下游处理速率的场景(如写 MQ、调用第三方配额)。
3. 滑动窗口(Sliding Window Log / Counter)— 边界精确
- 记录窗口内每个请求时间戳,统计"最近 window 秒"内请求数,超限拒绝;
- 特点:无固定窗口在窗口边界可能出现 2 倍突发的问题,计数精确,代价是需记录日志;
- 适合:对边界精度要求高的配额(如"每秒/每分钟 N 次"的严格 API 配额)。
三、选型对比
| 维度 | 令牌桶 | 漏桶 | 滑动窗口 |
|---|---|---|---|
| 允许突发 | 是(容量内) | 否 | 受控 |
| 输出速率 | 平均受限 | 恒定 | 按窗口 |
| 实现成本 | 低 | 低 | 中(存日志) |
| 典型场景 | 通用 API | 平滑下游 | 精确配额 |
> 经验:对外 API 默认选令牌桶;保护固定速率下游选漏桶;
> 计费/严格配额选滑动窗口。
四、分级限流(多维叠加)
从外到内分层,任一维度超限即拒绝:
1. 全局限流:保护整机总 QPS;
2. 接口级限流:核心接口单独配额,重接口更严;
3. 用户/租户级:按 user_id / API key 限制,防单用户占满;
4. IP 级:防匿名刷,配合黑名单。
五、分布式限流
单机内存限流在多实例部署时总配额会翻倍,需集中计数:
- Redis + Lua 脚本:原子完成"取计数→判断→增减→设过期",避免竞态;
- Lua 内实现令牌桶/滑动窗口,保证原子性与高性能;
- 注意时钟以 Redis 为准、key 加窗口标识、异常时 fail-open/fail-close 需明确策略。
六、限流响应规范
- 状态码 429 Too Many Requests;
- 响应头:
Retry-After: <秒>、X-RateLimit-Limit、X-RateLimit-Remaining、X-RateLimit-Reset; - body 返回结构化错误码与可读消息,便于客户端退避;
- 客户端应指数退避 + 抖动重试,不要固定间隔猛打。
七、落地检查清单
- [ ] 明确每级限流阈值(基于压测容量,而非拍脑袋)
- [ ] 选对算法(突发/恒定/精确)
- [ ] 分布式部署用集中计数(Redis+Lua)
- [ ] 返回 429 与 Retry-After
- [ ] 核心接口有独立配额与降级预案
- [ ] 限流触发有监控指标(拒绝数、触发率)与告警