连载中 3/8

从单体到微服务:一次痛苦但值得的架构演进

2026-08-21 · 657 阅读 · 1 评论 · 15 赞

为什么要拆?

我们的核心业务跑在单个 Spring Boot 仓库里,五年积累下来,编译一次要三分钟,改一个字段要动五个模块,部署窗口卡在凌晨两点——团队规模从 3 人涨到 12 人后,协作摩擦大到没法忽视。

不是我拍板要拆的。是某天测试环境再次因为一个无关模块的重启挂掉之后,测试组长在群里发了一句"能不能把 X 模块单独拎出来"。这句话成了导火索。

拆之前没想清楚的事

1. 数据库是最大的债

单体时,所有模块共用一个数据库,外键约束、跨模块 join 是家常便饭。拆服务时,你不可能一夜之间把所有表按服务边界分库——迁移周期可能长达半年。这半年里,多个服务共用同一个库,"微服务"其实只是"分布式单体"。

拆服务的第一步不是写新代码,而是理清数据归属。

2. 团队认知对齐比技术方案更难

架构方案写得很漂亮,但团队成员对"为什么要拆""拆完之后各自负责什么"的理解参差不齐。有人觉得微服务就是"每个模块独立部署",有人理解为"每个服务独立数据库"。认知不对齐会导致实施阶段反复返工。

我的建议:在拆之前,组织一次全员技术会议,把拆分边界、数据归属、团队分工画成一张大图贴到墙上。每天站会都对着这张图说话。

实际怎么拆的

我们用了绞杀者模式(Strangler Fig Pattern):先在网关层做路由分流,新功能直接写到新服务里,旧功能逐步迁移。迁移一个下线一个,保持系统始终可运行。

// 网关路由配置示例
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service

关键原则:旧接口不改行为,只改路由。让网关做透明代理,对前端无感。

踩过的坑

分布式事务是最痛的。单体时一个 @Transactional 搞定的事,拆完之后跨服务调用要用 Seata 或者最终一致性方案。我们的选择是:核心链路用 Seata AT 模式,非核心链路用消息队列 + 本地事务表做最终一致。

不是所有跨服务操作都需要强一致。想清楚业务能不能容忍短暂不一致,再选方案。

值不值?

拆完三个月回头看:编译时间从三分钟降到四十秒,部署可以按服务独立发布,团队各自迭代互不阻塞。但代价是运维复杂度翻倍——监控、日志、链路追踪全都要上。

如果你团队不到 5 人、业务变化不频繁,单体仍然是最好的架构。微服务不是银弹,是组织规模倒逼出来的工程妥协。

503

10 年全栈工程师 · 503咖啡馆主理人

#架构#微服务#Spring Boot

评论 (5)

端到端测试 2026-09-09 22:16
阶段4回归:点赞收藏评论全链路已就绪 ☕
端到端测试 2026-09-09 22:10
阶段4测试评论:<script>alert(1)</script>
李四 2026-08-30 09:15
"微服务不是银弹,是组织规模倒逼出来的工程妥协"——这句话得裱起来。
c
coffee_dev 2026-08-29 14:51
绞杀者模式确实是最低风险的拆法。但建议补一篇 Seata 的实践,AT 模式踩坑也不少。
张三 2026-08-29 10:23
数据归属那块深有感触,我们拆的时候也是卡在这。最后是做了半年的数据双写迁移才彻底分库。

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 1 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 12 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞