分布式系统与云原生架构

来源书目:

  1. 《微服务设计》Sam Newman
  2. 《深入理解高并发编程核心原理与案例实战》冰河
  3. 《分布式架构原理与实践》崔皓
  4. 《微服务架构设计模式》Chris Richardson
  5. 《凤凰架构》周志明
  6. 《云原生应用架构:微服务开发最佳实战》FreeWheel 核心业务系统开发团队
  7. 《高性能之道:SRE视角下的运维架构实践》王力
  8. 《云原生架构:从技术演进到最佳实践》贺阮
  9. 《Linux性能优化大师》赵永刚
  10. 《分布式系统性能优化》张程、王梓晨、曹洪伟
  11. 《云原生时代的可观测系统最佳实战》罗梦婷

一、微服务设计

1.1 为什么要拆

微服务解决的核心问题是组织问题——让小团队独立开发、独立部署、独立扩展。但它引入了分布式系统的全部复杂性:网络延迟、数据一致性、调试困难、运维成本暴增。

单体地狱的三个典型症状(Richardson):

  • 开发速度下降:代码库太大,修改牵一发动全身
  • 部署风险高:小改动需要整体重新部署
  • 技术栈锁定:无法为特定模块选择更合适的语言或数据库

拆分的前提条件(多书共识):

  • 团队规模超过 8-12 人("两个披萨"原则)
  • 模块间有清晰的业务边界
  • 不同模块的部署频率差异显著
  • 单体已经充分优化但仍有瓶颈(FreeWheel 强调这一条最常被跳过)

"如果无法为你的服务找到一个合理的理由让它独立存在,那它大概不应该成为一个独立的服务。" —— Sam Newman

1.2 怎么拆

服务边界应该由业务领域决定,而不是技术架构决定。这是 DDD(领域驱动设计)的核心思想。

两种拆分方式(Richardson):

| 策略 | 原理 | 适用场景 | |------|------|----------| | 按业务能力 | 围绕"业务做什么"组织服务 | 业务边界清晰、团队按业务线划分 | | 按 DDD 子域 | 用限界上下文划分 | 复杂业务领域、需精确定义边界 |

DDD 子域分三类:

  • 核心域:差异化竞争力,投入最多
  • 支撑域:必要但非差异化,可适度投入
  • 通用域:可使用现成方案,最小投入

拆分陷阱(Richardson):

  • 分布式单体:服务拆了但强耦合,比单体更糟
  • 上帝服务:所有请求经过一个中央服务,成为新瓶颈
  • 胶水代码:通信适配层维护成本抵消拆分收益
  • 按数据表拆分:导致事务性依赖,变成分布式大泥球(Newman)

1.3 迁移策略

绞杀者模式(Strangler Fig Pattern)是 Newman 和 Richardson 共同推荐的做法:新功能用微服务开发,旧功能逐步替代,直到单体被完全"绞杀"。关键是渐进式,不是并行运行新旧系统。

先拆什么? 按"变化频率"排序——先拆变化最快、最频繁修改的模块,最快感受到独立部署的收益。

"An important benefit of incrementally refactoring to a microservice architecture is that you get an immediate return on your investment." —— Richardson


二、分布式核心问题

2.1 CAP 定理与一致性模型

CAP 定理:分布式系统最多同时满足一致性(Consistency)、可用性(Availability)、分区容错性(Partition Tolerance)中的两个。分区容错是分布式系统的基本事实(网络一定会断),所以实际选择的是 CP 还是 AP。

BASE 理论:Basically Available、Soft State、Eventually Satisfied。接受最终一致性,是 AP 路线的理论基础。

一致性模型谱系(周志明):

强一致性 → 线性一致性 → 顺序一致性 → 因果一致性 → 最终一致性 → 弱一致性

大多数互联网系统选择最终一致性,通过补偿机制处理异常。

2.2 分布式事务

微服务拆分后,原本一个数据库事务能搞定的事变成跨服务操作,一致性保证是最大难题。

方案对比

| 方案 | 一致性 | 性能 | 复杂度 | 适用场景 | |------|--------|------|--------|----------| | 2PC | 强一致 | 低(阻塞) | 高 | 传统数据库 | | TCC | 最终一致 | 中 | 高(侵入性强) | 支付、订单 | | Saga | 最终一致 | 高 | 中 | 长流程业务 | | 本地消息表 | 最终一致 | 高 | 低 | 异步通知 | | 事件驱动 | 最终一致 | 高 | 中 | 事件流场景 |

Saga 模式(Richardson + FreeWheel)是当前最务实的方案:把长事务拆成一系列本地事务 + 补偿操作。两种协调方式:

  • 编排式(Choreography):各服务自主响应事件,松耦合,但流程难理解
  • 协调式(Orchestration):中央协调器管理,流程清晰,但协调器可能成为瓶颈

FreeWheel 实践:Saga + Kafka 事件驱动,每个事件消息都有幂等性保证。

Saga 的代价:隔离性降低——执行过程中其他事务可能读到中间状态。设计业务流程时接受"可回滚"而非"完全隔离"。

2.3 通信模式

| 维度 | 同步(REST/gRPC) | 异步(消息队列) | |------|-------------------|-----------------| | 延迟 | 低,直接返回 | 高,需等待处理 | | 可用性 | 低(级联依赖) | 高(解耦) | | 耦合度 | 高(时间+接口耦合) | 低(只依赖消息格式) | | 一致性 | 强(单事务) | 弱(最终一致) |

核心建议(Richardson + Newman):优先用异步消息,只在确实需要同步响应时用 REST/gRPC。同步调用超过 3 层就该考虑异步。避免链式同步调用导致的级联故障。

关键模式

  • 发件箱模式(Outbox Pattern):保证 DB 写入和消息发送的原子性
  • 幂等消费者:处理消息重复投递
  • 断路器(Circuit Breaker):检测故障自动切断请求,防止级联失败

2.4 数据去中心化

铁律(Newman + Richardson + FreeWheel 共识):每个服务拥有自己的数据库,服务间不允许直接访问彼此的数据库。共享数据库是分布式系统的"友敌"——看起来省事,暗中破坏服务独立性。

跨服务查询的解决方案(Richardson):

  • API 组合:简单场景,一个服务聚合多个下游响应
  • CQRS:读写分离,写操作更新业务库,读操作维护专门的查询视图
  • 事件溯源 + CQRS:写端发布领域事件,读端监听事件构建查询视图

事件溯源不是默认选择。只有当业务确实需要完整审计追踪、时间旅行查询时才值得投入。 —— Richardson


三、云原生实践

3.1 云原生核心原则

CNCF 定义:容器化封装、动态调度、面向微服务、声明式 API。

12-Factor App(贺阮):让应用对运行环境零假设。核心要点:

  • 代码库单一,多环境部署
  • 依赖显式声明
  • 配置与代码分离(环境变量管理)
  • 无状态进程(session 和缓存外置)
  • 一次性进程(随时可被杀,能从外部状态恢复)

不可变基础设施(周志明 + 贺阮):每次部署是新镜像替换旧实例,没有热更新、没有覆盖配置。从根本上改变了运维模式。

3.2 容器与编排

容器 vs 虚拟机的本质区别(贺阮):容器不虚拟硬件,而是共享宿主机内核。一个容器就是一个受限的进程。这就是为什么秒级启动、资源占用极低。

Kubernetes 核心设计哲学(贺阮 + 周志明):

  • 声明式:描述"期望状态"而非"操作步骤"
  • 控制器模式:自动收敛到期望状态(Pod 挂了自动拉起新的)
  • 最终一致性:系统终将达到你声明的状态

K8s 是云原生的"操作系统"。开发者不需要精通源码,但必须理解这个设计模型。

资源管理关键实践(周志明 + FreeWheel):

  • 为每个容器设置 CPU/内存的 requests 和 limits
  • HPA 结合自定义指标(队列长度、请求 QPS)而非仅看 CPU
  • PodDisruptionBudget 保护关键服务

3.3 服务网格

核心价值(周志明 + FreeWheel + 贺阮):把流量治理、可观测性、安全等横切关注点从业务代码中剥离,交给 Sidecar 代理处理。

[应用服务 A] ↔ [Sidecar A] ↔ [数据面] ↔ [Sidecar B] ↔ [应用服务 B]

适用判断(贺阮):服务数量超过 50 个、服务间调用关系复杂、团队有专门的平台工程能力——才考虑引入。小团队直接用 SDK(如 Resilience4j)可能更务实。

Istio 实践建议(FreeWheel):

  • 先从流量路由和可观察性两个场景入手
  • Envoy sidecar 资源开销约 100-200MB/实例,配置复杂度是实际痛点
  • mTLS 性能开销约 5-10%
  • VirtualService 规则匹配顺序很重要

3.4 DevOps 与 CI/CD

部署频率是最好的 DevOps 成熟度指标(贺阮)。能做到一天部署多次且不出事,说明自动化测试、监控、回滚都到位了。

关键实践(FreeWheel):

  • GitOps:Git 仓库是唯一的配置来源,ArgoCD 自动同步到 K8s
  • 自动回滚:发布后持续监控错误率和延迟,超阈值自动回滚
  • 特性开关:新功能先上线但不开启,降低发布风险
  • 流水线本身也需要版本管理:Jenkinsfile/gitlab-ci.yml 和代码放在一起

3.5 架构演进方法论

复杂度方程式(周志明):

总复杂度 = 认知负荷(k × N) + 协作成本

  • 单体:N=1,k 极大 → 总复杂度中高
  • 合理微服务:N=10-50,k 极低 → 总复杂度低
  • 过度拆分:N=100+,k 极低 → 总复杂度高(分布式地狱)

架构决策记录(ADR)(周志明 + 贺阮):记录重要技术决策及其理由,避免"会议上说过的东西没人记得"。

微服务成熟度模型(FreeWheel):

L0: 单体应用 → L1: 微服务化 → L2: 容器化 → L3: 云原生(服务网格)→ L4: 智能化

四、性能优化

4.1 性能指标体系

"多快好省"四维模型(张程等):

| 维度 | 核心指标 | 含义 | |------|----------|------| | 多(容量) | QPS/TPS、并发用户数 | 系统能承载多大负载 | | 快(时间) | P50/P95/P99 响应时间 | 处理请求有多快 | | 好(可用) | SLA、MTBF、MTTR | 系统有多稳定 | | 省(资源) | CPU 利用率、成本 | 用多少资源达到上述效果 |

用 P95/P99 而不是平均值:P99=500ms 意味着每 100 个请求就有 1 个用户等了 500ms。平均值掩盖了真实的用户体验。

4.2 性能分析方法论

USE 方法(张程等 + Newman):对每个系统资源问三个问题:

  • Utilization:资源使用百分比?
  • Saturation:有多少请求在排队?
  • Errors:有多少请求失败?

火焰图(张程等):X 轴采样比例,Y 轴调用栈。找最宽的"柱子",就是最热的代码路径。

性能优化黄金法则(张程等 + 赵永刚):

  1. 先度量,再优化——不要凭感觉
  2. 找最大瓶颈——20% 的问题带来 80% 的效果
  3. 一次只改一个变量
  4. 用数据验证
  5. 警惕回归——优化 A 可能导致 B 变差

4.3 网络延迟感知

延迟数字感(张程等)——记住这些数字做架构决策:

| 操作 | 延迟 | |------|------| | L1 Cache | ~1ns | | 内存访问 | ~100ns | | SSD 随机读 | ~100μs | | 同数据中心网络 | ~500μs | | 跨数据中心 | ~50ms | | 跨大洲 | ~150ms |

一次远程调用的延迟是本地调用的 100-1000 倍。每增加一个服务间调用,就增加一次网络 RTT。

4.4 缓存体系

缓存层级(张程等):L1/L2/L3 Cache → 本地缓存 → 分布式缓存(Redis)→ CDN 缓存

缓存三大经典问题

| 问题 | 原因 | 解决方案 | |------|------|----------| | 穿透 | 查询不存在的数据,缓存永远 miss | 布隆过滤器、缓存空值 | | 击穿 | 热点 key 过期,请求同时打到 DB | 互斥锁、永不过期 + 异步更新 | | 雪崩 | 大量 key 同时过期 | 过期时间加随机偏移、多级缓存 |

缓存策略选择:读多写少用 Cache-Aside,写多读少不缓存或 Write-Around,读写均衡用 Read-Through + Write-Behind。

4.5 高并发架构

分层防御模型(冰河):

用户 → CDN/静态化 → WAF → 网关限流 → 服务限流 → 本地缓存 → Redis → MQ 削峰 → DB

高并发架构的本质:分层限流 + 异步削峰 + 缓存为王

秒杀系统关键策略(冰河):

  • 前端:按钮防抖、验证码、静态化
  • 网关:令牌桶限流、IP 黑名单
  • 服务层:本地缓存 + Redis + 异步下单(消息队列削峰)
  • 数据库:乐观锁、行锁而非表锁

4.6 数据库优化

优化层次(张程等):SQL 优化 → 索引优化 → 表结构优化 → 读写分离 → 分库分表 → 引擎替换

索引原则:最左前缀匹配、覆盖索引减少回表、避免在索引列上做函数运算、区分度低的列不适合建索引。

4.7 Linux 系统性能

四大子系统瓶颈定位(赵永刚):

| 子系统 | 关键指标 | 危险阈值 | |--------|----------|----------| | CPU | 上下文切换率 | > 50,000/s | | CPU | 运行队列长度 | > CPU 核数 | | 内存 | swap 使用量 | > 0(持续增长) | | 磁盘 | %util | > 80% | | 磁盘 | await | > 20ms | | 网络 | 重传率 | > 0.1% |

调优铁律(赵永刚):

  1. 先评估、再调整;先备份、再动手
  2. 不在生产环境直接调优
  3. 每次只改一个变量
  4. 记录每一次变更和效果

SRE 漏斗优化法则(王力):从最外层开始逐层排查和过滤,每排除一层缩小一次范围,直到定位到真正瓶颈。


五、可观测性

5.1 可观测性 vs 监控

监控告诉你"系统出了什么问题",可观测性告诉你"为什么出了这个问题"。 —— 罗梦婷

可观测性的目标是:不需要进黑盒就能知道里面发生了什么

5.2 可观测性支柱

三大支柱(业界共识):

| 支柱 | 回答的问题 | 常用工具 | |------|-----------|----------| | 日志(Logging) | 发生了什么? | ELK、Loki | | 指标(Metrics) | 系统状态如何? | Prometheus + Grafana | | 链路追踪(Tracing) | 请求经过了哪些服务? | Jaeger、Zipkin、SkyWalking |

第四支柱:事件(Events)(罗梦婷 + 王力):80% 的线上故障和变更有关。部署事件、配置变更、基础设施变更、业务事件——这些和故障直接相关。

5.3 指标体系

RED 方法(面向请求):Rate(请求率)、Errors(错误率)、Duration(延迟)

USE 方法(面向资源):Utilization(使用率)、Saturation(饱和度)、Errors(错误)

指标分层(罗梦婷):

| 层次 | 示例 | 告警级别 | |------|------|----------| | 业务层 | 订单量、支付成功率、DAU | P0 | | 应用层 | QPS、P99 延迟、错误率 | P1 | | 中间件层 | Redis 命中率、MySQL 慢查询、Kafka Lag | P1-P2 | | 基础设施层 | CPU、内存、磁盘、网络 | P2 |

5.4 链路追踪

核心机制:每个请求在入口处生成 TraceID,通过 HTTP Header 在服务间传递,每个服务记录 Span 信息。

采样策略(罗梦婷):100% 采样在大流量场景不现实。

  • 固定比例采样
  • 自适应采样(基于错误率和延迟动态调整)
  • 尾部采样:先缓存再决定是否保留(如全保留错误链路)

5.5 告警设计

反直觉但重要(FreeWheel + 罗梦婷):告警太多等于没有告警

  • 告警必须 actionable——看到告警知道该做什么
  • 每条告警附带 Runbook(处理手册)
  • 告警分级:P0 立即响应 → P1 30 分钟内 → P2 当天 → P3 下次迭代
  • 告警聚合、抑制、升级机制

5.6 可观测性架构方案

推荐方案(罗梦婷):

| 方案 | 适用场景 | 优势 | |------|---------|------| | Grafana + Prometheus + Tempo + Loki | 中小规模、云原生原生 | 标签统一、资源占用低 | | Elastic Stack | 日志分析需求强 | 全文检索强大 | | 大规模优化方案 | PB 级数据量 | 水平扩展能力强 |

统一可观测架构三要素:统一的数据语义、统一的数据处理平台、统一的可视化系统。

5.7 可观测性成熟度模型

L4: 预测与自愈 — 系统能预测故障并自动修复
L3: 智能诊断 — 自动根因分析,MTTR < 5min
L2: 全链路关联 — 指标 + 日志 + 链路 + 事件四维关联
L1: 基础监控 — 有仪表盘和告警,但数据孤立
L0: 黑盒状态 — 出了问题靠猜

六、SRE

6.1 SRE 核心职责

SRE 不是运维的重新包装,而是用工程化手段解决运维问题的思路。四个核心维度:稳定、性能、效率、成本。(王力)

"技术为业务服务,不是反过来。很多技术人员容易陷入技术自嗨。" —— 王力

6.2 变更管控

变更是最大的故障来源。变更三板斧(王力):

  1. 可监控:上线后能看到关键指标变化
  2. 可灰度:先小流量验证
  3. 可回滚:出了问题能快速恢复

6.3 故障全生命周期

事前治理 → 变更管控 → 故障演练 → 应急响应 → 容灾降级 → 故障复盘(王力)

故障复盘不是追责,是学习。如果复盘会变成批斗会,以后没人敢如实报告问题。

应急"止血"常用操作:限流、降级、切换流量、隔离故障节点。

6.4 容量规划

不要全凭感觉。建立基于巡检的容量评估流程(王力):

  • 定期评估资源使用趋势
  • 对标业务增长预测容量缺口
  • 提前扩容而非临时抱佛脚

6.5 弹性伸缩

不只是 CPU 超阈值就加机器(王力):

  • 结合自定义指标(队列长度、请求 QPS)
  • 伸缩本身也可能引发故障,需要风险控制
  • 基于业务场景设计不同的伸缩策略

6.6 全局视角

SRE 不能只站在运维角度看问题,要切换到测试视角、用户视角、前端开发视角(王力)。每种视角能发现不同的问题——"系统指标正常但用户体验很差"这种矛盾,只有站在用户视角才能理解。


七、测试策略

7.1 微服务测试金字塔

        /  E2E 测试  \        ← 最少、最慢、最贵
       /  契约测试    \       ← 微服务独有,验证 API 兼容性
      /  组件测试      \      ← 单服务完整行为(mock 外部依赖)
     /   集成测试       \     ← 服务与基础设施交互
    /     单元测试       \    ← 最多、最快、最便宜

7.2 契约测试

微服务独有的测试类型(Newman + Richardson + FreeWheel)。消费者定义期望的 API 契约,提供者验证实现是否满足。用 Pact 框架实现。

价值:可以在不启动所有依赖服务的情况下,保证 API 变更不会破坏下游。

7.3 混沌工程

在生产或准生产环境主动注入故障,验证系统容错能力(FreeWheel)。

故障演练清单

  • 模拟单个 Pod 故障
  • 模拟网络延迟(增加 500ms)
  • 模拟依赖服务不可用
  • 模拟整个可用区故障

八、关键原则速查

| 原则 | 来源 | 一句话理解 | |------|------|-----------| | 康威定律 | Richardson | 你的团队怎么分组,系统就会怎么分块 | | 反向康威策略 | Richardson | 想要什么架构,先怎么分团队 | | 不可变基础设施 | 周志明 | 坏了就换新的,不要修 | | 声明式 API | 贺阮 | 告诉系统"我要什么",不是"怎么做" | | 12-Factor | 贺阮 | 让应用对运行环境零假设 | | 变更三板斧 | 王力 | 可监控、可灰度、可回滚 | | 幂等性设计 | Richardson | 重试不会出问题 | | 服务自治 | Newman + FreeWheel | 每个服务拥有自己的数据,不直接访问别人的库 | | 先度量再优化 | 张程 + 赵永刚 | 没有数据支撑的优化是凭感觉 | | 告警要 actionable | FreeWheel + 罗梦婷 | 看到告警知道该做什么 | | 复盘是学习不是追责 | 王力 | 否则没人敢如实报告问题 | | 绞杀者模式 | Newman + Richardson | 渐进式迁移,不要推倒重来 |


整理时间:2026-05-02 整理方式:从 11 本读书笔记中提取核心架构思想、设计原则和关键模式,用自己的话总结归纳,按主题分组。