作者: Sam Newman | 出版社: 人民邮电出版社 | 关键词: 微服务架构、服务拆分、API网关、事件溯源、CQRS、服务网格、康威定律
《微服务设计》(Building Microservices: Designing Fine-Grained Systems)是微服务架构领域的权威指南,由 ThoughtWorks 首席技术顾问 Sam Newman 撰写。第一版于 2015 年出版,恰逢微服务概念从 Netflix、Amazon 等一线互联网公司向外扩散的时期;第二版(2021 年更新)则全面覆盖了服务网格、函数计算(FaaS)、Kubernetes 生态等云原生主题,并在组织和文化维度上做了大幅扩展。
本书的核心观点可以用一句话概括:微服务不是技术选型,而是组织架构与技术架构之间的映射。 错误的拆分会带来灾难,而正确的粒度让团队获得真正的独立性与迭代速度。
任何架构决策都有其历史背景。在微服务兴起之前,绝大多数企业应用采用单体架构(Monolithic Architecture)。单体架构在项目初期具备明显优势:单一代码库、单一部署单元、简单的事务管理、IDE 友好。但随着系统规模的扩大,单体架构逐渐暴露出以下问题:
微服务架构的核心承诺是:将系统拆分为一组小型的、自治的服务,每个服务围绕特定业务能力构建,可独立部署、独立扩展、独立技术栈。
实践表明,这一架构在以下场景中表现尤为突出:
Sam Newman 在书中反复强调一个原则:拆分错误的微服务比不分拆更糟糕。 这是微服务架构最大的陷阱。
| 拆分维度 | 适用场景 | 风险 |
|---|---|---|
| 业务能力(Business Capability) | 大多数场景,与组织对齐 | 可能拆分过粗 |
| 子域(DDD Subdomain) | 复杂领域模型,团队经验丰富 | 学习曲线高 |
| 技术分层 | ❌ 强烈不推荐 | 导致跨层变更传播 |
| 功能/用例 | 小型团队快速迭代 | 粒度可能过细 |
领域驱动设计(DDD)中的 限界上下文(Bounded Context) 是微服务拆分的最佳起点。每个限界上下文对应一个微服务。识别限界上下文的方法是:
假设我们要设计一个电商系统,典型拆分为:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 用户服务 │ │ 商品服务 │ │ 订单服务 │
│ (User) │ │ (Product) │ │ (Order) │
└─────┬───────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
┌─────┴───────┐ ┌──────┴──────┐ ┌──────┴──────┐
│ 支付服务 │ │ 库存服务 │ │ 通知服务 │
│ (Payment) │ │ (Inventory) │ │ (Notification)│
└─────────────┘ └─────────────┘ └─────────────┘
关键要点:
Sam Newman 特别指出几个常见的拆分反模式:
微服务之间的通信是架构设计中的核心权衡点。
| 特性 | 同步(REST/gRPC) | 异步(消息队列/事件) |
|---|---|---|
| 耦合度 | 运行时耦合 | 时间解耦 |
| 可用性 | 依赖所有下游服务 | 下游短暂不可用不影响 |
| 一致性强 | 弱一致性 | |
| 监控难度 | 较低 | 较高(需要追踪事件流) |
| 推荐场景 | 查询类操作、低延迟要求 | 跨服务状态变更、最终一致性 |
REST 依然是微服务间通信最流行的选择。Sam Newman 的建议:
/v1/orders)或 Header 版本(Accept: application/vnd.company.v1+json)/getOrderById,使用 GET /orders/{id}在需要高性能、强类型、双向流式通信的场景下,gRPC 是更好的选择:
事件驱动是构建弹性微服务架构的关键模式。常用技术选型:
API 网关是微服务架构中的关键基础设施组件,承担着请求路由、认证授权、限流熔断、协议转换等职责。
| 方案 | 特点 | 适用场景 |
|---|---|---|
| Kong | 基于 OpenResty,插件生态丰富 | 企业级、传统微服务 |
| APISIX | 高性能,Apache 顶级项目 | 云原生、高性能场景 |
| Envoy | C++ 实现,服务网格的数据面 | 服务网格生态 |
| Spring Cloud Gateway | Java 生态最佳集成 | Spring 技术栈 |
| Traefik | 自动发现,自动 HTTPS | Kubernetes 原生 |
Sam Newman 特别推崇 BFF 模式:为每种客户端(Web、iOS、Android、第三方)创建独立的 API 网关层。不同客户端的交互模式差异巨大:
每个 BFF 专注于特定客户端的编排逻辑,避免所有客户端的定制需求污染同一个网关。
这是微服务最根本的数据原则。每个微服务拥有自己私有的持久化存储,其他服务只能通过该服务的 API 访问数据。
为什么如此重要?
一旦数据库按服务拆分,跨服务的分布式事务成为必然面对的问题。传统的两阶段提交(2PC)在微服务中几乎不可用——它引入了中心化协调器,在高并发下性能骤降且容错性差。
推荐的替代方案:Saga 模式
Saga 将一个跨服务的操作拆分为一系列本地事务,每个本地事务完成后发布事件触发下一个事务。如果某个步骤失败,则执行补偿事务回滚之前的操作。
Saga 执行流程(以订单流程为例):
① 订单服务创建订单(pending)→ 发布事件
② 支付服务扣款 → 发布事件
③ 库存服务扣减 → 发布事件
④ 订单服务标记完成
失败补偿:
③ 库存不足 → 发布失败事件
②' 支付服务退款 → 发布事件
①' 订单服务取消订单
事件溯源将每一次状态变更作为不可变的日志事件持久化,而不是持久化当前状态。
优势:
代价:
实战建议:不要在整个系统中使用事件溯源,只在需要审计追踪的特定领域使用(如金融交易、订单历史)。
CQRS 将读写模型分离,可以独立优化。在微服务中,CQRS 与事件溯源常常配合使用:
什么时候需要 CQRS?
什么时候不需要? 大部分 CRUD 应用不需要 CQRS,它会引入不必要的复杂度。
在容器化和编排平台普及之前,服务发现是微服务的核心难点之一。
| 模式 | 典型实现 | 优势 | 劣势 |
|---|---|---|---|
| 客户端发现 | Eureka + Ribbon | 减少一跳延迟 | 每种语言的客户端代码不同 |
| 服务端发现 | AWS ELB、K8s Service | 客户端不需要感知服务拓扑 | 额外网络跳转 |
在 Kubernetes 环境中,服务发现基本由平台层解决——K8s Service + CoreDNS 是事实标准,每个服务通过 DNS 名称即可访问。
微服务架构中,服务之间的网络调用可能因各种原因失败。弹性(Resilience)是指系统在面对故障时仍能提供正常服务的能力。
1. 断路器(Circuit Breaker)
状态机:CLOSED → OPEN → HALF_OPEN → CLOSED
- CLOSED:正常运行,请求通过
- OPEN:故障率超过阈值,直接拒绝请求(快速失败)
- HALF_OPEN:经过冷却时间,允许有限请求通过测试恢复情况
实现:Resilience4j(Java)、Hystrix(已停维)、Sentinel、Istio 熔断
2. 重试(Retry)
3. 超时(Timeout)
4. 舱壁隔离(Bulkhead)
Hugo 在项目实践中遇到过一个经典问题:早期接口超时统一设置为 30s,某次下游服务活锁导致所有连接被占满,API 网关线程池耗尽,最终全站不可用。解决方法是:
第二版新增的重要内容。服务网格将通信逻辑从应用中剥离到基础设施层。
| 方案 | 特点 | 适用 |
|---|---|---|
| Istio | 功能最丰富,社区最活跃 | 大规模生产环境 |
| Linkerd | 轻量级,CNCF 毕业项目 | 中小规模,K8s 生态 |
| Consul Connect | HashiCorp 出品,非 K8s 友好 | 混合部署环境 |
| Kuma | 基于 Envoy,支持多集群 | 多集群、多云场景 |
Sam Newman 也客观地讨论了服务网格的代价:
结论:服务网格适合有一定微服务规模和运维能力的中大型团队。10 个以下服务的小型部署不需要服务网格。
第二版新增的另一个重量级主题。函数计算(FaaS,如 AWS Lambda、阿里云函数计算)与微服务架构互补:
适用场景:
不适用的场景:
混合架构实战:Hugo 在某物联网项目中采用"核心微服务 + 边缘函数"的混合架构——核心业务(设备认证、指令下发)用微服务部署在 Kubernetes 上,非核心事件处理(数据清洗、异常报警)用函数计算处理,成本降低了约 40%。
微服务与 Docker 容器是天然伴侣。容器提供了:
K8s 已成为微服务编排的事实标准。典型部署模式:
# Deployment 示例
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: registry.example.com/order-service:v2.3.1
ports:
- containerPort: 8080
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: 500m
memory: 1Gi
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 30
为了在微服务环境下实现零停机发布,Sam Newman 推荐以下策略:
每个微服务应拥有独立的 CI/CD 流水线,遵循以下原则:
微服务架构中,一个请求可能跨越 5-10 个服务,传统的单机监控手段完全失效。
1. 日志(Logging)
2. 指标(Metrics)
3. 链路追踪(Tracing)
在团队实践中,我们采用了以下组合:
日志 → Loki + Grafana
指标 → Prometheus + Grafana(配合 AlertManager 告警)
链路追踪 → Jaeger
健康检查 → K8s Readiness/Liveness Probe
关键经验:
微服务的测试比单体更复杂,因为服务的边界增加了集成点的不确定性。
╱ 端到端(E2E) ╲ ← 少量,覆盖核心业务链路
╱ 契约测试(CDC) ╲ ← 微服务最关键的测试层级,保证服务间 API 兼容
╱ 集成测试(Integration)╲ ← 测试外部依赖(数据库、消息队列)
╱ 单元测试(Unit Tests) ╲ ← 大量,覆盖业务逻辑
这是微服务测试中最具价值的模式之一。服务消费者编写契约(Contract),服务提供者验证这些契约。通过 CDC,多个团队可以独立演化 API 而不用担心破坏上游服务。
主流工具:Pact(支持多种语言)、Spring Cloud Contract
康威定律(Conway's Law)是微服务架构成功与否的决定性因素:"设计系统的组织,其产生的设计等价于组织之间的沟通结构。"
第二版中,Sam Newman 引入了 Team Topologies 的理念:
| 团队类型 | 职责 | 适用场景 |
|---|---|---|
| 流对齐团队(Stream-Aligned) | 直接面向业务交付,拥有完整的服务所有权 | 大多数微服务团队 |
| 赋能团队(Enabling) | 帮助流对齐团队提升能力 | 平台工程、SRE |
| 复杂子系统团队(Complicated-Subsystem) | 维护高度专业化的子系统 | AI/ML、支付引擎 |
| 平台团队(Platform) | 提供内部工具和基础设施 | 内部开发者平台 |
参考 Netflix 和 Amazon 的实践:
在早期推行微服务时,团队犯过几个典型错误:
最终解决方案:回退到 8 个服务,组建全功能小团队(4-6 人),每个团队完整拥有 1-2 个服务从设计到运维的全生命周期。
Martin Fowler 提出的经典迁移模式,也是 Sam Newman 推荐的策略:
Phase 1: 提取无状态服务(认证、文件处理)
Phase 2: 拆分数据密集型领域(订单、商品)
Phase 3: 引入事件驱动解耦
Phase 4: 消除共享数据库(完成解耦)
结合 Sam Newman 的总结和 Hugo 的实战经验,以下是微服务实践中最高频的反模式:
| 反模式 | 表现 | 后果 | 解决方案 |
|---|---|---|---|
| 分布式单体 | 微服务间大量同步调用形成调用链 | 故障域扩大,端到端延迟恶化 | 引入异步、事件驱动 |
| 共享内核 | 多个服务通过共享库传递领域对象 | 隐式耦合,版本难以协调 | 通过 API 复制数据 |
| 数据服务贫血 | 服务只是数据表的 CRUD 封装 | 业务逻辑缺失,价值低下 | 引入 DDD,聚合业务行为 |
| 贪心网关 | API 网关中塞入大量业务逻辑 | 网关成为新的单体 | 使用 BFF 模式 |
| 夜间单体 | 服务各自独立部署但共享同一个数据库 | 自以为微服务,实际还是单体 | 按服务分库 |
| 过度自动化 | 每行代码都要求 100% 覆盖率 | 维护测试比编写代码更耗时 | 按风险分级测试 |
本书为微服务架构的深入理解和实践提供了坚实基础。以下内容与本书高度互补:
本书是 Hugo 架构师书架系列的核心读物之一。笔记整理于 2026 年 5 月。