i007.cc

i007.cc

优先队列-降维打击

05.价值资料

全球分布式限流系统 — 完整标准答案

第一步:澄清需求

  • 限流判断是同步的,卡在请求路径上,必须 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 延迟决定了全局同步不能在请求路径上,所有实时判断必须本地完成,全局只做异步校准。

发表回复