目录
设计原则 设计模式 架构模式 代码重构 高可用架构设计 容灾与多活 限流算法
01

高可用架构设计

背景 为什么系统需要高可用?

在单机系统中,任何硬件或软件故障都会导致服务中断。随着业务规模扩大,可用性成为衡量系统质量的核心指标。高可用(High Availability, HA)架构旨在通过冗余自动故障转移,确保系统在部分组件失效时仍能持续提供服务。

第一性原理: 高可用的本质是「消除单点故障 (SPOF)」。通过在网络层(多路径)、数据层(副本)、应用层(多实例)引入冗余,配合故障检测自动切换机制,将单点故障的影响降到最低。设计原则是:冗余 + 自动切换 + 优雅降级

原理 冗余 · 故障检测 · 自动切换 · 可观测性

高可用架构核心模式:

  • 主备模式 (Active-Passive): 主节点处理请求,备节点待命,故障时切换
  • 主主模式 (Active-Active): 所有节点同时处理请求,负载均衡分发
  • 多活 (Multi-Active): 跨地域多中心同时服务,流量按地域分发

关键组件:

  • 健康检查: 通过心跳、接口检测判断节点状态
  • 故障转移: 当健康检查失败时,自动将流量切换到备用节点
  • 流量管理: 负载均衡器负责分发请求,屏蔽后端变化
高可用架构模式对比 主备模式 主节点 (Active) 备节点 (Standby) ↑ 故障切换 资源利用率低 主主模式 节点 A 节点 B 节点 C 节点 D ↑ 负载均衡 资源利用率高 多活模式 中心 1 中心 2 中心 3 ↑ 跨地域分发 异地多活·容灾 图:三种高可用模式对比,主备、主主、多活各有适用场景
图:三种高可用模式对比,主备、主主、多活各有适用场景
▸ Nginx 高可用负载均衡配置
# 主备模式 (健康检查 + 故障转移) upstream backend { server 192.168.1.10:8080 max_fails=2 fail_timeout=10s; server 192.168.1.11:8080 backup; # 备节点 } # 主主模式 (多节点负载均衡) upstream backend { server 192.168.1.10:8080 weight=3; server 192.168.1.11:8080 weight=3; server 192.168.1.12:8080 weight=3; } # 使用 keepalived 实现 VIP 自动切换 (主备) vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 virtual_ipaddress { 192.168.1.100 } }

演进 单机 → 主备 → 集群 → 多活

  • 单机 (1990s): 无冗余,故障即服务中断
  • 主备 (2000s): 冷/热备,故障切换需人工或脚本
  • 集群 (2010s): 多实例负载均衡,自动故障转移
  • 多活 (2015+): 跨地域多中心,流量就近接入,真正高可用
"高可用架构的演进是从『单点依赖』『多点冗余』,再到『跨地域多活』的过程。每一次演进都提升了系统的鲁棒性,同时也增加了设计和运维的复杂度。"
—— 高可用设计哲学

取舍 设计中的权衡

⚡ 成本 vs 可用性
更高的可用性需要更多的冗余资源,如多活模式需要至少两个数据中心,成本翻倍。需要在可用性需求和预算之间平衡。
🔧 自动切换 vs 人工干预
自动切换响应快,但可能误判;人工干预安全但延迟高。需要设计合理的健康检查和阈值,结合自动与人工。
📊 数据一致性 vs 可用性
多活模式下,跨地域数据同步存在延迟,可能影响一致性。需要在 AP(可用性)和 CP(一致性)之间进行权衡。
02

容灾与多活

背景 如何应对大规模地域性故障?

即使是高可用集群,也无法避免自然灾害电力中断网络分区等地域性故障。容灾设计的目标是:在发生大规模故障时,能够快速恢复业务,数据不丢失。常用的容灾模式包括冷备温备热备双活多活

第一性原理: 容灾的核心指标是RTO(恢复时间目标)和RPO(恢复点目标)。RTO 决定了业务中断多久可以恢复,RPO 决定了最多丢失多少数据。容灾模式的选择本质上是在成本RTORPO 三者之间寻找平衡。双活/多活模式将 RTO 和 RPO 降至最低,但成本最高。

原理 灾备层次 · 双活 · 数据同步 · 流量切换

模式RTORPO成本适用场景
冷备小时级天级非关键业务
温备分钟级分钟级关键业务,可接受少量数据丢失
热备秒级秒级核心业务,数据零丢失要求
双活/多活接近零接近零极高互联网核心服务,全球业务
容灾模式 RTO/RPO 对比 RTO (恢复时间) 秒级 RPO (数据丢失) 零丢失 冷备 温备 热备 双活 图:容灾模式在 RTO/RPO 上的权衡
图:容灾模式在 RTO/RPO 上的权衡
▸ 数据库双活数据同步 (MySQL)
# 双主复制配置 (双向同步) server-id = 1 log-bin = mysql-bin auto-increment-increment = 2 auto-increment-offset = 1 # 另一节点的配置 server-id = 2 log-bin = mysql-bin auto-increment-increment = 2 auto-increment-offset = 2 # 复制过滤 (避免循环复制) replicate-do-db = mydb replicate-ignore-db = mysql

演进 冷备 → 异步复制 → 同步复制 → 多活

  • 冷备 (2000s): 定期备份,恢复时间长,数据丢失多
  • 异步复制 (2005+): 主从异步复制,RPO 在秒级
  • 同步复制 (2010+): 主从同步复制,RPO 接近零
  • 多活 (2015+): 跨地域多中心同时服务,RTO/RPO 均为零
"容灾的演进是从『被动恢复』『主动防御』的转变。冷备和异步复制在故障时仍然有服务中断和数据丢失,而多活模式实现了真正意义上的『无感容灾』。"
—— 容灾设计哲学

取舍 设计中的权衡

📊 数据一致性 vs 性能
同步复制保证数据一致性但增加写入延迟;异步复制性能好但可能丢失数据。需根据业务容忍度选择。
⚡ 多活 vs 运维复杂度
多活架构需要解决数据冲突、流量分发、延迟优化等问题,运维复杂度远高于单活。需要投入更多运维资源。
🔧 自动切换 vs 数据校验
故障切换时,需要校验数据一致性,避免切换后数据丢失或乱序。自动切换应配合数据校验机制。
03

限流算法

背景 如何防止流量冲击?

在互联网系统中,流量可能因为促销活动爬虫攻击等原因突然暴涨,导致系统过载甚至崩溃。限流(Rate Limiting)通过控制请求速率,保护后端服务不被压垮。

第一性原理: 限流的本质是「对请求速率进行整形」。常用的算法包括计数器(固定窗口)、滑动窗口漏桶令牌桶。计数器简单但存在临界问题;漏桶可以平滑流量但无法应对突发;令牌桶允许一定程度的突发,最常用。选择哪种算法取决于对突发容忍度平滑性的要求。

原理 计数器 · 滑动窗口 · 漏桶 · 令牌桶

算法原理突发支持实现复杂度
计数器固定时间窗口内计数不支持
滑动窗口将时间窗口细分为多个小窗口不支持
漏桶先进先出队列,匀速处理不支持
令牌桶定时生成令牌,请求消耗令牌支持 (积累令牌)
限流算法对比 漏桶 (Leaky Bucket) 请求 → 队列 (缓冲区) → 匀速处理 ⚡ 平滑流量,不支持突发 适合保护脆弱后端 令牌桶 (Token Bucket) 定时生成令牌 → 请求消耗令牌 ⚡ 支持突发 (积累令牌) 适合大多数业务场景 图:漏桶与令牌桶的对比,令牌桶更适合支持突发
图:漏桶与令牌桶的对比,令牌桶更适合支持突发
▸ 令牌桶限流实现 (Go)
// 令牌桶限流器 (使用 go.uber.org/ratelimit) import "go.uber.org/ratelimit" // 每秒 10 个请求,允许突发 10 个 limiter := ratelimit.New(10) func handleRequest() { // 获取令牌,若没有则阻塞 limiter.Take() // 处理业务逻辑 process() } // 滑动窗口限流 (使用 Redis) // Redis 4.0 支持滑动窗口限流 redis.replicate("CL.THROTTLE", "user:123", "10", "10", "60")

演进 计数器 → 滑动窗口 → 漏桶 → 令牌桶 → 自适应

  • 计数器 (简单): 固定窗口,临界点易突破
  • 滑动窗口 (改进): 解决临界问题,但内存占用增加
  • 漏桶 (平滑): 输出稳定,适合保护下游
  • 令牌桶 (灵活): 支持突发,应用最广
  • 自适应限流 (2018+): 根据系统负载动态调整阈值
"限流算法的演进是『从静态到动态』的过程。从固定阈值到自适应限流,算法的适应性不断增强,能够根据系统实时负载自动调节限流阈值。"
—— 限流设计哲学

取舍 设计中的权衡

⚡ 突发容忍 vs 保护效果
令牌桶允许突发,但可能短时间内压垮下游;漏桶严格控制速率,但用户体验差。需根据下游的承受能力选择。
🔧 分布式限流 vs 单机限流
分布式限流(如 Redis)可以全局控制,但增加了网络延迟;单机限流效率高但无法应对全局流量不均衡。
📊 固定阈值 vs 动态阈值
固定阈值简单但无法适应流量变化;动态阈值自适应但复杂,需要监控系统状态。需要在简单性和适应性之间权衡。
04

降级与熔断

背景 如何优雅地处理系统故障?

当系统出现故障或资源不足时,强制拒绝所有请求(熔断)或返回简化结果(降级)可以防止故障扩大。熔断和降级是系统性容错的核心手段,它们与限流共同构成了高并发保护的三大支柱

第一性原理: 降级的本质是「牺牲功能完整性换取系统可用性」;熔断的本质是「自动切断故障服务,防止雪崩」。降级是主动行为(返回简化结果),熔断是被动行为(拒绝请求)。两者通常配合使用:熔断器在检测到故障后,触发降级逻辑。

原理 熔断器状态机 · 降级策略 · 自动恢复

熔断器状态:

  • 关闭 (Closed): 正常状态,请求通过
  • 打开 (Open): 故障率超过阈值,拒绝请求
  • 半开 (Half-Open): 过一段时间后尝试恢复

降级策略:

  • 返回兜底值: 如默认数据、空列表
  • 执行备用逻辑: 如从缓存读取、调用备用服务
  • 快速失败: 直接返回错误,避免资源消耗
熔断器状态机 关闭 (Closed) 请求通过 打开 (Open) 拒绝请求 半开 (Half-Open) 尝试恢复 故障率超阈值 超时/恢复 请求失败再打开 状态转换条件 Closed → Open: 错误率 > 50% 且 请求数 > 10 Open → Half-Open: 5 秒后自动进入 Half-Open → Closed: 尝试请求成功率达到阈值 图:熔断器三状态转换,自动恢复和自我保护
图:熔断器三状态转换,自动恢复和自我保护
▸ Hystrix 熔断器配置 (Java)
// HystrixCommand 配置熔断器 @HystrixCommand(fallbackMethod = "fallbackMethod", commandProperties = { @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"), @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000") }) public String callService() { // 调用远程服务 return remoteService.call(); } public String fallbackMethod() { // 降级逻辑:返回默认值 return "default response"; } # 使用 Sentinel 实现熔断降级 (Spring Cloud) @SentinelResource(value = "resourceName", fallback = "fallback") public String process() { // 业务逻辑 }

演进 超时重试 → 熔断 → 自适应容错

  • 超时重试 (早期): 简单的故障处理,可能导致雪崩
  • 熔断器 (2012): Netflix Hystrix 引入,自动切断故障服务
  • 自适应熔断 (2016+): 根据系统负载动态调整阈值
  • 服务网格熔断 (2018+): 在代理层实现熔断,对应用透明
"熔断和降级的演进是从『代码层』『代理层』的转变。从 Hystrix 到 Istio/Envoy,熔断能力正从应用代码中剥离,成为基础设施的一部分。"
—— 容错设计哲学

取舍 设计中的权衡

⚡ 熔断阈值 vs 误判率
阈值过低会导致误判(频繁熔断),阈值过高会导致保护延迟。需要根据历史数据调整阈值,平衡保护效果和误判率。
🔧 降级 vs 业务损失
降级可能导致业务功能缺失,需要与产品协商确定降级策略。选择非核心功能降级,保留核心功能。
📊 自动恢复 vs 手动确认
自动恢复可以减少运维负担,但可能在不稳定时再次故障。需要设计合理的半开状态检测策略。
05

容量规划

背景 如何为系统预留足够的资源?

系统容量不足会导致服务不可用,容量过剩则浪费成本。容量规划的目标是:在保证服务质量的前提下,以最优的成本满足业务需求。它需要综合考虑业务增长峰值流量弹性扩展等因素。

第一性原理: 容量规划的本质是「匹配供给与需求」。通过负载测试确定系统的最大容量(单节点 QPS),通过历史数据预测未来需求,通过弹性扩展动态调整资源。容量规划不是一次性活动,而是持续的过程,需要定期复盘和调整。

原理 容量评估 · 负载测试 · 弹性扩展 · 预留

容量规划步骤:

  1. 确定指标: QPS、并发数、CPU/内存/网络使用率
  2. 负载测试: 通过压测找出系统瓶颈和最大容量
  3. 预测增长: 根据业务增速估算未来需求
  4. 弹性设计: 使用云原生技术(HPA、Cluster Autoscaler)动态扩展
容量规划流程 1. 确定指标 QPS, CPU, 内存 2. 负载测试 压测找出瓶颈 3. 预测增长 历史数据 + 业务预估 4. 弹性设计 HPA, Cluster Autoscaler 持续循环:监控 → 评估 → 调整 图:容量规划的四个步骤,持续迭代
图:容量规划的四个步骤,持续迭代
▸ Kubernetes 弹性扩缩容
# HPA (Horizontal Pod Autoscaler) apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: myapp-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: myapp minReplicas: 3 maxReplicas: 20 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 # 查看 HPA 状态 kubectl get hpa myapp-hpa # Cluster Autoscaler (节点自动扩缩) # 在云平台配置 Cluster Autoscaler,节点不足时自动添加

演进 静态规划 → 动态扩展 → 智能预测

  • 静态规划 (2000s): 按最大峰值预留资源,成本高
  • 动态扩展 (2010s): 基于阈值自动扩缩容,提升资源利用率
  • 智能预测 (2015+): 基于 AI/ML 预测流量,提前扩容
"容量规划的演进是从『静态预留』『动态自适应』的转变。云原生时代,弹性伸缩技术让容量规划变得更加经济和高效。"
—— 容量规划设计哲学

取舍 设计中的权衡

⚡ 预留 vs 成本
预留足够资源确保服务质量,但会增加闲置成本。弹性扩展可以减少成本,但扩容需要时间,可能在突发流量时跟不上。
📊 精确度 vs 复杂度
基于历史数据的预测模型复杂度高但准确度更高;简单阈值法简单但可能不准确。需要在精确性和实现复杂度之间权衡。
🔧 固定容量 vs 弹性
固定容量可预测但资源利用率低;弹性容量利用率高但管理复杂。云原生环境下应优先采用弹性策略。
06

SLO/SLA/SLI

背景 如何量化和承诺系统的可靠性?

在讨论高可用时,我们常说「99.99% 可用」,但这具体意味着什么?SLI(服务等级指标)定义了具体的可测量指标,SLO(服务等级目标)设定了期望的目标值,SLA(服务等级协议)则是对外承诺的合同。三者构成了一个完整的可靠性量化体系。

第一性原理: SLO/SLA/SLI 的本质是「将可靠性抽象为可量化的指标」。SLI 解决「测量什么」,SLO 解决「目标是什么」,SLA 解决「承诺什么」。好的 SLO 应该基于用户感知,而不是内部指标。例如,「P99 延迟 < 200ms」比「CPU 使用率 < 80%」更能反映用户体验。

原理 SLI · SLO · SLA · 错误预算

SLI (Service Level Indicator): 常见的 SLI 包括:

  • 请求延迟(Latency):P99、P95、平均延迟
  • 错误率(Error Rate):HTTP 5xx 比例
  • 吞吐量(Throughput):QPS
  • 可用性(Availability):正常运行时间比例

SLO (Service Level Objective): 例如「P99 延迟 < 200ms」或「可用性 > 99.99%」。

SLA (Service Level Agreement): 包含 SLO 和违反时的处罚条款。

SLI/SLO/SLA 关系 SLI • 请求延迟 (P99) • 错误率 • 可用性 SLO • P99 < 200ms • 错误率 < 0.1% • 可用性 > 99.99% SLA • 合同约定 • 惩罚条款 • 赔偿方案 错误预算 = (1 - SLO) × 时间周期。例如 99.99% SLO,每月错误预算为 4.32 分钟 图:SLI 测量 → SLO 目标 → SLA 承诺
图:SLI 测量 → SLO 目标 → SLA 承诺
▸ Prometheus SLO 告警
# P99 延迟超过 SLO 告警 groups: - name: slo_alerts rules: - alert: HighLatency expr: histogram_quantile(0.99, sum by(le) (http_request_duration_seconds_bucket)) > 0.2 for: 5m labels: severity: warning annotations: summary: P99 延迟超过 200ms description: P99 延迟为 {{ $value }}s,超过 SLO 阈值 # 错误率 SLO 告警 - alert: HighErrorRate expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.001 for: 5m labels: severity: critical annotations: summary: 错误率超过 0.1%

演进 无 SLO → 基础 SLO → 精细化 SLO

  • 无 SLO (早期): 系统可靠性无法量化,运维凭感觉
  • 基础 SLO (2010s): 基于可用性(99.9%)的简单 SLO
  • 精细化 SLO (2015+): 针对不同服务、不同 API 制定多重 SLO
  • 错误预算驱动 (2018+): 使用错误预算来决策发布和故障处理
"SLO 的引入将可靠性从『直觉』转变为『数据驱动』。错误预算的概念让团队在创新和稳定之间有了明确的权衡依据,是现代 SRE 的基石。"
—— SRE 设计哲学

取舍 设计中的权衡

⚡ 激进 vs 保守
激进的 SLO(如 99.999%)会给团队带来巨大压力,可能导致过度工程;保守的 SLO 可能无法满足用户期望。需要根据业务重要性和用户容忍度合理设定。
📊 多 SLO vs 管理开销
为每个服务设定独立的 SLO 能够更精准地管理可靠性,但会增加监控和告警的复杂度。建议对关键服务设定 SLO,非关键服务可以沿用通用标准。
🔧 错误预算 vs 发布节奏
错误预算耗尽时应暂停发布,但过度保守会拖慢迭代速度。需要建立合理的错误预算消耗策略,在可靠性和创新之间平衡。
07

混沌工程

背景 如何验证系统在故障下的表现?

传统的高可用测试都是预期内的(如模拟故障关闭节点),但现实中的故障往往不可预测。混沌工程通过主动注入故障(如网络延迟、丢包、节点崩溃),来验证系统的弹性容错能力。它源自 Netflix 的 Simian Army,现在已经发展为一套成熟的实践体系。

第一性原理: 混沌工程的核心是「通过破坏来验证系统的鲁棒性」。它遵循「假设 → 实验 → 验证」的循环:首先假设系统在某种故障下仍能正常工作,然后注入故障,验证假设是否成立。如果失败,则改进系统。这种主动攻击的方法比被动等待故障更能发现系统的脆弱环节。

原理 故障注入 · 实验设计 · 自动恢复验证

混沌工程实验流程:

  1. 定义稳态: 确定系统的正常行为指标(如延迟、错误率、吞吐量)
  2. 设计实验: 假设系统在特定故障下仍保持稳态
  3. 注入故障: 模拟网络延迟、丢包、进程崩溃、资源耗尽等
  4. 验证结果: 观察系统是否自动恢复,是否影响用户
混沌工程实验流程 1. 定义稳态 基准指标 2. 设计实验 假设 + 故障模式 3. 注入故障 延迟/丢包/崩溃 4. 验证结果 是否自动恢复 持续迭代:改进系统 → 重复实验 图:混沌工程实验流程,四个步骤形成闭环
图:混沌工程实验流程,四个步骤形成闭环
▸ Chaos Mesh 故障注入
# 使用 Chaos Mesh 注入网络延迟 apiVersion: chaos-mesh.org/v1alpha1 kind: NetworkChaos metadata: name: network-delay spec: action: delay mode: one selector: labelSelectors: app: myapp delay: latency: 200ms correlation: 50 jitter: 50ms # 注入 Pod 崩溃 apiVersion: chaos-mesh.org/v1alpha1 kind: PodChaos metadata: name: pod-failure spec: action: pod-failure mode: one selector: labelSelectors: app: myapp # 运行混沌实验 kubectl apply -f chaos-experiment.yaml kubectl get chaos

演进 手动故障测试 → 自动混沌实验 → 混沌即服务

  • 手动故障测试 (早期): 运维手动模拟故障,成本高、覆盖少
  • 自动混沌实验 (2015+): Netflix Simian Army, Chaos Monkey
  • 云原生混沌 (2018+): Chaos Mesh, Litmus 等工具出现
  • 混沌即服务 (2020+): 托管的混沌实验平台,降低使用门槛
"混沌工程是将『破坏』作为『建设』的一部分。它颠覆了『故障是不可接受的』传统观念,转而认为『故障是检验系统健壮性的试金石』。"
—— 混沌工程设计哲学

取舍 设计中的权衡

⚡ 实验风险 vs 收益
混沌实验可能会造成真实故障,影响用户。需要在生产环境或类生产环境进行,并设置 blast radius(影响范围)控制。
🔧 自动化 vs 可控性
自动化混沌实验可以增加覆盖,但失控可能引发重大问题。需要结合人工审批和自动停止机制。
📊 覆盖度 vs 复杂度
全面覆盖各种故障模式需要大量的实验配置,增加运维负担。建议从核心故障模式开始,逐步扩展覆盖范围。