# 可靠性与应用保护

作者: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_并联


# 二、提升可靠性的通用设计

  1. 消除单点:多副本、主备/多主、跨可用区/机房。
  2. 显式串联改并联:关键路径引入冗余与旁路(读写旁路/缓存旁路)。
  3. 退化可用:在压力/故障下提供部分功能部分人群服务。
  4. 弹性与削峰:并行/并发扩容、缓存、异步化、批处理。
  5. 故障域隔离:按租户/地域/功能切分,避免牵一发而动全身。

典型读写路径

  • 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)

目标:保护上游,在下游不稳定时主动中断调用

  • 条件:错误率、超时比例、并发拒绝率达到阈值。
  • 行为:进入打开状态,直接失败;经过半开探测成功再闭合。
  • 区别:降级是自我行为;熔断是上游对下游的保护

# 四、恢复与预热

保护手段是临时的,核心是何时、如何撤销

  1. 自动恢复:指标回落到安全区即撤出限流/关闭熔断/恢复功能。
  2. 预热放量:从小流量开始逐级提额,观察 RT/错误率/饱和度。
  3. 缓存预热:热点键提前装载;避免“恢复一瞬间”击穿回源。

# 五、实战参数与示例代码

# 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;
  }
}
1
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)。

恢复与演练

  • 自动撤销保护阈值;分阶段放量。
  • 故障注入/压测/容量基线;演练频率与复盘机制。

结语:可靠性的本质是约束下的权衡。先把系统做“坏得优雅”(隔离/限流/降级/熔断),再把系统做“好得稳健”(冗余/弹性/观测/演练)。