微服务系列(一) 我们的WMS单体应用终于扛不住了

我们的 WMS 单体应用,终于扛不住了

副标题:从一个 200 万行代码的 Spring Boot 项目说起


一、那个让人崩溃的周五

你有没有经历过这种周五?

去年双十一前夜,我们团队还在加班。产品经理急匆匆地跑来:“出库规则要改,客户临时要求把优先发货仓库从 A 仓换成 B 仓!”

我心想,这不就是改个配置嘛,分分钟的事。

结果一翻代码,傻眼了——出库规则散落在七八个类里,有的写在 Service 里,有的埋在 Mapper 的 SQL 里,还有一段祖传逻辑躲在某个叫 OutStockUtilV2 的工具类中。改完一处,IDE 的引用分析显示还有 23 处调用。更可怕的是,其中 3 处是反射调用的,IDE 根本搜不到。

“全量回归测一下吧。” 测试同学淡淡地说。

3 天。 就为了一个仓库优先级的调整,我们测了整整 3 天。期间还顺手修了两个因为"改一行崩三处"引发的 Bug。

那天晚上我坐在工位上,看着 Jenkins 上那个跑了 2 小时 17 分钟的发布流水线,陷入了沉思:这系统,是不是该动大手术了?


二、我们的 WMS 长什么样

先给不熟悉 WMS 的朋友科普一下,WMS 就是仓库管理系统(Warehouse Management System)。咱们这套系统,服务了公司 20 多个仓库,日均 10 万单,大促峰值能冲到 50 万单。

说白了,它的核心工作就这几件事:

  • 入库:货来了,验收入库,上架
  • 出库:订单来了,拣货、打包、发运
  • 库存:实时算库存,别让超卖把老板坑了
  • 库内作业:盘点、移库、补货这些脏活累活
  • 基础资料:商品、仓位、客户信息
  • 报表:给老板看的各种数据大屏

那它的技术架构长啥样呢?我用文字给你"画"一张简图:

┌─────────────────────────────────────┐
│           前端页面(JSP + jQuery)    │
│         前后端还没分离呢              │
└─────────────┬───────────────────────┘
              │
┌─────────────▼───────────────────────┐
│      单体 Spring Boot 应用           │
│   (200 万行代码,一个 fat jar)      │
│                                     │
│  ┌────────┐ ┌────────┐ ┌────────┐  │
│  │ 入库模块 │ │ 出库模块 │ │ 库存模块 │  │
│  └────────┘ └────────┘ └────────┘  │
│  ┌────────┐ ┌────────┐ ┌────────┐  │
│  │ 库内作业 │ │ 基础资料 │ │ 报表模块 │  │
│  └────────┘ └────────┘ └────────┘  │
└─────────────┬───────────────────────┘
              │
┌─────────────▼───────────────────────┐
│        单库 MySQL(主从复制)         │
│     CPU 常年 80%+,磁盘快满了        │
└─────────────────────────────────────┘

对,你没看错。200 万行代码,打成一个 jar 包;30 多个前端 JSP 页面直接塞在 src/main/webapp 里;数据库就一台主库扛所有读写,从库只用来跑报表。

这套架构在创业早期是功臣——开发快、部署简单、出了问题翻日志就行。但五年过去,它已经从"功臣"变成了"功臣(沉重地)"。


三、为什么现在必须拆

说实话,拆微服务这件事,我们管理层吵了快一年。有人觉得"能跑就别动",有人觉得"再不动就晚了"。最后促成决策的,其实是三个维度的压力一起爆发了。

3.1 业务维度:新渠道需求互相打架

公司这两年业务扩张得厉害,原来只服务自营电商,现在又接了跨境电商、B2B 批发、线下门店。

每个渠道的出库逻辑都不一样:

  • 自营电商:优先发离用户近的仓,求快
  • 跨境电商:要报关、要合单,流程复杂
  • B2B:一单单量巨大,要按批次先进先出
  • 门店:得支持到店自提和门店调拨

这些需求塞进同一个代码库,结果就是 if-else 满天飞。最夸张的一个方法,我数过,127 行代码,里面嵌了 9 层 if-else。每次加新渠道,都得小心翼翼地问:“我这段逻辑会不会把老渠道搞坏?”

3.2 技术维度:数据库真的加不动索引了

我们的核心库存表,数据量已经飙到 3 亿多条。为了支撑查询,这张表上的索引多达 17 个。

DBA 老哥有一次在群里发了一张截图,主库 CPU 飙到 97%,慢查询日志一分钟刷了 2000 多条。他@我说:“兄弟,这表不能再加索引了,再加快写操作要崩。”

我回他:“那怎么办?”

他回了我一个表情包:一只猫在键盘上躺平。

其实问题很清晰——单库单表扛不住了。但单体应用里,所有模块共享同一个数据库连接池。报表模块跑一个复杂统计查询,能把连接池占满,导致出库接口超时。这就是典型的"一颗老鼠屎坏了一锅粥"。

3.3 团队维度:30 个人抢一个代码库

我们后端团队从 5 个人扩张到 30 个人,代码库还是那一个。

每天早上第一件事是什么?不是写代码,是解决合并冲突。你改 StockService.java,他也改 StockService.java,Git 冲突解决到怀疑人生。

更魔幻的是发布。原来 10 分钟就能发完,后来变成了 2 小时——因为要等所有模块的测试都通过,要协调各个团队的发布窗口。有一次,报表团队的功能已经测好了,但因为入库团队发现了一个 Bug 要修,整个发布被推迟到第二天。

30 个人被一个 jar 包绑在了一起。 这效率,想想都心酸。


四、微服务是不是银弹

聊到这,估计有读者要说了:“你们这不就是要上微服务嘛,直接干啊!”

别急。作为一个踩过坑的老司机,我得诚实地说一句:微服务不是银弹,它甚至可能是新的坑。

拆完之后,复杂度一定会上升。原来一个方法调用就能搞定的事,现在可能要跨服务调 HTTP 或 RPC;原来本地事务就能保证的一致性,现在得考虑分布式事务;原来只有一个日志文件要翻,现在得在十几个服务的日志里排查问题。

所以咱们不能为了拆而拆。我画了个简单的判断标准,供你参考:

维度我们的现状是否适合拆
团队规模30+ 后端,分 5 个业务小组✅ 适合,按团队边界拆分
业务复杂度多渠道、多仓库、规则差异大✅ 适合,按业务领域拆分
运维能力有 3 人 DevOps 小组,玩过 K8s⚠️ 勉强,需要补基础设施
系统稳定性要求电商系统, downtime 成本极高⚠️ 拆分过程必须平滑过渡

结论是什么?可以拆,但要拆得慢、拆得稳,不能一刀切。

我们最终决定走"绞杀者模式"——新功能用微服务写,老功能逐步迁移,而不是直接把 200 万行代码一把推翻重写。这个决策,后来证明救了我们好几次。


五、本系列要讲什么

既然决定拆了,那怎么拆?拆成什么样?拆的过程中会遇到哪些坑?

这些问题,我会用接下来 8 篇文章,一步一步跟你分享我们的真实经历。不是教科书式的理论,而是有血有肉的实战记录。

先给你剧透一下后续内容:

  • 第 2 篇:DDD 领域拆分实战 —— 我们是怎么用领域驱动设计,把 6 大模块拆成 12 个服务的
  • 第 3 篇:数据库拆分与分库分表 —— 3 亿条库存表怎么平滑迁移,不停机、不丢数据
  • 第 4 篇:服务间通信选型 —— 同步 or 异步?HTTP or gRPC?消息队列怎么兜底
  • 第 5 篇:库存服务设计 —— 超卖问题怎么解?预占、冻结、释放的完整状态机
  • 第 6 篇:Saga 分布式事务落地 —— 出库失败怎么回滚?我们用 Saga 模式踩过的坑
  • 第 7 篇:基础设施搭建 —— K8s、Nacos、Gateway、CI/CD 流水线的搭建实录
  • 第 8 篇:监控与排障体系 —— 链路追踪、日志聚合、告警规则,出问题 5 分钟定位
  • 第 9 篇:一年后的复盘 —— 哪些决策做对了?哪些坑其实可以避开?

六、写在最后

写这篇文章的时候,我翻了一下去年的项目文档,看到那个双十一前夜的发布记录:2 小时 17 分钟,全量回归 3 天,合并冲突 47 处。

现在呢?同样的改动,15 分钟就能上线,回归测试只覆盖对应服务,合并冲突?基本没有了。

当然,这一路上我们也付出了代价:基础设施投入增加了,团队学习成本提高了,第一次做分布式事务的时候差点把测试环境搞崩。

但回头看,值。

如果你也正在经历类似的困境——单体应用越来越重,改不动、发不动、扛不住——希望这个系列能给你一些参考。不敢说我们的方案是最优的,但至少是真实的、踩过坑的。

你们公司现在是什么架构?有没有遇到过"改一行怕崩三处"的恐惧?欢迎在评论区聊聊,咱们一起交流!


(本文是 WMS 微服务改造系列第 1 篇,下篇预告:《DDD 领域拆分实战——6 大模块怎么拆成 12 个服务》)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值