07 - 限流、降级、熔断(稳定性三板斧)
前面几章都在讲「怎么处理更多请求」。这一章反过来:当请求实在太多、处理不过来时,怎么保证系统不被彻底压垮。这就是稳定性三板斧:限流、降级、熔断。
一句话先记住:扛不住的时候,宁可「拒绝一部分、牺牲一部分」,也要保住系统整体活着。
7.1 为什么需要「自我保护」
系统的处理能力有上限。当流量超过上限还硬扛,结果是全体一起死:
不做保护(硬扛):
流量 200% 超载
│
▼
所有请求都变慢 → 都超时 → 用户重试 → 流量更大 → 彻底雪崩
结果:100% 的用户都用不了
做保护(丢卒保车):
流量 200% 超载
│
▼
拒绝 50% → 保住 50% 能正常用
结果:一半用户体验正常,系统活着
核心哲学:高并发下,「部分可用」远好过「全部崩溃」。
7.2 限流:控制进来的请求速率
限流 = 给系统装一个「阀门」,超过设定速率的请求,直接拒绝或排队。
四种经典限流算法
① 固定窗口计数器
每 1 秒计数,超过 100 就拒绝,下一秒清零重来
缺点:窗口临界点可能瞬间涌入 2 倍流量
② 滑动窗口
把时间切成小格,滑动统计,比固定窗口更平滑
③ 漏桶(Leaky Bucket)
请求进桶,桶以固定速率「漏出」处理
特点:出水速率恒定,强制平滑,但不允许突发
④ 令牌桶(Token Bucket)★ 最常用
系统匀速往桶里放令牌,请求要拿到令牌才能通过
特点:允许一定突发(桶里攒了令牌就能瞬间放行一批)
| 算法 | 特点 | 一句话 |
|---|---|---|
| 固定窗口 | 简单,有临界问题 | 每秒清零重数 |
| 滑动窗口 | 更平滑 | 滑动统计最近一段 |
| 漏桶 | 出速恒定,不容突发 | 强制匀速处理 |
| 令牌桶 | 允许突发,最常用 | 攒令牌,凭令牌通行 |
在 FastAPI 里,限流常用 Redis 实现(比如用 Redis 的计数 + 过期,或 Lua 脚本实现令牌桶),因为多实例要共享限流计数。
限流放在哪一层?
[网关限流] ← 挡住整体流量(粗粒度,最外层)
│
[应用限流] ← 保护某个具体接口 / 某个用户(细粒度)
│
[下游限流] ← 保护数据库、第三方 API 不被打爆
7.3 降级:关掉非核心功能,保住核心
当系统压力过大,主动关掉一些不重要的功能,把资源留给核心链路。
电商大促,系统吃紧:
· 关掉「商品评价」「猜你喜欢」「历史浏览」 ← 非核心,降级
· 保住「浏览商品」「下单」「支付」 ← 核心,必须活着
降级的常见形式:
- 返回兜底数据:推荐列表挂了?返回一个默认热门列表
- 返回默认值/空:非关键信息查不到就先不显示
- 关闭功能:直接关掉某个耗资源的功能入口
降级是「有计划的舍弃」:提前想好「压力大时先砍哪些功能」,做成开关,紧急时一键降级。
7.4 熔断:下游挂了,别再往上撞
熔断针对的是依赖的下游服务出问题的场景。它像家里的「保险丝」——下游故障时,快速失败,别让请求堆积。
为什么需要熔断
没有熔断:
下游服务(比如短信网关)挂了,每个请求都要等超时(比如 3 秒)
→ 大量请求卡在这里等待
→ 你的线程/连接全被占满
→ 你自己也被拖垮(故障扩散)
有熔断:
检测到下游频繁失败 → 「熔断打开」→ 后续请求直接快速失败
→ 不再傻等,不占用资源 → 保护自己
熔断器的三个状态
┌─────────┐ 失败率超阈值 ┌─────────┐
│ 关闭 │──────────────▶│ 打开 │
│ (正常) │ │ (快速失败)│
└─────────┘ └────┬────┘
▲ │ 过一段时间
│ 试探成功 ▼
│ ┌─────────┐
└────────────────────│ 半开 │
试探失败 │ (放几个试探)│
└─────────┘
关闭:正常放行
打开:直接失败,不再调用下游
半开:过一会儿放几个请求试探,好了就恢复,还不行继续打开
7.5 三者的关系
系统压力过大 / 下游故障
│
┌─────────┼─────────┐
▼ ▼ ▼
限流 降级 熔断
控制入口 砍非核心 隔离故障下游
(少进来) (省资源) (别被拖死)
| 手段 | 针对 | 做法 | 一句话 |
|---|---|---|---|
| 限流 | 入口流量太大 | 超速率就拒绝 | 少放点进来 |
| 降级 | 系统整体吃紧 | 关掉非核心功能 | 保核心、弃枝叶 |
| 熔断 | 下游服务故障 | 快速失败不再调用 | 别被拖死 |
本章小结
| 你要记住 | 要点 |
|---|---|
| 核心哲学 | 部分可用 > 全部崩溃,丢卒保车 |
| 限流 | 控制进来的速率,令牌桶最常用 |
| 降级 | 主动关非核心功能,保核心链路 |
| 熔断 | 下游故障时快速失败,防止故障扩散 |
| 三者关系 | 限流管入口、降级管自身、熔断管下游 |
下一章预告:前面讲的都是架构层面。回到应用本身——为什么 FastAPI 用 async 能扛更多连接?并发模型的选择,决定单机的上限。
上一章 ← 06 - 读写分离与负载均衡 | 下一章 → 08 - 并发模型与异步 IO