分布式答"怎么部署",微服务答"怎么拆、拆完怎么协同"。拆错的微服务只换来分布式单体:多三个注册中心、三套配置中心、三套日志采集。这是选型题,不是概念题。
三个容器上线第二天,线程池先死了
订单服务、库存服务、支付服务,三个独立容器,各自独立数据库,Spring Cloud 全家桶配齐。上线第二天,订单服务调库存服务超时。
超时本身不致命。致命的是重试。
重试三次,订单服务的线程池打满,整条交易链路跟着一起死。 监控面板一片红,但没有一块面板能告诉你谁先挂的。
这就是拆分后最典型的形态:进程分开了,业务耦合一点没减。 创建订单必须等库存扣完才能提交;改一个功能要动三个服务;排查一次问题要开三个终端,外加一条链路追踪。
它有个名字------分布式单体。
这道题考的是三个层次,不是定义
真正被考的,不是"分布式 = 多模块多服务器"这种背诵题。是概念定位、治理维度、架构演化这三层,你有没有打通。
第一层:Redis Cluster 是分布式的,但它不是微服务
分布式讲的是怎么部署:原来一台机器干的活,摊到多台机器上。它解决单机容量、性能和可用性。
微服务讲的是怎么拆:按业务能力切成多个独立服务。它解决模块耦合、发布互相牵制、团队协作、独立扩容。
一句话记牢:分布式不一定要拆服务。 Redis Cluster 是分布式的,它不是微服务。单体应用部署多个副本,再配 Redis Cluster 和分库分表,照样具备分布式特征------架构上它还是单体。
反过来,微服务必然是分布式的。但它得额外回答两个问题:按什么拆?拆完怎么保证它们还能一起干活?
| 维度 | 分布式 | 微服务 |
|---|---|---|
| 回答的问题 | 机器不够、扛不住 | 代码太乱、改不动发不了版 |
| 动作 | 多副本、分库分表、缓存集群 | 按业务拆、独立部署、独立库 |
| 反例 | Redis Cluster 是分布式,不是微服务 | 微服务必然分布式,但拆完还得管得住 |
分布式是多人搬砖,微服务是各管一摊。
第二层:服务网格救得了治理,救不了拆错的架构
把这两个概念混为一谈的团队,通常只看见了"拆",没看见"管"。
分布式要面对 CAP------一致性、可用性、分区容错性怎么取舍。微服务在这个基础上又叠了一整套治理复杂度:服务发现、配置管理、熔断降级、链路追踪、分布式事务。
2026 年的标准做法是服务网格统一治理。Istio 这类方案被大量企业采用,把流量管理、安全通信、可观测性从业务代码里剥出来,下沉到基础设施层。服务数量涨到几十上百个之后,靠代码里硬编码的熔断和重试,根本维护不过来。
泼盆冷水:治理工具再全,也救不了拆错的架构。 订单、库存、支付在业务逻辑上依然强耦合,服务网格只会让这份耦合跑得更稳一点------不会让它消失。
第三层:拆错了怎么办?把边界交给编译器
十年微服务狂热过去,行业开始冷静。
大量团队推完微服务,没拿到预期的敏捷性,反而陷进分布式单体:进程分离了,业务逻辑还是紧耦合;改一个功能要动三五个服务,跨服务排查依赖链路追踪;单体的简单性没享受到,分布式全套运维成本一分没少。
模块化单体于是重新回到台上:逻辑上严格隔离,物理上统一部署。 模块之间走进程内方法调用,耗时纳秒级,RPC 的网络延迟直接消失。
Spring Modulith 原生支持模块化单体的构建和依赖验证:
java
class ModularityTests {
private static final ApplicationModules modules =
ApplicationModules.of(TradeApplication.class);
@Test
void verifiesModularStructure() {
// 存在循环依赖或跨模块访问内部包,直接构建失败
modules.verify();
}
@Test
void writesDocumentation() {
new Documenter(modules).writeDocumentation();
}
}
ArchUnit 可以把模块间的依赖规则钉死在编译期:
java
@AnalyzeClasses(packages = "com.example.trade")
class ModuleDependencyTest {
@ArchTest
static final ArchRule order_must_not_reach_into_inventory =
noClasses().that().resideInAPackage("..order..")
.should().dependOnClassesThat()
.resideInAPackage("..inventory.internal..");
@ArchTest
static final ArchRule payment_must_not_depend_on_order =
noClasses().that().resideInAPackage("..payment..")
.should().dependOnClassesThat()
.resideInAPackage("..order..");
}
边界不再靠"约定"和 Code Review 的良心,而是靠构建阶段直接失败。这才叫约束。
两种形态的账,摊开来看其实很清楚:分布式单体付的是运维成本和改不动的双重代价,模块化单体把边界成本挪到了编译期------一次构建失败,换掉一次线上雪崩。
写在最后
分布式和微服务的关系本身不复杂:一个管部署,一个管拆法;一个问扛不扛得住,一个问管不管得住。真正难的是第三层------你的业务边界到底在哪。
如果你的团队正卡在"微服务改不动、单体又不敢回退"的中间态,不妨先问一句:我们拆的是业务能力,还是只是把一张表拆成了三次 RPC?你踩过哪种坑,评论区聊聊。觉得有用的话,点个赞。