# 可靠性与应用保护
作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)
# 一、可靠性基础与组合方式
完全可靠不存在,工程目标是把不可用概率压到可接受范围。
- 失效率/可靠性:R(0)=1,R(+∞)→0。
- SLA/SLI/SLO:度量与目标要前置约定。
# 1. 串联系统(Series)
多个模块必须全部成功,整体可靠性为乘积:
[ R_{series} = \prod_{i=1}^{n} R_i ]
例:5 个模块各 99%,整体:0.99^5 ≈ 95.1%。
# 2. 并联系统(Parallel)
任一模块成功即可,整体可靠性:
[ R_{parallel} = 1 - \prod_{i=1}^{n}(1 - R_i) ]
例:两个各 99%,整体:1 - 0.01×0.01 = 99.99%。
# 3. 冗余投票(k/n 冗余)
n 个模块中任意 k 个有效即可:
[ R_{k/n} = \sum_{i=k}^{n} \binom{n}{i} R^i (1-R)^{n-i} ]
组合可靠性:R_串 < R_冗余 < R_并联。
# 二、提升可靠性的通用设计
- 消除单点:多副本、主备/多主、跨可用区/机房。
- 显式串联改并联:关键路径引入冗余与旁路(读写旁路/缓存旁路)。
- 退化可用:在压力/故障下提供部分功能或部分人群服务。
- 弹性与削峰:并行/并发扩容、缓存、异步化、批处理。
- 故障域隔离:按租户/地域/功能切分,避免牵一发而动全身。
典型读写路径
- Read/Write Through:调用方只与缓存交互,缓存负责与数据库一致。
- Cache Aside:读先查缓存,未命中回源再回填(读有旁路,路径更“抗故障”)。
退化等级(示意)
- L0:全量功能、全量用户。
- L1:全量用户 + 核心功能。
- L2:部分用户 + 核心功能。
- L3:只读或静态页。
- L4:不可用但可快速自愈。
# 三、应用保护“四件套”
# 1)隔离(Isolation)
目标:故障不外溢。从业务维度识别可隔离对象,结合线程池/信号量/连接池做限域执行。
- 线程池隔离:每个依赖独立池,避免线程相互抢占。
- 信号量隔离:以并发信号量限制外呼同时数,开销更低。
- 资源隔离:按功能/租户拆分实例与限额。
原则:最小影响面,以最少资源保护最重要路径。
# 2)限流(Rate Limiting)
目标:把流量控制在系统可承受范围,抵御突刺与雪崩。
常见算法与适用:
| 算法 | 特性 | 适合场景 |
|---|---|---|
| 固定窗口 | 实现简单,但边界抖动 | 粗粒度阈值、后端能消峰 |
| 滑动窗口 | 平滑统计,避免边界突刺 | API 网关、公共接口 |
| 漏桶 | 恒定出水,强节流 | 严格稳定吞吐的后台 |
| 令牌桶 | 允许突发,平均限速 | 面向用户请求、允许短时峰值 |
补充能力:排队(公平性)、优先级(重要请求保活)、预热(冷启动渐进放量)。
# 3)降级(Degradation)
目标:在部分失败下仍可提供“有价值”的服务。
策略清单:
- 精度降级:高精变低精(LBS 降级精度、图像清晰度)。
- 数据来源降级:停止直查 DB → 读取缓存近似值(销量、热度)。
- 同步转异步:写缓存/MQ,前台快速返回。
- 功能剪裁:关闭非关键功能/模块。
- 只读模式/禁写:保护数据一致性。
- 分用户降级:白金用户保真,其它用户近似或排队。
触发方式:自动(错误率/RT/队列长度阈值)、手动(紧急开关/灰度规则)。
# 4)熔断(Circuit Breaker)
目标:保护上游,在下游不稳定时主动中断调用。
- 条件:错误率、超时比例、并发拒绝率达到阈值。
- 行为:进入打开状态,直接失败;经过半开探测成功再闭合。
- 区别:降级是自我行为;熔断是上游对下游的保护。
# 四、恢复与预热
保护手段是临时的,核心是何时、如何撤销:
- 自动恢复:指标回落到安全区即撤出限流/关闭熔断/恢复功能。
- 预热放量:从小流量开始逐级提额,观察 RT/错误率/饱和度。
- 缓存预热:热点键提前装载;避免“恢复一瞬间”击穿回源。
# 五、实战参数与示例代码
# 1)固定窗口(演示思路)
// 仅示意:生产建议使用滑动窗口或令牌桶
class FixedWindowLimiter {
final long windowMs; final int limit;
long winStart = System.currentTimeMillis();
int count = 0;
synchronized boolean allow() {
long now = System.currentTimeMillis();
if (now - winStart >= windowMs) { winStart = now; count = 0; }
if (count < limit) { count++; return true; }
return false;
}
}
2
3
4
5
6
7
8
9
10
11
12
# 2)令牌桶关键点
- 以固定速率生成令牌(r tokens/sec),桶容量(b)允许突发 b。
- 获取不到令牌 → 拒绝或排队;支持分优先级获取。
- 速率按实例可用 QPS与上游峰值估算,预留 20% 余量。
# 3)滑动窗口统计(要点)
- 把窗口拆为 N 个子桶(如 10×100ms),滚动聚合,平滑边界。
- 指标用于限流、熔断判定以及自恢复。
# 六、工程化 Checklist
SLA/目标
- 定义 SLI(可用性、RT、错误率、饱和度)与 SLO/SLA。
- 关键路径与降级等级(L0~L4)对齐业务影响。
隔离
- 依赖级别线程池/信号量隔离。
- 热路径与慢依赖拆分部署,限额独立。
限流
- 算法选择(滑窗/令牌桶),速率、突发、队列、优先级。
- 预热策略与灰度规则。
降级
- 缓存兜底键集合与过期策略。
- 同步转异步与幂等设计。
- 分用户/分功能白名单与熔断回退页。
熔断
- 错误率与超时阈值、半开探测比例与间隔。
- 失败快速(fail fast)与回退(fallback)。
恢复与演练
- 自动撤销保护阈值;分阶段放量。
- 故障注入/压测/容量基线;演练频率与复盘机制。
结语:可靠性的本质是约束下的权衡。先把系统做“坏得优雅”(隔离/限流/降级/熔断),再把系统做“好得稳健”(冗余/弹性/观测/演练)。
← 数据库设计