ef70b1d8e1
## 新增内容 ### 附录文档扩展 - 扩展前端项目架构文档 (frontend-project-architecture.md) - 扩展后端项目架构文档 (backend-project-architecture.md) - 扩展数据治理文档 (data-governance.md) - 扩展数据可视化文档 (data-visualization.md) - 扩展分布式系统文档 (distributed-systems.md) - 扩展高可用文档 (high-availability.md) - 扩展单体到微服务文档 (monolith-to-microservices.md) - 扩展系统设计方法论文档 (system-design-methodology.md) - 扩展 Docker 容器文档 (docker-containers.md) - 扩展 Kubernetes 文档 (kubernetes.md) - 扩展 Linux 基础文档 (linux-basics.md) - 扩展神经网络文档 (neural-networks.md) ### 新增交互式组件 - 数据治理组件: DataQualityDemo, DataGovernanceFrameworkDemo, DataLineageDemo - 数据可视化组件: ChartTypeSelectorDemo, DashboardLayoutDemo - 分布式系统组件: CAPTheoremDemo, ConsistencyModelsDemo, DistributedChallengesDemo - 高可用组件: AvailabilityCalculatorDemo, FailoverStrategyDemo - 系统设计组件: SystemDesignStepsDemo, CapacityEstimationDemo - Docker 容器组件: DockerArchitectureDemo, DockerLifecycleDemo - Kubernetes 组件: K8sArchitectureDemo, K8sWorkloadsDemo - Linux 基础组件: LinuxFileSystemDemo, LinuxCommandDemo, LinuxPermissionsDemo - 神经网络组件: NeuronDemo, NetworkLayersDemo, NetworkArchitectureDemo - 单体到微服务组件: ArchEvolutionDemo - 项目架构组件: ProjectArchitectureComparisonDemo - 附录导航组件: AppendixFlowMap ### 英文版重构 - 将 en-us 目录重命名为 en - 更新相关配置和组件中的语言代码 ## Bug 修复 - 修复 index.js 中重复的组件导入语句导致的 build 失败 - 恢复被注释的 InvertedIndexDemo 和 SearchRelevanceDemo 导入 - 修复 HomeFeatures.vue 中 en-us 与 config.mjs 中 en 不一致导致的语言切换问题 ## 其他改进 - 添加构建脚本 (scripts/build.mjs) - 更新依赖版本
157 lines
7.4 KiB
Markdown
157 lines
7.4 KiB
Markdown
# 从单体到微服务的演进
|
||
|
||
::: tip 前言
|
||
**没有哪个架构是"最好的",只有"最适合当前阶段的"。** 从单体到微服务不是一步到位的跳跃,而是随着业务规模和团队规模增长,逐步演进的过程。过早拆分微服务和过晚拆分一样危险。
|
||
:::
|
||
|
||
**这篇文章会带你学什么?**
|
||
|
||
学完这章后,你将获得:
|
||
|
||
- **演进路径**:理解从单体到微服务的四个阶段
|
||
- **拆分时机**:知道什么时候该拆、什么时候不该拆
|
||
- **拆分策略**:掌握按业务域拆分的方法论
|
||
- **通信模式**:了解服务间同步和异步通信的选择
|
||
- **数据拆分**:理解数据库拆分的挑战和方案
|
||
|
||
| 章节 | 内容 | 核心概念 |
|
||
|-----|------|---------|
|
||
| **第 1 章** | 架构演进路径 | 单体→模块化→SOA→微服务 |
|
||
| **第 2 章** | 拆分时机与原则 | Conway 定律、团队自治 |
|
||
| **第 3 章** | 拆分策略 | DDD 限界上下文、绞杀者模式 |
|
||
| **第 4 章** | 服务通信 | REST、gRPC、消息队列 |
|
||
| **第 5 章** | 数据拆分 | 数据库拆分、数据同步 |
|
||
|
||
---
|
||
|
||
## 1. 架构演进路径
|
||
|
||
架构演进不是技术驱动的,而是**组织规模驱动的**。当团队从 5 人增长到 500 人时,单体架构的协作效率会急剧下降。
|
||
|
||
| 阶段 | 架构 | 团队规模 | 特点 |
|
||
|------|------|---------|------|
|
||
| 起步期 | 单体应用 | 1~10 人 | 所有代码在一个项目中,部署简单 |
|
||
| 成长期 | 模块化单体 | 10~50 人 | 代码按模块划分,但仍然一起部署 |
|
||
| 扩张期 | SOA(面向服务) | 50~200 人 | 按业务线拆分为粗粒度服务 |
|
||
| 规模期 | 微服务 | 200+ 人 | 细粒度服务,每个团队独立开发部署 |
|
||
|
||
<ArchEvolutionDemo />
|
||
|
||
::: tip Conway 定律
|
||
"设计系统的组织,其产生的架构等同于组织的沟通结构。"——Melvin Conway
|
||
|
||
简单说:3 个团队做一个系统,最终会变成 3 个服务。架构拆分的本质是**组织拆分**。
|
||
|
||
**反向 Conway 定律**:既然组织结构决定了系统架构,那么想要什么样的架构,就先调整成什么样的组织结构。比如你想拆出独立的支付服务,就先组建一个独立的支付团队。很多公司微服务拆分失败,不是技术问题,而是组织没有跟着调整。
|
||
:::
|
||
|
||
---
|
||
|
||
## 2. 什么时候该拆微服务?
|
||
|
||
不是所有系统都需要微服务。过早拆分会带来不必要的复杂性。
|
||
|
||
| 信号 | 说明 | 建议 |
|
||
|------|------|------|
|
||
| 部署冲突频繁 | 多个团队改同一个代码库,经常冲突 | 考虑拆分 |
|
||
| 某模块需要独立扩容 | 搜索模块需要 10 倍于其他模块的资源 | 考虑拆分 |
|
||
| 技术栈需要差异化 | AI 模块用 Python,主站用 Java | 考虑拆分 |
|
||
| 团队 < 10 人 | 沟通成本低,单体足够 | 不要拆 |
|
||
| 业务还在探索期 | 需求变化快,边界不清晰 | 不要拆 |
|
||
| 没有 DevOps 能力 | 没有 CI/CD、容器化、监控体系 | 不要拆 |
|
||
|
||
---
|
||
|
||
## 3. 拆分策略
|
||
|
||
### 3.1 按业务域拆分(DDD 限界上下文)
|
||
|
||
DDD(领域驱动设计)的限界上下文(Bounded Context)是拆分微服务的最佳指导原则。每个限界上下文对应一个独立的业务域,有自己的数据模型和业务规则。
|
||
|
||
**什么是限界上下文?** 同一个词在不同业务域中含义不同。比如"用户"在用户域是指注册信息(姓名、邮箱),在订单域是指下单人(收货地址、支付方式),在推荐域是指行为画像(浏览历史、偏好标签)。限界上下文就是划定一个边界,在这个边界内,术语和模型有明确统一的含义。
|
||
|
||
```
|
||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||
│ 用户域 │ │ 订单域 │ │ 支付域 │
|
||
│ │ │ │ │ │
|
||
│ User │ │ Order │ │ Payment │
|
||
│ Profile │ │ OrderItem │ │ Refund │
|
||
│ Address │ │ Cart │ │ Transaction │
|
||
│ │ │ │ │ │
|
||
│ 用户服务 │ │ 订单服务 │ │ 支付服务 │
|
||
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
|
||
│ │ │
|
||
└────── API 调用 / 事件通信 ───────┘
|
||
```
|
||
|
||
| 限界上下文 | 核心实体 | 对应服务 |
|
||
|-----------|---------|---------|
|
||
| 用户域 | User、Profile、Address | 用户服务 |
|
||
| 商品域 | Product、Category、SKU | 商品服务 |
|
||
| 订单域 | Order、OrderItem | 订单服务 |
|
||
| 支付域 | Payment、Refund | 支付服务 |
|
||
| 物流域 | Shipment、Tracking | 物流服务 |
|
||
|
||
### 3.2 绞杀者模式(Strangler Fig Pattern)
|
||
|
||
不要一次性重写整个单体,而是像绞杀榕一样,逐步用新服务替换旧模块:
|
||
|
||
1. 在单体外部创建新服务
|
||
2. 通过代理层将部分流量路由到新服务
|
||
3. 验证新服务稳定后,逐步迁移更多流量
|
||
4. 最终完全替换旧模块
|
||
|
||
---
|
||
|
||
## 4. 服务通信模式
|
||
|
||
| 方式 | 协议 | 特点 | 适用场景 |
|
||
|------|------|------|---------|
|
||
| REST | HTTP/JSON | 简单通用,生态好 | 对外 API、CRUD 操作 |
|
||
| gRPC | HTTP/2 + Protobuf | 高性能,强类型 | 内部服务间高频调用 |
|
||
| 消息队列 | AMQP/Kafka | 异步解耦,削峰填谷 | 事件通知、异步任务 |
|
||
| GraphQL | HTTP/JSON | 客户端按需查询 | BFF 层、移动端 |
|
||
|
||
::: tip 同步 vs 异步的选择
|
||
- **需要立即返回结果** → 同步(REST/gRPC)
|
||
- **不需要立即返回** → 异步(消息队列)
|
||
- **一个事件触发多个动作** → 异步(发布-订阅)
|
||
|
||
经验法则:能异步就异步,同步调用链越长,系统越脆弱。
|
||
:::
|
||
|
||
---
|
||
|
||
## 5. 数据拆分:最难的部分
|
||
|
||
微服务拆分中最痛苦的不是代码拆分,而是数据库拆分。每个服务应该拥有自己的数据库,但这意味着跨服务查询变得困难。
|
||
|
||
| 挑战 | 描述 | 解决方案 |
|
||
|------|------|---------|
|
||
| 跨服务 JOIN | 不能直接 JOIN 两个服务的表 | API 组合查询、数据冗余 |
|
||
| 分布式事务 | 跨库事务无法用本地事务 | Saga、本地消息表 |
|
||
| 数据一致性 | 多个服务的数据可能暂时不一致 | 最终一致性、事件驱动 |
|
||
| 数据迁移 | 从共享库迁移到独立库 | 双写过渡、数据同步工具 |
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
从单体到微服务是一个渐进的过程,不是一蹴而就的革命。
|
||
|
||
回顾本章的关键要点:
|
||
|
||
1. **演进路径**:单体→模块化单体→SOA→微服务,每一步都有明确的驱动力
|
||
2. **拆分时机**:团队规模、部署冲突、扩容需求是拆分的信号
|
||
3. **拆分策略**:用 DDD 限界上下文指导拆分,用绞杀者模式渐进迁移
|
||
4. **通信选择**:能异步就异步,同步调用链越短越好
|
||
5. **数据拆分**:最难但最重要,接受最终一致性是关键心态转变
|
||
|
||
## 延伸阅读
|
||
|
||
- [Building Microservices](https://www.oreilly.com/library/view/building-microservices-2nd/9781492034018/) - Sam Newman 微服务经典
|
||
- [Monolith to Microservices](https://www.oreilly.com/library/view/monolith-to-microservices/9781492047834/) - 渐进式迁移指南
|
||
- [Domain-Driven Design](https://www.domainlanguage.com/ddd/) - Eric Evans 的 DDD 经典
|
||
- [The Strangler Fig Pattern](https://martinfowler.com/bliki/StranglerFigApplication.html) - Martin Fowler 的绞杀者模式
|
||
|