Lesson 0010 · 系统设计 · Phase 3 权衡

限流、熔断、降级:系统的自我保护

⏱ 预计 30 分钟 🎯 稳定性三件套,面试必考、工作救命 📖 前置:Lesson 0007 · Lesson 0009
与你的 Mission 的关系

Lesson 0002 教你算容量,但容量算得再准也挡不住尖峰与故障。限流防被打死、熔断防被拖死、降级防被吓死——三件套是面试「你的系统挂了怎么办」的标准答案库,也是工作中值班不爆炸的底气(Stripe、NGINX 的工程实践是主要参考[1][3])。

§1 · 限流Gatekeeper 的四种算法

限流(rate limiting)回答「超过容量的请求怎么办」:快速拒绝(HTTP 429 + Retry-After 头),好过全系统排队慢慢死。四种经典算法[1]

算法机制特点
固定窗口每分钟一个计数器,超过就拒实现最简单;窗口边界突刺:两分钟交界处可达 2 倍限值
滑动窗口日志记录每个请求的时间戳,窗口内超数就拒最精确;内存开销大(O(请求数))
滑动窗口计数相邻两窗口按时间加权插值精度与内存的折中,常用
令牌桶恒速往桶里放令牌,请求取令牌,桶空则拒允许受控突发(桶是突发额度),参数直观(速率+桶深),工业首选

漏桶(leaky bucket)与令牌桶常被混谈:漏桶是恒定速率放行(把突刺削平),令牌桶是平均速率 + 允许突发。选哪个取决于你想「削平」还是「允许瞬时」。详细对比与伪代码见 限流算法速查表

分布式限流的坑

多实例部署时「每实例 100 QPS」≠「全局限 1000 QPS」(流量不均)。全局限流要把计数放到 Redis(原子 INCR + 过期,或 Lua 脚本)——而这一步又引入 Redis 依赖与竞态,工程上常按「实例级粗限 + 全局限精调」双层做。面试里说出「单机限流不等于全局限流」这句话就赢过大多数人。

§2 · 熔断止损的艺术

熔断器(circuit breaker)借自电路保险丝,Fowler 的定义是标准参考[2]。它防的是级联失败:下游 B 变慢 → 你的线程全在等 B → 耗尽 → 你也挂 → 上游跟着挂,全链路雪崩。

状态行为切换条件
Closed正常放行失败率/慢调用率超阈值(如 50%)→ 转 Open
Open直接快速失败,不再碰下游冷却时间(如 30s)到 → 转 Half-Open
Half-Open放少量试探请求试探成功 → Closed 恢复;失败 → 回 Open 再冷却

两个配套:超时——所有跨网络调用必须有超时,没超时的系统熔断都救不了;指数退避重试exponential backoff + jitter)——重试要间隔翻倍并加随机抖动,否则「重试风暴」会把故障放大 N 倍(与 L7 的 DLQ 相接:重试到上限进死信)。

§3 · 降级有舍才有得

降级(graceful degradation)回答「资源不够时,保什么、舍什么」:

三件套的关系一句话:限流是进门排队(拒绝外人),熔断是发现着火切断邻居,降级是着火时决定先搬什么走。

§4 · 随堂检测

§5 · 检索练习

凭记忆回答:① 令牌桶的两个参数各控制什么?它和漏桶的本质区别?② 熔断器三状态与切换条件?③ 为什么重试必须加指数退避和抖动?

核对参考答案

① 速率(稳定流量)+ 桶深(突发额度);令牌桶允许突发、漏桶恒速放行削平突发。② Closed→(失败率超阈)→Open→(冷却到点)→Half-Open→(试探成功/失败)→Closed/Open。③ 故障期全量重试会形成重试风暴放大故障;退避拉开间隔、抖动避免同时起跳。完整对照见 限流算法速查表

§6 · 本周行动

行动任务(约 40 分钟)

挑你系统的一个外部依赖(第三方 API、ES、下游服务),逐项回答:
① 调用有超时吗?设多少?依据是什么?
② 有熔断吗?失败阈值和冷却时间?
③ 重试带指数退避和抖动吗?上限几次?之后去哪(DLQ?)?
④ 它挂了,你的页面/接口给用户什么?(降级预案存在吗?)

§7 · 延伸资源

💬 想现场推演「你们某个依赖挂掉的全链路反应」?把依赖图发我。