微服务合并重构:从十几个服务收敛到六个服务的实践思考
前言
很多人第一次做微服务项目时,很容易陷入一个误区:服务拆得越多,架构就越高级。
但真正开始维护之后,问题会很快暴露出来:启动慢、依赖多、调用链路长、排查困难、Docker 资源占用高、接口改动牵一发动全身。尤其是个人项目或学生项目,如果十几个服务同时存在,架构看起来很完整,实际开发体验却会越来越沉重。
所以微服务重构的第一步,不一定是继续拆,而是先合并。
这篇文章记录一次后端服务合并的思考过程:目标不是把系统退回单体,而是把过度拆分的服务收敛成更清晰、更容易维护、更适合后续 Kubernetes 演示的 5 到 7 个核心服务。
原来的问题:服务太多,边界太碎
项目最开始的设计偏向“功能一个服务”,例如用户、视频、评论、弹幕、点赞、收藏、搜索、推荐、AI、直播、后台管理等都可能被拆成独立服务。
这种拆法的好处是概念上很清楚,每个模块都能单独命名、单独启动、单独注册到 Nacos。但问题也很明显:
- 服务数量过多,本地开发和调试成本很高。
- 很多服务之间强依赖,实际并不能独立演进。
- Feign 调用过多,导致链路变长,错误定位困难。
- 有些服务只是 CRUD 外壳,独立存在的价值不大。
- Docker 里 Nacos、RocketMQ、MySQL、Redis、ES、MinIO 等基础设施已经有固定资源开销,再叠加大量 Java 服务,机器负担明显增加。
微服务不是为了“数量多”,而是为了隔离复杂度。如果拆分之后复杂度反而增加,就应该重新审视边界。
合并原则:按数据强相关,而不是按名字好看
这次合并没有简单按业务名词划分,也没有完全按物理资源属性划分,而是综合考虑五个因素。
第一,数据强相关性。
如果几组表经常需要一起查询、一起更新,甚至有事务性要求,那么它们更适合放在同一个服务里。否则为了保持“服务独立”,反而会制造大量远程调用和数据拼接。
第二,调用频率和延迟敏感度。
如果两个服务之间几乎每个请求都会互相调用,那么它们的边界就值得怀疑。高频同步调用会让微服务退化成“分布式单体”。
第三,技术栈一致性。
如果多个服务使用同样的语言、框架、数据库访问方式,并且部署特征也类似,那么合并后可以减少重复配置、重复 DTO、重复 Feign 客户端和重复启动成本。
第四,独立扩展需求。
有些模块确实需要单独扩展,例如视频处理、AI 推理、搜索推荐。这类模块负载特征明显,不应该被强行合并到普通业务服务里。
第五,维护者规模。
如果是个人项目或学生项目,一个人同时维护十几个服务并不现实。更合理的做法是保留微服务形态,但把服务数量控制在 5 到 7 个。
最终服务划分
合并后的目标是 6 个后端服务。
1. 网关服务
网关独立保留。
它负责统一入口、路由转发、跨域、认证透传、限流入口等能力。网关不应该承载具体业务逻辑,它的价值是让前端只面对一个入口,同时为以后接入 Ingress、灰度发布、统一鉴权打基础。
2. 用户与社交服务
该服务包含用户、账号、关注、粉丝、私聊、系统消息等能力。
这些功能都围绕“用户关系”展开,数据之间天然有关联。例如用户主页需要用户信息、关注状态、粉丝数、消息状态;私信和通知也经常依赖用户身份体系。
把这些能力放在一起,可以减少大量用户相关的远程调用。
3. 视频与媒体服务
该服务负责视频、稿件、文件上传、分片上传、转码、抽音频、封面、播放地址、直播和剪辑后端等媒体相关能力。
这是系统里最典型的 IO 密集型服务。它大量依赖对象存储、视频文件、转码任务和播放链路。视频二进制数据不应该通过服务之间传来传去,而应该放在对象存储里,服务之间只传文件 key、任务 id、状态和事件。
因此,视频编码、转音频、文件元数据管理都应该归还给视频与媒体服务,而不是混在 AI 服务里。
4. 内容交互服务
该服务包含弹幕、评论、动态、点赞、收藏、投币等互动能力。
这些功能看似分散,但它们的共同点是都围绕内容对象发生。用户看视频时,需要评论、弹幕、点赞状态、收藏状态、投币状态等信息。如果拆得过碎,一个视频详情页可能要调用多个服务。
动态也更适合放在内容交互里,而不是用户社交里。因为动态的核心不是用户关系本身,而是内容发布和互动。
5. 搜索与推荐服务
搜索和推荐独立保留。
搜索通常依赖 Elasticsearch,推荐可能依赖用户画像、内容标签、行为日志和算法逻辑。它们和普通业务 CRUD 的负载模型不同,也可能需要独立扩展。
但用户画像不一定要完全放在推荐服务里。更合理的方式是:用户基础画像由用户服务提供,搜索推荐服务消费行为事件,形成自己的召回、排序和索引数据。
6. AI 服务
AI 服务只保留真正 AI 相关的能力,例如审核、摘要、字幕、语音识别、客服、本地模型调用等。
之前把转码、抽音频等视频处理能力也放进 AI 服务,是因为它们服务于视频 AI 流程。但从边界上看,这些能力并不是 AI,它们是媒体预处理。更合理的做法是:
- 视频服务负责上传、转码、抽音频和生成对象存储 key。
- 视频服务通过 MQ 发出“音频已生成”“视频可分析”等事件。
- AI 服务消费事件,只处理 AI 推理、摘要、字幕、审核等任务。
- AI 结果再通过事件或接口回写业务状态。
这样 AI 服务不会变成“什么重活都往里塞”的大杂烩。
为什么不是继续拆得更细
微服务拆分应该服务于独立演进,而不是服务于命名。
如果评论、弹幕、点赞、收藏都拆成单独服务,表面上每个服务职责单一,但实际会出现这些问题:
- 视频详情页聚合成本变高。
- 远程调用数量增加。
- DTO 和 Feign 客户端重复。
- 本地启动和排查更困难。
- 小服务没有真正独立扩展价值。
对个人项目来说,最重要的是把“分布式关键链路”做清楚,而不是把每个功能都拆成独立 Spring Boot 应用。
合并之后,Feign 怎么处理
服务合并后,原来的 Feign 调用不应该永久保留。
如果两个模块已经进入同一个服务,原来的远程调用应该逐步改成本地 service 调用。这样可以减少网络开销,也能让事务、异常处理和调试更自然。
但删除旧 Feign 接口也要有顺序:
- 先确认调用方和被调用方已经在同一个服务内。
- 把高频链路改成本地 service 调用。
- 编译通过后再删除无用 Feign client。
- 保留跨服务边界上真正需要的接口。
也就是说,旧 Feign 接口可以慢慢删,但不能一边合并服务,一边让内部调用继续绕远路。
MQ 在合并后的角色
合并服务不代表所有事情都要同步调用。
视频处理、AI 摘要、审核、推荐更新、搜索索引更新,都更适合用 MQ 异步解耦。
以视频处理链路为例:
- 用户上传视频到对象存储。
- 视频服务保存元数据,并提交转码或抽音频任务。
- 任务完成后发送 MQ 事件。
- AI 服务消费事件,生成字幕、摘要或审核结果。
- 前端通过 SSE 或轮询查看任务进度。
这种架构比服务之间互相 Feign 调用更清晰。Feign 适合查询和轻量同步操作,MQ 适合长耗时任务和状态流转。
合并不是退回单体
这次重构不是否定微服务,而是从“过度拆分”回到“合理拆分”。
合并后的系统仍然具备微服务特征:
- 有网关统一入口。
- 有独立服务边界。
- 有服务注册与发现。
- 有 MQ 异步事件。
- 有对象存储、缓存、搜索、数据库等基础设施。
- 后续可以部署到 Kubernetes。
区别只是服务数量更少,边界更粗,维护成本更低。
总结
微服务架构的关键不是服务数量,而是边界质量。
对个人项目或学习项目来说,最合理的方式不是追求十几个服务同时运行,而是把系统收敛到 5 到 7 个真正有意义的服务:
- 网关
- 用户与社交
- 视频与媒体
- 内容交互
- 搜索与推荐
- AI 服务
这样的结构既能保留微服务、MQ、对象存储、搜索、AI、Kubernetes 等技术展示空间,又不会让运维和调试成本失控。
真正值得展示的不是“我拆了多少服务”,而是“我为什么这样划分服务边界,以及这些边界如何降低复杂度”。