从单体到微服务迁移的真实经验分享
结论先行:从单体迁到微服务,正确做法不是「重写一遍」,而是用绞杀者模式(Strangler Fig,即在新服务前面挂一层路由,把流量一个模块一个模块地切过去)分 6-12 个月渐进替换;真正决定成败的不是技术选型,而是「先拆哪个模块」和「可观测性是否先于第一个服务上线」。
什么情况下该拆微服务,什么情况下不该拆?
结论:只有当团队规模超过 8-10 人共用一个代码库、每周上线次数被压到 1 次以下、且模块边界已经清晰时,才值得拆。
可量化的判断信号有三个。第一是部署成本:一次上线要跑 40 分钟全量回归测试,任何小改动都得等别人合并。第二是协作成本:同一个仓库里 15 人并行提交,每天出现 3 次以上合并冲突。第三是故障隔离:某个报表导出把 CPU 打满,导致整站接口超时。
反过来说,日活 1 万以内、研发 5 人以内的项目不要拆。这时候「模块化单体」(Modular Monolith,即代码里严格分模块、模块间只通过接口调用,但打包成一个进程部署)能拿到 80% 的收益,却不用付分布式系统的代价。
拆分顺序怎么定?第一刀该砍哪里?
结论:第一刀砍「读多写少、依赖少、失败不影响主流程」的模块,典型是通知、短信推送、报表导出、搜索索引。
具体步骤:花 1-2 周做依赖分析,用 ArchUnit 或 jdeps 生成模块调用图,标出每个模块的入度和出度;画一张依赖矩阵表,选出度接近 0 的模块作为候选。我们当时的第一个服务是「通知服务」,它只依赖用户 ID,失败了大不了短信晚发 5 分钟,风险最低。
第二个拆的是「订单查询」,第三个才是「下单」。切忌按技术分层拆成 user-service、data-service 这种,那种拆法会让一次下单请求横跨 5 个服务,反而比单体更慢。
数据库怎么拆?迁移期能共享库吗?
结论:迁移期允许共享同一个物理库,但必须禁止跨服务直连别人的表,只能走 API 或领域事件。
分三步走:第一步分 schema 不分实例,各服务只拿自己的账号权限;第二步按表把数据迁到独立实例,用双写过渡——新老两套写路径同时写,跑 7 天对账任务,差异率低于 0.01% 再切读流量;第三步清理跨库 JOIN,把原来一条 SQL 搞定的关联改成两次查询加内存组装。
双写期间最容易出事的是「写成功一半」:老库成功、新库超时。解决办法是给双写加本地重试队列,重试 3 次仍失败则落盘告警,不要静默丢弃。
分布式事务和一致性怎么处理?
结论:不要用 XA 或 2PC,用「本地消息表 + 定时补偿」,复杂流程改用 Saga。
本地消息表的做法是:在同一个数据库事务里同时写业务表和消息表,事务提交后由后台任务每 5 秒扫一次未投递记录,发到 Kafka 3.x 或 RocketMQ 5.x,投递成功标记状态。失败重试 3 次后进死信队列,由人工或补偿脚本处理。这样做的核心是「业务写和消息写在同一个本地事务里」,天然具备原子性。
Saga 适合跨 3 个以上服务的流程,比如「下单 → 扣库存 → 扣优惠券 → 生成支付单」,每一步都要想清楚反向补偿动作是什么,补偿接口必须幂等。
线上出问题怎么排查?可观测性什么时候建?
结论:可观测性必须在拆出第一个服务之前就建好,等出事故再补,排查成本会翻 10 倍。
三件套缺一不可:日志统一带 traceId(一次请求的唯一标识),用 ELK 或 Loki 聚合;指标用 Prometheus + Grafana,至少覆盖每个服务的 P99 延迟、错误率、QPS 三个面板;链路追踪用 OpenTelemetry,初期采样率设 100%,流量稳定后降到 1%-10% 控制成本。
我们踩过的最深的坑,是第一个服务上线两周后才发现日志分散在 6 台机器上,定位一次超时问题花了 4 小时,补上 traceId 之后同样的排查只要 10 分钟。
我们踩过的三个坑
结论:最大的坑是「按技术分层拆」,第二是「过早引入服务网格」,第三是「没有契约测试」。
第一个坑前面说过,不重复。第二个坑是过早引入 Istio 这类服务网格,团队只有 8 个人却要维护一套控制平面,运维成本直接压过收益,后来回退成 API Gateway + 客户端负载均衡。第三个坑是接口没有契约测试,上游把返回字段从 userId 改成 user_id,下游解析全挂。上 Pact 做消费者驱动契约测试之后,这类问题在 CI 阶段就被拦住了。
收尾
迁移微服务的核心就三句话:用绞杀者模式渐进替换,不重写;先拆低风险模块,按业务能力而不是技术分层切;可观测性和契约测试先于第一个服务上线。做到这三点,12 个月内把 12 万行代码的单体拆成 8 个服务是可控的;做不到,拆得越快,故障越多。
原文链接:https://www.gj0.com/thread-115.html
转载请注明出处并保留本声明;内容仅代表作者观点,与本站立场无关。若本文涉嫌侵权,请联系本站处理。