微服务 vs 模块化单体:拆了三年服务后,我们崩了(附决策参数)

🔑 关键词:微服务,模块化单体,架构选型,服务拆分,落地实践

📖 摘要:从真实团队拆微服务后 P99 从 80ms 涨到 450ms 的经历讲起,用具体数字对比单体、微服务与模块化单体的取舍,给出什么时候不该拆微服务的判断参数。

2019年我还在一个40人的后端团队,负责一个核心交易老系统。 这个Spring Boot单体工程已经跑了5年,代码量超过50万行,但线上流量其实不高,QPS峰值2000,平时不到300。 最痛的是发布:整个应用打一个Fat Jar,启动12分钟,从提交代码到生产环境需要40分钟。 代码冲突频繁到每天下午都要开“合并仲裁会”。 有一次因为一个静态全局变量没有清理,线上所有用户都收到了双倍积分,差点酿成事故。

图片

于是架构委员会决定学习Netflix,目标是三个月把单体拆成微服务。 我们没有评估业务复杂度,只记住了“独立扩展、故障隔离、技术栈自由”这些口号。 拆完之后,Git仓库从1个变成126个,线上服务实例120个。 为了配合微服务,我们引入了Nacos注册中心、Sentinel限流、Seata分布式事务,还有SkyWalking做全链路追踪。 这些组件本身就是十几个进程,环境依赖数从3个变成了20多个。

图片

麻烦来得比收益更快。 本地联调需要同时启动十几个下游服务,小团队根本承受不了。 一次下单最长的调用链要穿7个服务、11次RPC,P99从单体的80ms涨到450ms。 分布式事务用TCC,补偿代码写了4000行,依然有对不上账的告警。 半年后统计,30%的线上故障和业务逻辑无关,而是注册中心抖动、网络超时或配置漂移。 为了支撑原来1.5倍流量,机器成本翻了4倍。

图片

后来我们做了一件事:重新合并那些生命周期一致、数据域相同、发布频率接近的服务。 不看架构图,只看git提交记录和数据库表依赖:哪些服务总被同一批人一起改,哪些表被多个服务共用,就把它们划成模块。 最终我们用模块化单体来落地——一个应用工程内,模块之间通过Java接口调用,禁止直接访问对方Mapper;对外保留REST API作为BFF,内部不再走HTTP。 两个月时间,服务数从120个合并到32个;交易链路P99回到200ms,机器成本降了40%。 如果让我总结,微服务不是错,错在把服务拆分当成了目标。 面对80人以下的团队、日均调用量不到百万次、没有独立扩容硬需求的系统,请先考虑模块化单体。 当你真的有某个领域需要3倍以上的独立扩容、有独立团队能自主把控它的发布时,再拆那一个领域也不迟。

图片

判断框架:

  • 服务数量<50,团队≤3个,先别拆;
  • 一条业务链路RPC跳数>5,说明拆碎了;
  • 补偿代码量>业务代码量,是硬凑微服务;
  • 单个模块扩容诉求是其他模块的3倍以上,才值得独立服务。

图片

🏷️ 标签: