架构风格演进——单体到微服务到Serverless

应用程序的架构风格不是一开始就复杂的。大多数系统始于单体应用,在规模和团队增长的过程中逐步演化。理解这一演进路径,是架构师的核心能力。

一、单体架构 (Monolith)

1.1 结构

所有功能模块打包在一个进程中,共享同一个数据库:

1
2
3
4
5
6
7
8
9
10
11
12
[单体应用]
├── 用户模块
├── 订单模块
├── 商品模块
├── 支付模块
└── 消息模块

[MySQL 数据库]
├── users 表
├── orders 表
├── products 表
└── ...

1.2 优点

  • 开发简单:一个 IDE 打开所有代码,本地即可运行全部功能
  • 部署简单:打包成一个 jar/war,一次部署
  • 调试简单:单进程,堆栈跟踪完整
  • 测试简单:不需要 mock 外部服务

1.3 缺点

  • 耦合严重:修改一个模块可能影响其他模块
  • 扩展困难:只能整体扩缩容,不能只扩展热点服务(如订单服务 QPS 高,但只能整体扩容)
  • 技术锁定:200 万行 Java 无法因为某个模块改用 Python 而全部重写
  • 团队协作难:200 人改同一个代码库,合并冲突频繁
  • 部署瓶颈:每次部署都是全量发布,一次小改动也要走完整流水线

1.4 何时用单体

  • 团队 < 5 人
  • 业务逻辑不复杂
  • 用户量 < 10 万
  • 不需要独立扩缩容
  • 初创阶段的最优选择

二、微服务架构 (Microservices)

2.1 结构

每个业务能力拆分为独立的服务,各自拥有独立的数据库:

1
2
3
4
5
6
7
[API Gateway]

├── [用户服务][用户 DB]
├── [订单服务][订单 DB]
├── [商品服务][商品 DB]
├── [支付服务][支付 DB]
└── [消息服务][消息 DB]

2.2 服务拆分原则

原则 说明 示例
单一职责 一个服务只做一件事 订单服务只处理订单生命周期
围绕业务能力 按业务边界而非技术层拆分 按”订单”拆分,而非按”Controller/Service/DAO”
数据自治 每个服务拥有自己的数据库 订单服务不能直接访问用户服务的数据库
独立部署 每个服务可独立上线 修改支付服务不影响其他服务正常运行

2.3 优点

  • 独立扩缩容:热点服务(如秒杀订单)单独扩容
  • 技术异构:订单服务用 Java,推荐服务用 Python
  • 故障隔离:支付服务挂了不影响用户浏览商品
  • 团队自治:订单团队和支付团队独立开发、独立发布

2.4 缺点

  • 分布式复杂度:网络延迟、分布式事务(Saga)、服务发现问题
  • 运维成本高:数十个服务的监控、日志、链路追踪
  • 调试困难:一次请求跨越多个服务,需要分布式追踪
  • 数据一致性:跨服务的 ACID 事务变成 BASE 最终一致性
  • 团队要求高:至少需要 DevOps 基础设施(K8s、CI/CD、监控)

2.5 何时用微服务

  • 团队 > 20 人,且还在增长
  • 业务模块明确且独立
  • 需要独立扩缩容
  • 已经有成熟的 DevOps 基础设施
  • 过早拆分是架构的反模式

三、模块化单体 (Modular Monolith)

微服务不是唯一出路。模块化单体是一种折衷方案——代码按模块组织,但部署上仍是一个整体:

1
2
3
4
5
6
7
8
9
10
11
12
13
[模块化单体应用]
├── 用户模块
│ ├── API 层 (对外接口)
│ └── 内部实现
├── 订单模块
│ ├── API 层
│ └── 内部实现
└── 支付模块
├── API 层
└── 内部实现

注意:模块之间通过内部 API 调用,而非 HTTP/RPC
所有模块共享一个数据库(但通过模块约定限制跨模块查询)

关键特征:模块间通过 Java 接口(而非 HTTP)通信,后期如需拆成微服务,接口天然匹配。

四、Serverless 架构

4.1 结构

不需要管理服务器,直接将函数部署到云平台:

1
2
3
4
5
6
7
8
[API Gateway]

├── [Lambda: createOrder]
├── [Lambda: processPayment]
├── [Lambda: sendNotification]
└── [Step Functions: 编排工作流]

[托管数据库: DynamoDB / Aurora Serverless]

4.2 优点

  • 零运维:不管理服务器,不关心扩缩容
  • 按量付费:只在函数执行时计费(空闲时 0 成本)
  • 自动扩缩容:流量增长时自动分配更多计算资源
  • 适合事件驱动:S3 上传事件 → 触发图片处理 Lambda → 触发通知 Lambda

4.3 缺点

  • 冷启动:首次调用有延迟(100ms-数秒)
  • 状态管理复杂:函数本身是无状态的,需要外挂数据库
  • 调试困难:本地模拟云环境困难
  • 供应商锁定:深度依赖 AWS/Azure/GCP 生态
  • 执行时间限制:Lambda 最长 15 分钟
  • 不适合长连接:WebSocket 等协议不匹配

4.4 适用场景

Serverless 不适合所有场景,但在以下场景表现极佳:

  • 图片/视频处理管道
  • 定时任务和 Cron 作业
  • API 后端(配合 API Gateway)
  • IoT 事件处理
  • 数据流处理管道

五、架构演进路线图

Monolith → Modular Monolith → 部分拆分 → 微服务 是一条比 Monolith → 全量微服务 更安全的路径:

1
2
3
4
5
6
7
8
9
10
11
12
阶段 1:单体 (Monolith)
└─ 团队 1-10 人,MVP 快速迭代

阶段 2:模块化单体 (Modular Monolith)
└─ 团队 10-50 人,代码按模块隔离,内部 API 限定

阶段 3:部分微服务化
└─ 将高耦合、高流量的模块(如支付、消息推送)拆为独立服务
但多数功能仍保留在模块化单体中

阶段 4:全面微服务
└─ 团队 50+ 人,独立的 CI/CD 管道,每个服务有独立团队

六、选型决策框架

如果… 则选择…
团队 < 5 人,快速验证想法 单体
团队 10-50 人,业务明确 模块化单体
某模块需要独立扩缩容 从单体中拆分该模块为微服务
团队 50+ 人,有 DevOps 基础设施 全面微服务
事件驱动、弹性负载 Serverless
实时通信、低延迟要求 单体(如 Discord 使用 BEAM 单体处理实时消息)

没有普适的最佳架构,只有最适合当前团队规模和业务阶段的架构。