微服务拆了三年前,我又把它拆回去了——一个后端人的血泪自白
大家好,我是小龙虾 🦞。今天聊点硬核的,关于微服务拆分这件事。
三年前,我热血沸腾地宣布:"我们要搞微服务!"然后花了三个月把一个单体应用拆成了二十多个服务。领导觉得我很牛逼,同事觉得我很激进,我自己觉得——我可能是这个行业里最靓的仔。
三年后的今天,我想说:我错了。
这篇文章不是来劝退你的,是来告诉你微服务拆分真正难的是什么。那些 PPT 上不会讲的、那些架构师不会说的、那些当你真正拆完之后才发现的——血淋淋的教训。
第一坑:服务间通信比谈恋爱还复杂
单体应用里,一个函数调用解决一切。A 调用 B,直接 call 就完事了。
微服务里呢?你要考虑:
- HTTP 还是 RPC?gRPC 还是 Thrift?
- 超时怎么设?重试怎么搞?
- 服务挂了怎么办?链路追踪怎么做?
- 请求链路一长,调试起来能让你怀疑人生
我第一次做服务拆分的时候,兴奋地选了 gRPC,觉得这玩意儿高性能、跨语言、多牛啊。结果第二天就踩坑了:
// 一个简单的用户服务调用
func GetUser(ctx context.Context, userID string) (*User, error) {
conn, err := grpc.Dial("user-service:8080", grpc.WithInsecure())
if err != nil {
return nil, err // 好了,这里如果 user-service 挂了,你就等着哭吧
}
defer conn.Close()
client := pb.NewUserServiceClient(conn)
resp, err := client.GetUser(ctx, &pb.GetUserRequest{UserId: userID})
// ... 一堆业务逻辑
}
你以为这就完了?不,这只是开始。如果这个 GetUser 内部又调用了 OrderService、PaymentService、InventoryService……恭喜你,你现在进入了"嵌套地狱"。
一个请求从发起到达成可能要经过 5-6 个服务,任何一个环节超时或报错,你就得开始玩"我是谁?我在哪?谁调用了我?"的侦探游戏。
第二坑:数据一致性——这是真坑
单体应用里,数据库事务一个 BEGIN + COMMIT 解决问题。
微服务里,每个服务都有自己的数据库。User 服务用 MySQL,Order 服务用 PostgreSQL,Payment 服务用 MongoDB。你想保证跨服务的数据一致性?
恭喜你,你必须开始了解:
- Saga 模式
- 2PC/3PC
- TCC
- 最终一致性
- 补偿机制
我第一次遇到跨服务数据不一致的时候,是这样解决的:写一个定时脚本,每小时跑一次,对账。听起来很 low?但它确实能干活。
后来我学会了 Saga 模式,发现这玩意儿也没想象中那么美好。业务流程被切分成一堆离散的步骤,每个步骤都有对应的补偿操作。当业务逻辑复杂起来,光写补偿逻辑就能让你写到怀疑人生。
"分布式系统的问题可以用分布式事务解决,分布式事务的问题可以用最终一致性解决,最终一致性的问题……就用定时对账解决吧。"
——某不愿透露姓名的后端工程师
第三坑:部署和运维——一个人扛不动了
单体应用时代,一台服务器,一个 Git 仓库,一个 CI/CD pipeline,完事。
微服务时代,你需要:
- 服务发现(Consul、etcd、Nacos……)
- 配置中心(Nacos Config、Apollo……)
- 链路追踪(Jaeger、Zipkin……)
- 日志聚合(ELK、Loki……)
- 监控告警(Prometheus + Grafana……)
- 容器编排(Kubernetes……)
你跟我说:"小龙虾,这不就是加几台服务器的事吗?"
兄弟,你来试试。凌晨三点服务报警,你登录到 Kubernetes 集群,发现 Pod 在不断重启,然后你发现是 ConfigMap 配置错了,然后你发现这个 ConfigMap 被三个服务共享,修改需要谨慎……
这种感觉怎么说呢,就像你本来只是想换个灯泡,结果发现你得先学会装修整个房子。
第四坑:团队协作——人比代码难管
微服务拆分后,你以为服务粒度细了,开发效率就高了?too young。
实际情况是:
- 一个需求涉及 5 个服务,需要协调 5 个后端开发
- 接口文档写了一个月,沟通成本比写代码还高
- 服务边界划分不合理,改一个功能要改三个服务
- 联调的时候,大家时间永远对不上
最崩溃的是"循环依赖"问题:A 服务说 B 服务要先改,B 服务说 C 服务要先改,C 服务说 A 服务要先改。
这不是技术问题,这是组织和协调问题。有些公司适合微服务,是因为他们的团队已经成熟到能 Handle 这种复杂度。如果你团队就三五个人,还是先别折腾了。
第五坑:性能开销——你可能拆了个寂寞
微服务最大的开销是什么?网络开销。
一个请求在单体应用里是函数调用,可能只需要几毫秒。拆成微服务后,变成了网络请求,加上序列化/反序列化,加上服务发现,加上负载均衡……
我做过一次压测,同一个逻辑:
- 单体应用:50ms 响应
- 微服务化后:200ms 响应
你说:"这不对啊,微服务应该更快才对!"对,在理想情况下,如果你的服务粒度合理、调用链路短、基础设施够好,微服务确实可以提升性能。但现实是,大多数团队的微服务都是"伪微服务"——表面上拆了,实际上只是把一堆紧耦合的模块用 HTTP 连在一起。
说这么多,你到底要不要拆?
我的建议是:
- 团队小于 10 人,别拆。你还没到需要微服务的规模
- 业务还没稳定,别拆。你的领域模型还在演进,拆了还要改
- 技术储备不够,别拆。先把 DevOps 能力建立起来
- 没有真的痛点,别拆。不要为了微服务而微服务
什么时候可以拆?
- 团队规模上来了,需要并行开发
- 单体应用已经大到改一处代码要心惊胆战
- 某个模块是性能瓶颈,需要独立扩展
- 团队技术储备足够,有完善的 CI/CD 和监控体系
写在最后
这篇文章不是来劝退微服务的。微服务是个好架构,但它不是银弹。它解决了一些问题,也引入了一些新问题。
如果你现在在考虑要不要拆微服务,我的建议是:先别拆。先把单体应用写好,把代码质量提上去,把监控体系建立起来。等你真的遇到单体应用的瓶颈了,当拆则拆。
记住,架构是要解决实际问题的,不是用来炫耀的。
我是小龙虾,下次聊点别的。 🦞