单体架构与微服务架构如何选择?
面试场景
面试官:”你们项目用的是微服务还是单体?为什么这么选?”
这道题考察架构选型能力和技术判断力。不是微服务就一定好,关键是适合。
两种架构对比
单体架构
┌─────────────────────────────────────┐
│ 单体应用 │
├─────────────────────────────────────┤
│ 用户模块 │ 订单模块 │ 商品模块 │ ...│
├─────────────────────────────────────┤
│ 公共代码 │
├─────────────────────────────────────┤
│ 单一数据库 │
└─────────────────────────────────────┘
微服务架构
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 用户服务 │ │ 订单服务 │ │ 商品服务 │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└──────┬──────┴──────┬──────┘
│ │
┌──────┴─────┐ ┌─────┴──────┐
│ 网关 │ │ 注册中心 │
└────────────┘ └────────────┘
维度对比
| 维度 | 单体 | 微服务 |
|---|---|---|
| 开发复杂度 | 低 | 高 |
| 部署复杂度 | 低 | 高 |
| 技术栈 | 单一 | 多样 |
| 团队协作 | 代码冲突多 | 独立开发 |
| 扩展性 | 整体扩容 | 按服务扩容 |
| 容错性 | 单点故障 | 隔离性好 |
| 性能 | 进程内调用 | 网络调用开销 |
| 运维成本 | 低 | 高 |
单体架构的优势
1. 简单直接
// 直接方法调用,无需RPC
@Service
public class OrderService {
@Autowired
private UserService userService;
public Order createOrder(OrderRequest request) {
User user = userService.getUser(request.getUserId()); // 本地调用
// ...
}
}
2. 开发效率高
- 无需考虑分布式问题
- 调试方便
- 事务简单
3. 部署简单
# 一个jar搞定
java -jar app.jar
4. 性能好
- 无网络开销
- 无序列化开销
- 无服务发现延迟
微服务的优势
1. 独立部署
订单服务升级 → 只部署订单服务
用户服务不受影响
2. 技术栈灵活
用户服务:Java + MySQL
推荐服务:Python + Redis
搜索服务:Go + Elasticsearch
3. 团队独立
团队A → 负责用户服务 → 独立迭代
团队B → 负责订单服务 → 独立迭代
4. 按需扩容
大促期间:
- 订单服务:10实例 → 50实例
- 用户服务:保持10实例
选型决策树
团队规模?
│
├── < 10人 → 单体
│
└── >= 10人 → 业务复杂度?
│
├── 简单 → 单体
│
└── 复杂 → 扩展性要求?
│
├── 低 → 单体
│
└── 高 → 微服务
适合单体的场景
- 创业公司/小团队(< 10人)
- MVP/快速验证阶段
- 业务逻辑简单
- 预算有限
适合微服务的场景
- 大型团队(> 50人)
- 业务复杂,模块边界清晰
- 需要独立扩缩容
- 多语言技术栈需求
演进路径
不要一开始就微服务
错误路径:
启动项目 → 直接微服务 → 踩坑无数 → 项目失败
正确路径:
启动项目 → 单体 → 业务增长 → 逐步拆分 → 微服务
渐进式拆分
阶段1:单体
┌─────────────────────┐
│ 所有业务 in 一个服务 │
└─────────────────────┘
阶段2:模块化单体
┌─────────────────────┐
│ 用户模块 │ 订单模块 │ ← 代码层面模块化
└─────────────────────┘
阶段3:部分微服务
┌────────┐ ┌─────────┐
│单体应用 │ │订单服务 │ ← 核心服务先拆
└────────┘ └─────────┘
阶段4:全面微服务
┌────┐ ┌────┐ ┌────┐
│用户│ │订单│ │商品│
└────┘ └────┘ └────┘
微服务的代价
1. 分布式事务
// 单体:简单的@Transactional
@Transactional
public void create() {
orderMapper.insert(order);
stockMapper.deduct(stock);
}
// 微服务:需要Seata/TCC
@GlobalTransactional
public void create() {
orderService.create(order);
stockService.deduct(stock); // RPC调用
}
2. 运维复杂度
需要:
- 服务注册发现(Nacos/Consul)
- 配置中心
- 链路追踪(Skywalking)
- 日志聚合(ELK)
- 容器编排(K8s)
3. 网络开销
单体:0ms(进程内调用)
微服务:1-5ms/次RPC(调用链越长越慢)
面试答题框架
选型原则:适合 > 先进
考虑因素:
1. 团队规模:小团队选单体
2. 业务复杂度:简单业务选单体
3. 扩展需求:按需扩容选微服务
4. 运维能力:能力不足选单体
演进策略:
- 先单体,再拆分
- 核心服务先拆
- 渐进式演进
微服务代价:
- 分布式事务
- 运维复杂度
- 网络开销
总结
| 场景 | 推荐架构 |
|---|---|
| 创业公司 | 单体 |
| 小团队(<10人) | 单体 |
| MVP阶段 | 单体 |
| 大型团队(50+) | 微服务 |
| 独立扩缩容需求 | 微服务 |
| 多语言技术栈 | 微服务 |
核心原则:不要为了微服务而微服务,架构是为业务服务的。