限流与背压
用途:流量突增、下游变慢时保护自己的系统
三个层次
「三个层次」小节链接| 层次 | 目标 | 手段 |
|---|---|---|
| 入口限流 | 保护自身容量 | 计数器/滑动窗口/令牌桶/漏桶 + 快速拒绝 |
| 并发度控制 | 限制在途请求数 | 信号量、连接池上限、线程池队列 |
| 背压传递 | 让上游感知并降速 | 有界队列 + 拒绝、429/Retry-After、响应式背压 |
并发度控制常被忽略却最有效:按利特尔法则,允许在途数 = 目标 QPS × 下游 P99 耗时。下游变慢时在途数会自然膨胀,限住并发就限住了内存与排队。
算法对比
「算法对比」小节链接| 算法 | 特点 | 适用 |
|---|---|---|
| 固定窗口计数 | 实现最简单,临界点可放行 2 倍流量 | 粗粒度保护 |
| 滑动窗口 | 平滑临界问题,内存与实现稍复杂 | QPS 限流 |
| 令牌桶 | 允许突发(桶内余量),平均速率受控 | 大多数业务入口 |
| 漏桶 | 恒定速率流出,不允许突发 | 保护下游(如批量写入) |
| 自适应并发 | 按 RT 与队列动态调整(梯度/小队列原则) | 下游耗时波动大的场景 |
拒绝之后做什么
「拒绝之后做什么」小节链接限流只有配合降级才有意义:
- 快速失败:返回明确错误码与
Retry-After,不要静默丢弃。 - 排队兜底:短时有界队列(毫秒级),超过即拒。
- 降级路径:返回缓存值、简化结果、关闭非核心功能。
- 优先级:核心链路(支付、下单)优先,非核心(推荐、导出)先拒。
- 配额隔离:按调用方/租户限,防止单个来源拖垮全局。
分布式限流
「分布式限流」小节链接单机限流在集群下需要换算总量。集中式方案(Redis + 原子操作、专用限流服务)准确但有额外 RT 与可用性依赖;本地方案(每台分摊额度 + 定期再平衡)性能好但精度弱。选择依据:是否允许超发与能否接受中心组件故障。
开源参考:Alibaba Sentinel(流控、熔断、热点参数限流)、Resilience4j、Guava RateLimiter(单机令牌桶)、Envoy/Nginx 层限流、API 网关内置配额。
与熔断、超时的关系
「与熔断、超时的关系」小节链接- 超时:必须有,否则线程被占满,限流形同虚设。
- 熔断:下游故障时快速失败,给其恢复空间;半开探测恢复。
- 重试预算:重试流量要计入配额(例如重试不超过总请求的 10%),否则限流被自家重试打穿。
- 退避 + 抖动:避免同步重试造成二次洪峰。
常见误区
「常见误区」小节链接| 误区 | 修正 |
|---|---|
| 只限 QPS 不限并发 | 下游变慢时 QPS 很低但线程已满 |
| 队列越大越“稳” | 只是把故障变成用户超时,且增加 OOM 风险 |
| 限流阈值凭感觉设 | 用压测确定单实例容量,再留 20-30% 余量 |
| 拒绝无监控无告警 | 拒绝次数必须看板化并设阈值 |
| 所有调用方共用一个配额 | 按来源/租户隔离 |
延伸阅读
「延伸阅读」小节链接- Martin Kleppmann《数据密集型应用系统设计》第 6 章(背压与批量)
- Ben Maurer《Rate Limiting, Stores, and Slack》(Meta 的生产实践论述)
- Sentinel / Resilience4j 官方文档