微服务 与 单体架构
对比维度 | 单体架构 | 微服务架构 |
架构本质 | 一个单一的、功能齐全的应用程序 | 一组小型、独立的服务集合 |
开发 | 团队工作在同一个代码库,易产生冲突。技术栈统一。 | 每个服务可以由 独立的小团队 负责,允许使用最适合的技术栈(多语言)。 |
部署 | 任何微小的更改都需要 重新构建和部署整个应用。 | 每个服务可以独立部署。更新某个功能只需部署对应的服务,风险更低。 |
扩展性 | 整体扩展: 必须复制整个应用程序,即使只有10%的功能需要扩展。 | 精细化扩展: 只需为需要处理更多负载的特定服务进行扩展。资源利用率高,成本效益好。例如,只为“报表生成”服务增加实例。 |
容错性 | 一个组件(如一个内存泄漏)的故障可能导致整个应用程序崩溃。 | 故障隔离: 一个服务的故障通常不会导致整个系统瘫痪。其他服务可以继续运行。 |
数据管理 | 通常共享一个单一、统一的数据库。 | 每个服务拥有自己独立的数据库(或数据库 schema),避免服务间通过数据库产生紧密耦合。 |
(单体:订单模块、用户模块、报表模块……所有这些代码仍然紧密耦合在同一个进程里,各个模块(订单、用户)之间是直接的方法调用,而不是网络调用 )
为什么说单体架构可以是分布式的?
单体架构确实可以以 分布式 的方式部署。这听起来可能有些矛盾,但关键在于区分 “应用程序架构” 和 “部署架构”:
-
应用程序架构(Application Architecture):关注的是 代码 和 功能 是 如何组织 的。是作为一个 单一单元(单体),还是作为 多个独立服务(微服务)
-
部署架构(Deployment Architecture):关注的是 编译后的应用程序 如何 被放置到硬件(服务器、虚拟机、容器)上运行。是部署在 一台机器(集中式),还是分布在 多台机器(分布式)?
“单体架构”描述的是 应用程序架构 的特征,而“分布式应用程序”描述的是 部署架构 的特征。它们是不同维度上的概念,因此可以组合在一起。
单体如何实现分布式部署
一个单体应用程序(一个完整的、不可分割的代码单元)可以通过以下两种主要方式实现“分布式部署”:
1. 整体 复制式 部署(Clustering / Load Balancing)
这是最常见的方式。您将 整个单体应用程序的完整副本 部署到多台服务器(虚拟机、容器)上,并在这些副本前面使用一个 负载均衡器。
-
工作原理:
-
您编译打包好整个单体应用(比如一个巨大的
app.war
文件)。 -
您将这个完全相同的包部署到服务器A、服务器B、服务器C上。
-
用户访问负载均衡器的地址。
-
负载均衡器将请求轮流转发到后台的任意一台服务器(A, B 或 C)。
-
每台服务器都有能力独立处理这个请求,因为它运行着整个应用的完整功能。
-
-
为什么这仍然是单体?
-
因为 每一台服务器 上运行的仍然是那个 完整的、包含所有业务功能的、不可分割的应用程序单元。代码结构没有改变。
-
订单模块、用户模块、报表模块……所有这些代码仍然紧密耦合在同一个进程里。
-
-
这样做的目的:
-
提高 吞吐量 和 处理能力:三台服务器 自然能比 一台服务器 处理更多的用户请求。
-
提高 可用性:如果服务器A宕机了,负载均衡器 会自动将 流量 切换到正常的服务器B和C上,整个应用对外仍可用。
-
2. 层次 分离式 部署(N-tier Deployment)
另一种方式是将 单体应用程序 的不同 逻辑层次 部署到不同的服务器上。一个经典的例子就是三层架构:
-
Presentation Layer (Web Server): 运行在服务器A上,负责显示网页和处理用户输入。
-
Application/Business Layer (App Server): 运行在服务器B上,包含所有的业务逻辑(订单处理、用户管理等)。
-
Data Layer (Database Server): 运行在服务器C上,负责数据存储。
-
为什么这仍然是单体?
-
虽然层次被 物理分离 了,但关键在于“应用/业务层”本身是一个不可分割的单元,只是把 业务代码 单独部署在了一台专门的应用服务器上。业务层内部的各个模块(订单、用户)之间是直接的方法调用,而不是网络调用。它们没有被拆分成独立的服务。
-
与 微服务分布式 的根本区别
现在我们可以清晰地看到单体分布式和微服务分布式的核心差异:
特性 | 单体架构的分布式部署 | 微服务架构的分布式部署 |
部署单元 | 整个应用程序 的 多个完整副本。 | 每个独立服务 的多个副本。 |
扩展粒度 | 粗粒度:必须整体扩展,即使只需要扩展一个功能。 | 细粒度:可以 只扩展需要更多资源的那个服务(如只扩展报表服务)。 |
技术栈 | 整体统一。 | 每个服务可以独立选择最适合的技术栈。 |
内部通信 | 同一应用内部通过 本地方法 调用(Fast, In-memory)。 | 服务之间通过 网络API调用(REST, gRPC等,有网络开销和延迟)。 |
数据库 | 通常共享一个统一的数据库。 | 每个服务通常拥有自己独立的数据库。 |
所以,当有人说“我们的单体应用是分布式部署的”,他们通常指的是:
“我们将我们那个庞大的、包含所有功能的单一应用程序,复制了很多份,分别运行在多台服务器上,以实现更高的容量和可靠性。”
这并没有改变其 单体 的本质——代码依然耦合,部署单元依然庞大,扩展依然不够灵活。它只是利用了 分布式 的部署方式来 克服单体架构在 性能 和 可用性 上的某些限制,但无法解决 资源浪费、迭代困难 等根本性问题。