跳转至

稳定性

稳定性治理的核心目标是:在部分组件故障时,系统整体仍能提供可接受的服务。本质上是通过一系列防御性设计,将故障的影响范围控制在最小,并尽快恢复。

服务降级

当系统负载过高或下游依赖不可用时,主动放弃部分非核心功能,保障核心链路的可用性。

降级策略

策略 说明 示例
返回兜底数据 使用缓存或默认值代替实时计算结果 推荐列表降级为热门榜单
关闭非核心功能 通过开关关闭边缘特性 关闭商品评论、个性化推荐
简化业务流程 跳过非关键步骤 下单时跳过积分计算,异步补偿
拒绝低优先级请求 按业务优先级分级处理 VIP 用户正常服务,普通用户排队

降级级别

L0(正常)→ L1(轻度降级:关闭非核心功能)→ L2(中度降级:返回兜底数据)→ L3(重度降级:仅保留核心写入链路)

降级设计原则

  • 降级开关应支持动态下发(通过配置中心),不需要重启服务
  • 降级策略需要提前演练,确保兜底逻辑可用
  • 降级后必须有恢复机制:手动恢复或自动探测恢复

熔断

熔断是对下游服务的保护机制。当下游持续异常时,调用方主动切断请求,防止故障扩散和资源耗尽(如线程池被大量超时请求占满)。

熔断状态机

  ┌──── 失败率 < 阈值 ────┐
  │                     │
  ▼                     │
Closed ──失败率≥阈值──► Open ──冷却时间到──► Half-Open
  ▲                                          │
  │                                          │
  └───── 探测请求成功 ─────────────────────────┘
                探测请求失败 → 回到 Open
  • Closed(关闭) — 正常状态,所有请求放行,持续统计失败率
  • Open(打开) — 熔断触发,所有请求直接返回 fallback,不再调用下游
  • Half-Open(半开) — 冷却期结束后,放行少量探测请求。成功则恢复为 Closed,失败则回到 Open

熔断参数

参数 说明 推荐值
失败率阈值 触发熔断的失败比例 50%~60%
统计窗口 计算失败率的时间窗口 10~30 秒
最小请求数 窗口内低于此数量不触发熔断 10~20 次
冷却时间 Open 状态持续时间 5~30 秒
探测数量 Half-Open 时放行的请求数 1~5 次

熔断 vs 降级

  • 熔断是对调用方的保护,自动触发,粒度是单个下游接口
  • 降级是对整体系统的保护,通常由人工或策略触发,粒度是功能模块
  • 两者常配合使用:熔断触发后执行降级逻辑(返回兜底数据)

限流

限流是对自身服务的保护机制。通过控制单位时间内的请求数量,防止系统被突发流量压垮。

限流算法对比

算法 原理 优点 缺点
固定窗口 在固定时间窗口内计数,超过阈值则拒绝 实现最简单 存在临界突刺问题(窗口边界处可能瞬间放过 2 倍流量)
滑动窗口 将窗口细分为多个小窗口,滑动统计 解决临界突刺 实现稍复杂,内存占用更高
漏桶 请求以任意速率进入桶,以固定速率流出 输出速率恒定,保护下游 无法应对合理的突发流量
令牌桶 以固定速率生成令牌,请求需获取令牌才能通过 允许一定程度的突发(桶中有积攒的令牌) 实现相对复杂

令牌桶算法

           固定速率放入令牌
                │
                ▼
请求到达 ──► [获取令牌] ──有令牌──► 放行处理
                  │
                  └──无令牌──► 拒绝 / 排队

令牌桶是最常用的限流算法,兼顾了平滑限流突发容忍

  • 桶容量(burst)决定了允许的最大突发量
  • 生成速率(rate)决定了稳态下的最大 QPS

限流维度

限流不仅仅是全局 QPS 限制,应按多维度精细化控制:

  • 接口级限流 — 按 API 路径限流,核心接口单独配置
  • 用户级限流 — 按用户 ID 限流,防止单个用户刷接口
  • IP 级限流 — 防爬虫、防 DDoS
  • 服务级限流 — 按调用方服务限流,防止某个下游拖垮自己

分布式限流

单机限流只能保护单个实例。在微服务场景下,需要分布式限流来控制整个服务集群的总流量:

请求 → API Gateway / Sidecar → Redis(集中式计数 / 令牌桶) → 后端服务

常见方案:

  • Redis + Lua — 使用 Lua 脚本保证计数的原子性,适合大多数场景
  • Sentinel — 阿里开源的流量治理组件,支持集群限流、热点参数限流
  • API Gateway 限流 — 在网关层统一限流(如 Kong、APISIX)

限流后的处理

限流不等于直接拒绝。应根据场景选择:

  • 快速失败:返回 429 Too Many Requests,适合客户端可重试的场景
  • 排队等待:将请求放入队列,匀速处理,适合削峰场景(如秒杀)
  • 降级响应:返回兜底数据,保证用户体验

超时重试

超时设置

每一次跨服务调用都必须设置超时时间,否则调用方线程 / 协程会被阻塞,导致资源耗尽。

超时设置原则

  • 超时时间应略大于下游的 P99 延迟(如下游 P99 = 200ms,超时设为 500ms)
  • 不同操作设置不同超时:读操作超时 < 写操作超时
  • 链路超时递减:上游超时 > 下游超时(如 Gateway 3s → Service A 2s → Service B 1s),避免下游超时后上游仍在等待

重试策略

策略 说明 适用场景
固定间隔重试 每次间隔固定时间重试 简单场景
指数退避 每次重试间隔翻倍(如 100ms → 200ms → 400ms) 避免重试风暴
指数退避 + 抖动 在指数退避基础上加随机偏移 避免多个客户端同时重试(推荐)
重试间隔 = min(base * 2^attempt + random(0, jitter), max_interval)

重试的注意事项

重试必须满足的前提

  • 接口幂等:重试意味着可能重复调用,下游接口必须支持幂等(通过唯一请求 ID 去重)
  • 限制重试次数:一般不超过 3 次,避免重试风暴压垮下游
  • 仅重试可恢复错误:网络超时、503 可以重试;400、403、404 不应重试
  • 重试预算:整个链路的重试总量应有上限(如 Google SRE 推荐的 retry budget:重试请求不超过总请求的 10%)

负载均衡

负载均衡将请求分散到多个服务实例上,避免单点过载。

常见算法

算法 原理 特点
轮询(Round Robin) 按顺序依次分配请求 简单均匀,不考虑实例差异
加权轮询 按权重比例分配,高性能实例分配更多请求 适合异构集群
最少连接数 将请求分配给当前连接数最少的实例 适合长连接、处理时间差异大的场景
一致性哈希 根据请求特征(如用户 ID)哈希到固定实例 适合有状态服务、缓存亲和
P2C(两次随机选择) 随机选 2 个实例,取负载较低的那个 兼顾性能与均衡,gRPC 默认策略

健康检查

负载均衡需要配合健康检查,及时摘除异常实例:

  • 主动健康检查 — 负载均衡器定期向实例发送探测请求(HTTP / TCP)
  • 被动健康检查 — 根据实际请求的响应状态判断(连续 N 次失败则摘除)
  • 优雅下线 — 服务主动通知注册中心下线,等待存量请求处理完毕后再停止

故障隔离

故障隔离的原则是:一个组件的故障不应影响其他组件。通过隔离手段将故障限制在最小范围内。

隔离维度

维度 说明 示例
线程池隔离 为不同的下游调用分配独立线程池 支付服务和推荐服务使用不同线程池,推荐服务超时不会耗尽支付服务的线程
信号量隔离 限制并发调用数量,不分配独立线程 轻量级隔离,适合本地快速调用
进程隔离 关键服务独立部署,不与其他服务混部 核心交易服务独占容器
机房隔离 不同机房独立运行,故障不跨机房扩散 单元化架构
数据隔离 不同租户 / 业务使用独立的数据库或表 多租户 SaaS 系统

舱壁模式(Bulkhead)

借鉴船舶的水密舱设计:船体被隔板分为多个独立舱室,即使一个舱室进水,也不会影响其他舱室。

                   ┌─────── 线程池 A(支付) ───────┐
                   │  最大 20 线程 | 队列 50        │
请求 ──► 路由分发 ──►├─────── 线程池 B(推荐) ───────┤
                   │  最大 10 线程 | 队列 20        │
                   ├─────── 线程池 C(通知) ───────┤
                   │  最大  5 线程 | 队列 10        │
                   └──────────────────────────────┘

隔离粒度选择

  • 线程池隔离适合 I/O 密集型调用(RPC、HTTP),代价是额外的线程开销
  • 信号量隔离适合计算密集型或本地缓存调用,开销更小
  • 核心原则:越重要的依赖,隔离级别越高

灰度发布

灰度发布(又称渐进式发布)是指将新版本逐步推送给一小部分用户,验证无误后再全量上线,从而降低发布风险。

发布策略对比

策略 原理 优点 缺点
金丝雀发布 先将少量流量(如 5%)导向新版本,逐步增加 风险可控,可快速回滚 需要流量调度能力
蓝绿发布 维护两套完整环境,切流量一次性切换 回滚极快(切回旧环境) 资源成本翻倍
A/B 测试 按用户特征分组,对比新旧版本的业务指标 数据驱动决策 实现复杂,需要数据分析平台
滚动发布 逐批替换旧版本实例 不需要额外资源 回滚速度慢,新旧版本共存时间长

灰度流量路由

灰度发布的关键是精确的流量路由,常见的路由维度:

  • 按比例 — 5% → 20% → 50% → 100% 逐步放量
  • 按用户 ID — 指定测试用户或按 ID 尾号分流
  • 按地域 — 先在某个城市灰度
  • 按请求头 / Cookie — 内部员工携带特定标识,优先使用新版本

灰度验证指标

灰度期间必须持续监控以下指标,确保新版本表现正常:

  • 错误率:新版本的错误率不应高于旧版本
  • 延迟:P50 / P99 延迟不应有明显劣化
  • 业务指标:转化率、下单量等核心业务数据
  • 资源消耗:CPU、内存是否异常上涨

灰度发布注意事项

  • 灰度期间必须有自动回滚机制:关键指标超过阈值时自动切回旧版本
  • 数据库 Schema 变更需要前向兼容:新旧版本代码必须都能正常工作
  • 灰度流量的日志和链路追踪应打上版本标签,便于对比分析

异地多活/容灾

异地多活是稳定性的终极保障,确保单个数据中心故障时业务不中断。

容灾架构模式

模式 说明 RPO/RTO 成本
冷备 备用机房平时不提供服务,灾难时手动切换 RPO 高(数据丢失多),RTO 高(恢复慢)
温备 备用机房有预备资源,但需时间启动 RPO 中,RTO 中
热备(主从) 一个机房承担读写,另一个机房实时同步作为备份 RPO 低,RTO 中 较高
双活 / 多活 多个机房同时提供读写服务 RPO ≈ 0,RTO ≈ 0

RPO(Recovery Point Objective):可容忍的最大数据丢失量

RTO(Recovery Time Objective):从故障到恢复服务的最大时间

异地多活的核心挑战

数据一致性是多活架构最难解决的问题。跨数据中心的数据同步不可避免地面临延迟,需要在一致性和可用性之间权衡:

  • 单元化(Set-based) — 按用户维度(如 UID 尾号)将数据和流量划分到不同单元,每个单元内部自治。跨单元调用走专线,单元故障时将其流量切换到其他单元
  • 数据同步方案 — 基于 Binlog 的异步复制(如 Canal + Kafka),或使用数据库原生的多主复制
  • 冲突处理 — 多个机房同时写入同一条数据时的冲突解决策略(Last Write Wins、业务合并等)

容灾切换

正常状态                              故障切换
┌────────┐    ┌────────┐           ┌────────┐    ┌────────┐
│ 机房 A  │◄──►│ 机房 B  │    ──►   │ 机房 A  │    │ 机房 B  │
│ (活跃)  │    │ (活跃)  │          │ (故障)  │    │ (接管)  │
└────────┘    └────────┘           └────────┘    └────────┘
         DNS / GSLB                        DNS 切换 / 流量调度

切换机制

  • DNS 切换 — 修改 DNS 解析指向,生效时间取决于 TTL(通常 30s~5min)
  • GSLB(全局负载均衡) — 基于健康检查自动切换流量,秒级生效
  • API Gateway 路由 — 在网关层直接切换后端集群

容灾演练

异地多活架构必须定期演练(如每季度一次),验证:

  • 切换流程是否顺畅、切换耗时是否达标
  • 数据同步延迟是否在可接受范围内
  • 切换后业务指标是否正常
  • 回切流程是否可靠

评论