系统设计 Deep Dive:设计速率限制器(Rate Limiter)
系统设计深度解析:设计速率限制器(Rate Limiter)。涵盖令牌桶、漏桶算法、分布式限流策略与Redis实现方案,还原真实系统设计面试的完整思考过程与面试官追问方向。
面试官真实提问
“请设计一个 Rate Limiter(速率限制器),用于限制 API 的请求频率。比如每个用户每分钟最多 60 次请求。”
“如何扩展到分布式环境?如果有 100 台服务器,如何确保全局限流准确?”
这道题在 Google、Meta、Amazon 的面试中频繁出现,通常考察候选人对算法选择和分布式系统设计的理解。
需求澄清清单
功能需求
Must-have:
- ✓ 限制用户/IP 的请求频率
- ✓ 支持多种限流粒度(每秒、每分钟、每天)
- ✓ 超出限制时返回 429 Too Many Requests
Nice-to-have:
- 多级限流(全局、租户级、用户级)
- 动态调整限流配额
- 限流统计与告警
规模估算
| 指标 | 估算值 |
|---|---|
| API 服务器 | 100 台 |
| 总 QPS | 100 万 |
| 唯一用户 | 1000 万 |
| 限流规则数 | 10 万(不同用户/租户不同配额) |
非功能需求
- 延迟: 限流检查 < 5ms(不能成为瓶颈)
- 可用性: 99.99%(限流失败应该允许请求通过,而不是拒绝)
- 准确性: 分布式环境下 ±5% 误差可接受
第 1 步:高层设计
┌─────────┐
│ 客户端 │
└────┬────┘
│
┌────▼───────────┐
│ API Gateway │ 限流中间件
└────┬───────────┘
│
┌─┴─┐
┌──▼──┐ ┌──▼──┐
│ API │ │ API │ Node A / B
│ A │ │ B │
└──┬──┘ └──┬──┘
└──┬──┘
┌──▼──────┐
│ Redis │ 限流状态存储
│ Cluster │
└─────────┘
核心组件:
- API Gateway:统一的限流入口,拦截所有请求
- API Server:业务逻辑处理
- Redis Cluster:存储限流计数状态,支持原子操作
第 2 步:核心组件设计
★ 限流算法对比(核心考点)
1. 固定窗口计数器(Fixed Window Counter)
时间轴: |--- 窗口 1 (0:00-0:01) ---|--- 窗口 2 (0:01-0:02) ---|
请求数: 60/60 1/60
[注意] 问题:窗口边界突发流量
窗口 1 末尾 60 次 + 窗口 2 开头 60 次 = 1 秒内 120 次
优点: 实现简单,内存占用低 缺点: 窗口边界突刺问题
2. 滑动窗口计数器(Sliding Window Counter)
时间轴:
│── 0:00 (20次) ├── 0:01 (25次) ├── 0:02 (15次) ──│
当前滑动窗口 = 0:01 + 0:02 = 25 + 15 = 40/60
(加权求和,解决固定窗口边界突刺问题)
将固定窗口细分为多个小桶,滑动窗口 = 当前桶 + 前 N 个桶的加权求和。
优点: 解决了固定窗口的边界突刺问题 缺点: 内存占用中等,实现复杂度稍高
3. 令牌桶算法(Token Bucket)
┌─────────────────────────┐
│ 令牌桶 │
│ 容量: 6 │
│ 当前: ●●●○○○ │
│ (●=令牌, ○=空位) │
└─────────┬───────────────┘
│ 请求到达,消耗 1 个令牌
▼
┌─────────────────────────┐
│ 剩余: ●●○○○○ │
│ (2 个令牌) │
└─────────────────────────┘
令牌以固定速率生成,桶满时丢弃多余令牌
优点: 允许突发流量(桶内令牌),实现复杂度适中 缺点: 需要维护令牌生成逻辑
4. 漏桶算法(Leaky Bucket)
请求进入: ●●●●●
│
▼
┌──────────────┐
│ 漏桶 │ ●●●○○○
│ 固定速率流出 │
└──────┬───────┘
│
▼
固定速率处理
(不支持突发流量,平滑输出)
优点: 平滑流量,实现简单 缺点: 不支持突发流量
[对比] 算法对比
| 算法 | 实现复杂度 | 精度 | 突发支持 | 内存占用 | 适用场景 |
|---|---|---|---|---|---|
| 固定窗口 | ★ | 低(边界突刺) | ✗ | 低 | 简单计数 |
| 滑动窗口 | ★★ | 中 | 部分支持 | 中 | 平滑限流 |
| 令牌桶 | ★★ | 高 | ✓ | 低 | API 限流 |
| 漏桶 | ★★ | 高 | ✗ | 低 | 流量整形 |
[重点] 推荐方案:滑动窗口 + 令牌桶组合
滑动窗口控制长时间窗口(如每分钟),令牌桶控制短时间突发(如每秒)。
API 设计
# 限流响应头
X-RateLimit-Limit: 60 # 限流配额
X-RateLimit-Remaining: 45 # 剩余请求数
X-RateLimit-Reset: 1651785600 # 重置时间戳
# 超出限制
HTTP 429 Too Many Requests
{
"error": "rate_limit_exceeded",
"retry_after": 30 # 秒
}
限流层级设计
全局限流 ──→ 租户级限流 ──→ 用户级限流 ──→ IP 级限流
(保护系统) (按租户配额) (按用户配额) (防滥用)
四层限流,从上到下越来越精细
第 3 步:扩展性与优化
[重点] 分布式限流方案
方案 1:集中式(Redis)
- 所有服务器共享 Redis 计数
- ✓ 精确
- ✗ Redis 成为瓶颈,单点故障
方案 2:本地 + 协同(推荐)
- 每台服务器维护本地计数器
- 定期同步到 Redis 做全局校验
- ✓ 低延迟、高可用
- [注意] 有短暂的不精确期
┌── 本地限流 ────────────────┐
│ │
│ 本地计数器 │
│ 5ms 内响应 │
│ │ │
│ ├──→ 允许 → 业务逻辑 │
│ │
└────────┬───────────────────┘
│ 异步
▼
┌── Redis 全局限流 ──────────┐
│ │
│ Redis 计数器 │
│ │ │
│ ├──→ 超限 → 记录日志 + 告警 │
│ │
└────────────────────────────┘
容量估算
| 指标 | 计算 | 结果 |
|---|---|---|
| Redis 内存 | 1000 万用户 × 100 字节 | 1GB |
| QPS | 100 万请求/秒 | 每节点 ~17 万 QPS |
| Redis 集群 | 3 主 3 从 | 6 个节点 |
[问答] 面试官常问 Trade-offs 与实战问答
[问] Q1:你选择了哪种限流算法?为什么?
候选人回答:
“我选择了滑动窗口 + 令牌桶的组合方案。滑动窗口控制长时间窗口(如每分钟 60 次),避免固定窗口的边界突刺问题。令牌桶控制短时间突发(如每秒 10 次),允许用户有一定的突发能力。这种组合既保证了精度,又兼顾了用户体验。”
面试官追问:
“如果只用固定窗口会怎样?”
候选人回答:
“固定窗口在窗口边界会出现突刺问题。比如用户可能在 0:00-0:01 发送 60 次请求,然后在 0:01-0:02 又发送 60 次,导致 1 秒内实际发送了 120 次请求。滑动窗口通过加权平均解决了这个问题。“
[问] Q2:分布式环境下如何保证限流准确性?
候选人回答:
“我采用本地计数 + Redis 异步校验的方案。每台服务器维护本地计数器,快速判断是否超限。同时异步将计数同步到 Redis,由 Redis 做全局校验。这样即使 Redis 暂时不可用,本地限流仍然生效,只是精度会有短暂下降。”
面试官追问:
“如果 Redis 宕机了怎么办?”
候选人回答:
“限流器本身应该 fail-open。如果 Redis 宕机,允许所有请求通过,但本地限流仍然生效。同时触发告警,运维团队紧急修复。限流的目的是保护系统,而不是拒绝服务。“
[问] Q3:如何处理热点用户的限流?
候选人回答:
“对于热点用户(比如大 V 的 API 调用),我会采用多级限流策略。第一级是本地限流,快速失败。第二级是 Redis 限流,精确控制。第三级是全局限流,防止单个用户占满所有资源。同时会监控异常模式,自动触发防护规则。”
面试官追问:
“如果用户投诉限流太严格怎么办?”
候选人回答:
“提供动态限流配额。对于付费用户或重要客户,可以临时提高配额。同时提供限流统计 API,让用户了解当前的使用情况。“
[问] Q4:限流器本身成为瓶颈了怎么办?
候选人回答:
“限流器应该尽可能轻量。我采用本地计数 + Redis 异步校验的方案,限流检查在 5ms 内完成。如果 Redis 成为瓶颈,可以增加 Redis 节点,或者采用本地限流为主、Redis 为辅的策略。”
面试官追问:
“如果本地限流不够精确怎么办?”
候选人回答:
“本地限流的精度取决于同步频率。如果同步频率高(如每秒),精度就高。对于大多数场景,±5% 的误差是可以接受的。如果需要更高精度,可以增加同步频率,或者采用分布式共识算法。“
[问] Q5:如何实现多级限流?
候选人回答:
“全局限流保护系统整体,租户级限流保护多租户隔离,用户级限流保护单个用户,IP 级限流防止滥用。每一级都有独立的计数器和阈值。请求通过所有层级才能被处理,任何一层超限都被拒绝。”
面试官追问:
“如果某一层级限流太严格怎么办?”
候选人回答:
“提供动态调整机制。监控系统负载,自动调整限流阈值。同时提供手动覆盖,允许运维团队临时调整限流策略。“
进阶扩展方向
- 动态限流: 根据系统负载自动调整配额
- 配额管理: 付费用户更高配额
- 分布式协调: 基于 Redisson 的分布式锁
- 可视化监控: 实时限流仪表盘
[注意] 常见踩坑点
| # | 踩坑点 | 解决方案 |
|---|---|---|
| 1 | 时钟不同步:分布式服务器时间不一致 | 用 NTP 或单调时钟 |
| 2 | Redis 单点故障:限流器本身成为单点 | Redis Cluster + fail-open |
| 3 | 内存泄漏:不过期清理限流 key | 设置 TTL + 定期清理 |
| 4 | 精确到毫秒:不需要这么精确 | 秒级足够,降低 Redis 压力 |
总结
Rate Limiter 设计考察:
| 能力 | 考察点 |
|---|---|
| 算法选择 | 4 种限流算法的适用场景和 trade-offs |
| 分布式设计 | 如何在多台服务器间协调限流状态 |
| 降级策略 | 限流器故障时的容错处理 |
| 性能优化 | 5ms 内完成限流检查的要求 |
[重点] 面试提示: 先讲清楚 4 种算法的差异,然后选择一个推荐方案深入。面试官通常会追问”如果 Redis 宕机了怎么办”,准备好 fail-open 的降级策略。
推荐阅读
- 系统设计面试完全指南 — 掌握万能回答框架
- 设计 URL 短链服务(TinyURL) — 另一道入门级经典题目
💡 需要面试辅导?
如果你对准备技术面试感到迷茫,或者想要个性化的面试指导和简历优化,欢迎联系 Interview Coach Pro 获取一对一辅导服务。
👉 联系我们 获取专属面试准备方案