系统设计面试:如何设计高可用微服务系统(快速容灾与流量切换)
高可用微服务是每一场系统设计面试的必考题。本文以”设计一个可以快速容灾的微服务系统”为主线,完整梳理面试官期待听到的答案框架:如何检测故障、如何切换流量、如何恢复,以及背后的关键权衡。
1. 面试题还原与破题
面试官常见问法:
“请设计一个微服务系统,要求当某个服务或整个可用区出现故障时,能在 30 秒内完成流量切换,并且用户感知的错误率控制在 0.1% 以下。”
破题三步走:
- 澄清需求:RTO(恢复时间目标)是多少?RPO(恢复点目标)是多少?是单服务故障还是整个 AZ/Region 级故障?
- 拆解子问题:故障检测 → 决策 → 流量切换 → 验证 → 回滚
- 分层设计:基础设施层(DNS/LB)、服务层(熔断/限流)、数据层(主从/多活)
2. 整体架构图
1 | ┌─────────────────────────────┐ |
关键组件:Global Load Balancer(流量入口)、双 Region 部署、跨 Region 数据同步、统一的可观测平台。
3. 故障检测
容灾的第一步是”知道坏了”。检测越快,RTO 越低。
3.1 分层检测体系
| 层次 | 检测手段 | 典型延迟 |
|---|---|---|
| 基础设施 | Cloud Provider Health Check、BGP 路由收敛 | 1–5s |
| 负载均衡 | LB Active Health Check(HTTP 200 + 延迟阈值) | 2–10s |
| 服务网格 | Envoy/Istio Outlier Detection(连续错误率) | 秒级 |
| 业务指标 | 成功率 / P99 / 业务转化率 | 秒-分钟级 |
| 综合判断 | 告警聚合 + 自动决策引擎 | 数秒 |
3.2 Active Health Check vs Passive Detection
Active Health Check:
- LB 或 Sidecar 定期发探针请求(
/healthz),连续 N 次失败则摘除 - 优点:主动发现、可精确控制阈值
- 缺点:探针不代表真实流量路径,可能漏报”局部故障”
Passive Detection(Outlier Detection):
- 观测真实请求的错误率/连接超时,动态驱逐异常实例
- Envoy 配置示例:
1
2
3
4
5outlier_detection:
consecutive_5xx: 5 # 连续 5 次 5xx 触发驱逐
interval: 10s
base_ejection_time: 30s
max_ejection_percent: 50 # 最多驱逐 50% 的实例,防止雪崩
最佳实践:两者结合,Active 负责”快速摘除死实例”,Passive 负责”识别抖动实例”。
3.3 关键 SLI 指标
面试时需要说清楚监控什么:
1 | 成功率 = (成功请求数) / (总请求数) 目标 > 99.9% |
告警策略建议用 多窗口告警(如同时满足 5min 窗口和 1min 窗口),避免瞬时抖动触发误报:
1 | # 伪代码:多窗口 SLO 告警 |
4. 流量切换
检测到故障后,核心问题是”怎么切”。面试官会追问:会不会切出新的故障?
4.1 流量切换层次
1 | Layer 1: DNS 切换(分钟级) |
4.2 金丝雀切流程序
永远不要一刀切,切流应该是渐进式的:
1 | T=0s 检测到 Region A 错误率 > 5% |
关键判断:在每个阶段都要检查目标 Region 是否能承受新增流量(容量预留)。
4.3 熔断器(Circuit Breaker)
熔断器是服务层最常用的自保手段,三种状态:
1 | Closed(正常) |
配置参数(以 Resilience4j 为例):
1 | resilience4j.circuitbreaker: |
4.4 限流与降级
切流过程中,目标 Region 会承接额外流量,必须有限流兜底:
令牌桶 vs 漏桶:
| 令牌桶(Token Bucket) | 漏桶(Leaky Bucket) | |
|---|---|---|
| 允许突发 | ✅ 是 | ❌ 否 |
| 输出速率 | 不均匀 | 匀速 |
| 适用场景 | API 限流(允许短时突发) | 后端保护(平滑流量) |
降级策略(按优先级):
- 返回缓存数据(Cache Fallback)
- 返回默认值(Default Response)
- 调用降级服务(Simplified Service)
- 直接拒绝并返回 429(最后手段)
5. 数据层容灾
流量切换了,数据怎么办?这是面试最容易被追问的地方。
5.1 RPO vs RTO 权衡
| 策略 | RTO | RPO | 成本 |
|---|---|---|---|
| 冷备(Backup & Restore) | 小时级 | 数小时 | 低 |
| 温备(Warm Standby) | 分钟级 | 秒级 | 中 |
| 热备(Active-Passive) | 秒级 | 近零 | 高 |
| 多活(Active-Active) | 毫秒级 | 零 | 极高 |
5.2 主从切换(Active-Passive)
适合大多数互联网场景,要点:
- 异步复制:主库写入后异步同步到从库,RPO < 1s 可接受
- 自动 Failover:使用 MySQL MHA / PostgreSQL Patroni / Redis Sentinel 做自动主从切换
- 防止脑裂:引入外部仲裁(如 etcd / ZooKeeper),确保同一时刻只有一个主库
1 | Primary (Region A) |
5.3 多活数据库(Active-Active)
适合对 RTO/RPO 极致要求的场景(如支付、交易):
- 分片(Sharding):按用户 ID / Region 分片,每个 Region 只写自己的分片,避免跨 Region 写冲突
- 冲突解决:Last-Write-Wins(LWW)或 CRDT(Conflict-Free Replicated Data Types)
- 全局事务:使用分布式事务(2PC / Saga),但需接受延迟升高
5.4 缓存层容灾
Redis 常见容灾方案:
1 | 单 Region:Redis Sentinel(1主2从+哨兵) |
6. 自动化容灾决策
6.1 容灾编排流程
1 | # 容灾决策引擎伪代码 |
6.2 人工干预节点
自动化容灾中必须保留人工干预点:
- 切流到 100% 之前需要人工确认(防止自动化把坏流量带到健康 Region)
- 数据库主从切换 建议半自动(系统检测 + 人工执行,防脑裂)
- 回滚操作 必须随时可执行,且有明确的 Rollback Runbook
7. 混沌工程验证
容灾方案设计完不等于能用,需要通过混沌工程持续验证。
7.1 常见注入故障类型
| 故障类型 | 注入方式 | 验证目标 |
|---|---|---|
| 服务实例宕机 | kill -9 进程 | Outlier Detection + 服务发现 |
| 网络延迟 | tc netem / toxiproxy | 超时配置、熔断 |
| 网络丢包 | iptables drop | 重试策略 |
| 磁盘满 | fallocate 写满 | 错误处理、告警 |
| AZ 故障 | 关闭整个 AZ 路由 | 跨 AZ 切流 |
| Region 故障 | 全 Region 流量归零 | 跨 Region DR |
7.2 GameDay 演练
定期(每季度)进行 GameDay:
- 提前通知相关团队,确定演练时间窗口
- 注入故障,观察系统自动响应
- 如超过 RTO 未恢复,人工接管
- 演练后写 Post-Mortem,更新 Runbook
8. 面试加分项
8.1 回答框架
面试时按 STAR 结构回答:
- Situation:说清楚规模(QPS、节点数、数据量)
- Task:明确 RTO/RPO 目标
- Action:逐层讲解检测、切流、数据容灾
- Result:量化结果(切流时间、错误率)
8.2 常见追问和参考答案
Q: 如何防止切流时造成新的故障(切流雪崩)?
A: 切流前检查目标 Region 容量,先扩容再切流;切流过程中持续监控目标 Region 的 CPU/内存/队列深度;设置最大切流速度,允许随时 Rollback。
Q: DNS 切换很慢,怎么加速?
A: 提前将 TTL 降到 30–60s(正常时是 300s);使用 Anycast + BGP 路由调整代替 DNS,秒级生效;在客户端 SDK 中绕过 DNS 缓存,直接刷新 IP 列表。
Q: 多活架构下如何解决数据一致性?
A: 按照数据的写入来源分片,避免同一数据在多个 Region 同时写入;对于必须跨 Region 的写(如全局库存),使用分布式事务(Saga 补偿模式),并接受最终一致性;对强一致场景(支付),只在主 Region 写,多活退化为读多活。
Q: 如何区分”局部抖动”和”真实故障”,避免误切流?
A: 使用多窗口告警(短窗口触发预警,长窗口触发切流);引入多个独立的探测源(LB 健康检查 + 业务成功率 + 外部探测),至少两个来源同时异常才触发切流;设置最小观测时间(如连续 10s 异常)。
8.3 设计权衡总结
| 决策点 | 选项 A | 选项 B | 推荐 |
|---|---|---|---|
| 检测速度 | 激进阈值(快但误报多) | 保守阈值(慢但准确) | 多窗口结合 |
| 切流速度 | 立即全切(快但风险大) | 渐进切流(慢但安全) | 渐进切流 |
| 数据一致 | 强一致(延迟高) | 最终一致(延迟低) | 按业务选择 |
| 自动化程度 | 全自动(快但风险高) | 全手动(安全但慢) | 半自动+人工确认关键步骤 |
总结
设计快速容灾的微服务系统,核心是快速检测 + 安全切流 + 数据保障三位一体:
- 检测:分层检测(LB 探针 + Outlier Detection + 业务指标),多窗口告警减少误报
- 切流:Global LB + 服务网格实现秒级切流,渐进式(10%→50%→100%)避免雪崩,配合熔断限流做自保
- 数据:根据 RTO/RPO 选择主从热备或多活,缓存提前预热,防止切流后缓存击穿
- 验证:混沌工程 + 定期 GameDay,让容灾能力持续可信
面试核心:不要只说”我会用 Kubernetes 部署多副本”,而是要展示你对故障检测延迟、切流安全性、数据一致性权衡的深入思考。