全球分布式限流系统 — 完整标准答案
第一步:澄清需求
- 限流判断是同步的,卡在请求路径上,必须 5ms 内完成
- 5% 误差容忍意味着偶尔放行 1050 次是可以接受的
- 跨 Region 延迟 80-150ms,这个数字决定了不能做跨 Region 同步判断
第二步:核心矛盾
全球共享配额(需要汇总 5 个 Region 的数据)
↕ 无法调和
P99 < 5ms(跨 Region 一次网络请求就超标)
结论:每次请求的限流判断必须纯本地完成,全局同步只能异步进行。
第三步:双层限流架构
第一层:本地限流(毫秒级,硬保护)
每个 Region 的限流 service 在本地内存中维护计数器,纯内存操作,无任何网络调用:
本地配额 = 全局配额 / Region 数量
= 1000次/分钟 / 5个Region
= 200次/分钟 per Region
≈ 34次/10秒 per Region
每个请求进来:本地计数器 +1,超过本地配额立即拒绝。全程 < 1ms。
第二层:全局限流(秒级,精确配额)
各 Region 限流 service
↓ 每 5 秒批量上报本地计数(后台任务,不在请求路径上)
Master Redis(部署在流量最集中的 Region)
↓ 汇总全球数据,计算滑动窗口(60秒)
↓ 发现超额 → 下调各 Region 配额
各 Region Slave Redis(只读,从 Master 同步)
↓ 本地限流 service 从 Slave 读取最新配额
第四步:动态配额分配
静态平均分配(每 Region 固定 200次/分钟)浪费配额。Master Redis 每 5 秒根据实际使用量动态调整:
初始分配:北美 200,欧洲 200,亚太 200,南美 200,中东 200 观察 5 秒: 北美实际用了 180,亚太只用了 40 动态调整: 北美 → 300,亚太 → 100,其余不变 总量仍是 1000次/分钟
这样流量集中的 Region 获得更多配额,整体利用率更高。
第五步:完整请求链路
用户请求到达北美 API Gateway
↓
限流 service 检查本地内存计数器(< 1ms)
├── 超过本地配额(34次/10秒)→ 立即拒绝,返回 429
└── 未超过 → 放行,计数器 +1
后台异步(每 5 秒):
本地 service → 批量上报计数 → Master Redis
Master Redis → 滑动窗口计算 → 调整各 Region 配额
Master → 同步 → 各 Region Slave Redis
Slave → 更新本地 service 的配额上限
第六步:算法选择——滑动窗口
Master Redis 使用 Sliding Window Log(滑动窗口日志):
python
# Redis 中每个用户一个 Sorted Set
# key: rate_limit:{user_id}
# score: 时间戳(毫秒)
# value: 请求唯一 ID
ZADD rate_limit:user123 <timestamp> <request_id>
ZREMRANGEBYSCORE rate_limit:user123 0 <now - 60秒>
count = ZCARD rate_limit:user123
if count > 1000:
触发限流,下发配额削减指令
各 Region 上报的是带时间戳的请求数,Master 在精确的 60 秒滑动窗口内统计。
第七步:故障处理
Master Redis 宕机 → Fail Open(故障开放)
检测方式:Circuit Breaker 连续 3 次写入 Master 超时(> 200ms)→ 自动降级 降级行为: 全局限流失效,仅依赖本地限流 本地配额保持最后一次从 Master 收到的值不变 恢复行为: Master 恢复后进入 1 分钟冷却窗口 重新收集各 Region 数据,建立全局视图 冷却结束后恢复全局限流执行
Fail Open 理由:限流是服务质量保障,不是安全边界。短暂超额(因本地限流依然存在,超额有限)远好过服务完全中断。
第八步:存储容量
每用户 Sorted Set: 1000次请求 × (时间戳 8B + ID 16B) = 24KB/用户 100万用户: 24KB × 1,000,000 = 24GB Master Redis:1主1从,24GB 数据量,单机可以承载 各 Region Slave:同上,只读
关键设计原则总结
| 原则 | 实现 |
|---|---|
| 请求路径零跨 Region 调用 | 本地内存判断,< 1ms |
| 全局精确配额 | Master Redis 异步汇总,5 秒延迟 |
| 流量不均匀 | 动态配额分配,每 5 秒调整 |
| Master 故障 | Fail Open + Circuit Breaker |
| 误差控制 | 本地配额 × 5 Region ≈ 全局配额,宕机期间超额可控 |
这道题的核心洞察只有一句话:跨 Region 延迟决定了全局同步不能在请求路径上,所有实时判断必须本地完成,全局只做异步校准。
