微服务到底给我们带来了什么

微服务到底给我们带来了什么

一、当初为什么要做微服务

那是 2019 年底,单体项目已经跑了三年,代码量大概是 40 万行,十几个人的团队在同一个 Git 仓库里写代码。

问题不是一开始就很严重的。慢慢地,我们开始注意到一些小事:一个前端改了首页的推荐逻辑,要等后端同学把订单、库存、商品三个模块都合并完才能发版;某个节日活动的促销模块流量突然翻了 5 倍,但整个项目只能整体扩容,把用不上的模块也跟着扩了一遍,成本浪费明显。

最终推动拆分的直接原因,是有一次上线,测试同学发现了一个回归 Bug,追查下来是 A 同学的改动影响了 B 同学改的逻辑,两个人的代码耦合在了同一个 Service 里。那次修复耽误了三天,所有人的需求都压在那里等着。

技术负责人开始认真讨论微服务方案,核心诉求是两个:独立部署模块隔离

二、拆分完之后,解决了什么

有几件事确实变好了:

1. 促销模块可以单独扩容了

活动期间只需要把 promo-service 的实例数拉起来,其他服务不动。这个效果是立竿见影的,大促期间的成本比之前整体扩容节省了大概 30%。

2. 团队发版解耦

商品团队和订单团队不再互相等待,各自维护自己的 Git 仓库,各自的 CI/CD 流水线。周五订单团队要上线新功能,不再需要等商品团队的代码合并完成。

3. 故障隔离有了边界

某次推荐服务挂了,因为订单服务做了降级处理(推荐位返回空不影响下单),用户体验基本没有影响。在单体时期,任何一个模块抛出的未捕获异常,都可能让整个进程崩溃。

三、拆分完之后,带来了什么新麻烦

这是比「优点」更值得聊的部分。

1. 分布式事务是真实的痛

拆分之前,创建订单和扣减库存在同一个数据库里,一个事务搞定。拆分之后,order-serviceinventory-service 各自有独立的数据库,跨服务的数据一致性变成了一个需要专门解决的问题。我们最终接入了 Seata 的 AT 模式,解决了大部分场景,但 Seata 自身也引入了配置复杂度和性能开销。

2. 一个请求要跨五个服务,出了问题不知道在哪

下单接口出了 Bug,可能的位置是 order-serviceinventory-servicepromo-serviceproduct-serviceuser-service 中的任何一个。每个服务的日志是分开的,要手动比对时间戳来还原调用链,效率极低。后来接入 SkyWalking 才真正把这个问题解决了,但这是后来的事。

3. 本地开发和联调成本急剧上升

以前 git clone 跑一个 Spring Boot 应用就能开发,拆完之后本地要同时起 8 个服务,还要有 Nacos、Redis、MySQL。新同学入职,光是把本地环境跑通就要花一天时间。用 Docker Compose 编排了本地环境之后好了很多,但拆分初期那段时间是真的很痛苦。

4. 运维和部署复杂度直线上升

单体时期,发版就是把一个 jar 包推到服务器上重启。微服务之后,每个服务都有独立的 CI/CD 流水线,有独立的健康检查、独立的配置、独立的日志收集。Jenkins 配置文件越来越多,出了部署问题排查链路也变长了。

四、如果重来,我会怎么做

不是否定微服务,是在那个具体情境下,我认为有几个决策值得重新想。

拆分时机可以再晚一点。当时团队只有十几个人,代码库虽然大但还没到「无法管理」的程度。微服务带来的收益(独立部署、模块隔离)在那个规模下其实是可以通过「模块化单体」来接近实现的——清晰的包结构 + 严格的模块边界,成本会低很多。

基础设施应该先行。我们是先把服务拆了,才开始补链路追踪、配置中心、服务网格这些基础设施。正确的顺序应该反过来:先把 SkyWalking、Nacos、统一日志采集建好,再开始拆服务。拆分初期那段「出了问题不知道怎么查」的黑暗期,大半是基础设施没跟上造成的。

拆分粒度不要追求「完美拆分」。我们当时有几个服务拆得太细,一个业务流程要调六七个服务,每增加一个跳转就增加一次网络延迟和一个故障点。有几个服务后来合并回来了。