限流熔断与降级
流量突增或下游故障时,系统不能无限排队直到拖垮自己。限流、熔断、降级是保护边界的三种手段,常和 Redis、缓存与消息队列 一起出现。
三者分工
| 手段 | 回答的问题 | 类比 |
|---|---|---|
| 限流 | 入口流量是否超限? | 景区限流 |
| 熔断 | 下游已经坏了,还要一直打吗? | 电闸跳闸 |
| 降级 | 保核心、舍非核心? | 战时配给 |
请求 → 限流 → 业务 → 调下游
↓ 失败率/延迟高
熔断打开 → 快速失败或走降级逻辑
限流
| 算法 | 说明 |
|---|---|
| 固定窗口 | 每分钟 N 次;边界可能突发 2N |
| 滑动窗口 | 平滑窗口计数 |
| 令牌桶 | 匀速放令牌,可应对一定突发 |
| 漏桶 | 恒定流出速率 |
| 粒度 | 例子 |
|---|---|
| 全局限流 | 保护整个服务 |
| 用户/IP | 防刷 |
| 接口 | 热点 API 单独配额 |
实现:网关(Nginx/Kong)、应用内(Guava/Sentinel)、Redis 计数。
HTTP 429 Too Many Requests + Retry-After。
熔断(Circuit Breaker)
状态机:Closed → Open → Half-Open
| 状态 | 行为 |
|---|---|
| Closed | 正常调用 |
| Open | 失败率/慢调用超阈值,直接失败,不调下游 |
| Half-Open | 试探少量请求,成功则 Closed |
| 参数 | 说明 |
|---|---|
| 失败率阈值 | 如 50% / 10s |
| 最小请求数 | 样本太少不熔断 |
| 打开时长 | 冷却后再 Half-Open |
与 超时、重试 配合:重试次数过多会放大故障(retry storm)。
降级
| 类型 | 例子 |
|---|---|
| 读降级 | 返回缓存默认值、静态兜底页 |
| 写降级 | 异步化、队列削峰 |
| 功能降级 | 关评论、关推荐,保下单支付 |
| 体验降级 | 简化 UI、提示稍后再试 |
降级要 可配置、可观测、可回滚;别静默丢数据。
和缓存 / MQ 的关系
| 组合 | 场景 |
|---|---|
| 限流 + 队列 | 入口拒绝 + 异步排队(注意积压) |
| 缓存降级 | 下游挂时读 stale cache |
| 熔断 + 降级 | 快速失败 + 兜底响应 |
可观测
| 指标 | 说明 |
|---|---|
| QPS / 拒绝数 | 限流是否过严 |
| 熔断状态 | Open 时长、恢复次数 |
| 降级命中率 | 兜底是否过多 |
常见误解
- 「限流越多越好」——误杀正常用户;要分级与白名单
- 「熔断 = 重启服务」——是调用侧停止打坏下游
- 「降级就是返回 null」——要有业务可接受的兜底
面试速记
- 限流算法? 固定/滑动窗口、令牌桶、漏桶。
- 熔断三状态? Closed / Open / Half-Open。
- 降级和熔断区别? 熔断保护调用链;降级是功能/体验取舍。
- 为什么限流在网关做? 统一入口、早拒绝、省资源。网关动态路由与分布式限流见 API 网关设计与实现。
- 重试和熔断冲突? 盲目重试放大故障;要限次 + 幂等。