后端架构演进:从单体到云原生,我走了这五年
引言:那年我们还在用Tomcat部署WAR包

2018年夏天,我在一家中型互联网公司做后端开发。团队有七八个后端工程师,技术栈是Spring Boot + MyBatis + MySQL + Redis,所有业务模块都塞在一个WAR包里,通过Tomcat部署在阿里云ECS上。系统刚上线的时候还蛮顺畅的,但到了年底双十一期间,系统一崩再崩,最夸张的一次是用户注册接口卡了整整40分钟才恢复。
当时的我一边抓耳挠腮地看日志、一边祈祷着“能不能别再压测了”,内心却隐隐明白:是时候重构架构了。
今天我想跟你聊聊这几年经历的后端架构变迁之路——从单体应用到微服务再到云原生,这段旅程不仅改变了代码结构,也重塑了我的工程思维方式。
项目背景:一个电商后台的成长烦恼

我们的核心产品是一款面向中小商家的SaaS电商平台,涵盖商品管理、订单处理、营销活动等功能模块。初期采用的是单体架构,代码仓库虽然按照模块划分目录,但实际上所有逻辑都在一个工程里跑,数据库也是统一的一主多从的MySQL集群。
早期的结构非常简单粗暴:
src/
├── controller
│ ├── OrderController.java
│ ├── ProductController.java
│ └── UserController.java
├── service
│ ├── OrderService.java
│ ├── ProductService.java
│ └── UserService.java
└── dao
├── OrderDao.java
├── ProductDao.java
└── UserDao.java
所有的请求都打到同一个Tomcat实例,Redis缓存、RabbitMQ队列、定时任务都集中在这个工程中。
这种设计在创业初期非常高效——部署快、调试方便,团队小的时候沟通成本低。但随着功能越来越多,问题也随之暴露出来。
遇到的挑战:当单体开始撑不住增长
挑战一:部署风险大,发布像开盲盒
每次发版本都像是玩俄罗斯转盘游戏,尤其是涉及到核心模块(比如订单流程)时,改错一行代码就可能造成整个平台不可用。
有一次我们上线了一个新的促销功能,结果把原来的优惠券逻辑搞挂了,导致大量已下单的用户退款,运营部门打电话都打爆了……
挑战二:性能瓶颈明显,资源利用率低
由于都是部署在一个节点上,有些模块占用内存高,而另一些模块又常常空闲。为了应对高峰期的压力,我们不得不把整台服务器的资源配置拉满,但这其实是非常浪费的做法。
更糟糕的是,某个模块频繁GC或者出现慢查询,很容易拖垮其他正常的业务流程。例如,当时商品搜索模块因为数据量太大,使用了复杂的SQL查询,直接导致Redis连接池被打满,进而影响了支付链路的性能。
挑战三:团队协作变得困难
当人数增加到10+人之后,不同模块的改动经常出现冲突。合并代码、解决依赖、测试覆盖都变得越来越难。我们甚至专门为不同模块分配了负责人,但依旧难以避免“你改了一行我就全得回归一遍”的窘境。
更麻烦的是,不同业务线并行开发的需求越来越多,单体架构根本无法支撑灵活的迭代节奏。
解决思路:从拆分开始,走向服务化
第一步:按领域进行初次拆分
我们决定先对系统进行垂直拆分,将原本集中的模块按照业务领域划分成独立服务。大致划分为:
- 用户中心
- 商品中心
- 订单中心
- 营销中心
- 支付中心
- 日志审计服务
每个服务都独立开发、部署、升级,并且使用Spring Cloud Gateway来做API聚合和路由控制。
第二步:引入分布式治理能力
微服务之间要通信,一开始我们用了HTTP REST API,后来换成OpenFeign + Nacos注册中心,最后逐步过渡到gRPC和Dubbo协议。
这个过程其实并不顺利。比如有一次我们想优化库存模块的调用链路,从同步改为异步消息解耦,但因为缺乏经验,导致多个服务之间的状态不一致,后续花了好几天才修复数据一致性问题。
我们学到了一个教训:服务拆得太细反而不好维护,要根据业务边界合理划分,而不是盲目追求微服务数量。
第三步:基础设施升级,拥抱Kubernetes
2020年开始,我们逐步将ECS机器迁移到Kubernetes集群上,结合Helm做配置管理、Prometheus+Grafana做监控告警、ELK做日志分析。
迁移过程中遇到最大的问题是环境差异——本地开发没问题,上了K8s之后各种报错。比如数据库连接失败、健康检查不通过、ConfigMap挂载错误等。
最终我们统一了开发/测试/生产环境的基础镜像版本,同时为每个服务编写了完整的Dockerfile和Chart文件,大大提升了部署效率。
第四步:迈向真正的云原生架构
2021年后,我们尝试引入Serverless和Service Mesh来提升系统的弹性和可观测性。例如:
- 使用阿里云Serverless函数计算处理图片上传后的缩略图生成,节省了不少闲置算力。
- 引入Istio作为服务网格,在不需要业务代码变动的前提下增强了服务间的链路追踪与限流熔断能力。
这一阶段我们不再仅仅关注代码层面的设计,而是更加注重整体系统架构的稳定性和自动化能力。
技术细节分享:一些值得记录的地方
数据库设计:如何避免跨库事务?
我们在拆服务初期遇到了一个很头疼的问题:不同服务之间需要共享数据。例如,订单服务需要访问商品价格信息,这时候如果每次都走远程调用,性能会很差。
我们采取了几种策略:
- 冗余字段:在订单表中保存当时的商品价格和标题,这样即使商品信息修改也不会影响历史订单。
- CQRS模式:部分读操作通过Event Bus将数据写入另一个读专用库,降低耦合。
- 最终一致性保障:使用RocketMQ异步更新各服务的数据副本,确保数据不会丢失。
这些做法虽然牺牲了一些实时性,但换来的是系统稳定性与可扩展性的大幅提升。
接口设计:不要只考虑GET/POST,要考虑幂等和失败重试
举个例子,我们曾经因为没有对接口加幂等控制,导致用户重复下单,给财务带来了很大麻烦。后来我们采用了以下方案:
- 所有写操作都带上唯一业务ID(比如订单编号)
- 使用Redis缓存请求指纹(userId+businessId),记录执行状态
- 对于外部回调接口,支持幂等处理机制(如支付宝通知)
另外,我们也在各个服务之间增加了自动重试机制,比如Feign默认3次重试、Ribbon设置超时阈值,这些都是提升系统鲁棒性的重要手段。
实施效果:架构演进带来的改变
经过几年的努力,整个后端系统的健壮性和伸缩性有了显著提升:
- 部署效率提升80%以上:Kubernetes配合CI/CD实现了秒级部署和滚动更新
- 故障隔离能力增强:一个服务出问题,不会波及全局系统
- 开发协同效率提高:每个小组专注自己的服务,测试覆盖率提升至75%+
- 运维成本下降:通过自动化监控报警,减少了大量人工介入
- 业务响应速度加快:新业务需求可以快速切分成独立服务上线
更重要的是,我们建立了一套完整的工程规范,包括代码风格、服务命名、日志格式、异常处理等,使得团队新人能够更快地上手。
经验总结:我想给你的几点建议
如果你也在经历类似的架构演进过程,以下是我踩过坑后总结的经验,希望能帮到你:
✅ 不要盲目追新,合适才是硬道理
我见过很多团队一味追求新技术,比如非要用Service Mesh却不懂Envoy原理,或者用Serverless却没理解冷启动代价。任何技术决策都必须基于当前业务痛点和技术储备情况。
✅ 拆服务不是终点,而是起点
很多人以为把代码拆成N个Jar包就算微服务了,但如果没有配套的服务治理、配置中心、链路追踪,本质上还是换个壳的单体应用。
建议你一开始就规划好服务间通信方式、异常处理机制、日志上报方式等内容,不然未来会吃大亏。
✅ 重视基础建设,否则后期反噬巨大
记得早年我们没有标准化的服务监控,出了问题只能靠日志肉眼找原因。后来我们引入了Prometheus + Grafana + SkyWalking,才算真正做到了“看得见”。
建议尽早接入APM工具、实现统一的日志中心、完善监控指标体系。
✅ 团队协同比技术本身更重要
技术和工具都是死的,关键在于人的协作是否顺畅。我们曾经因为沟通不到位,导致两个服务的接口定义完全不一致,上线当天才发现问题。
现在我们会用Swagger统一文档,用GitLab Issue跟踪变更,用Code Review保证质量。
尾声:架构演进,从来都不是一场“技术秀”
如今回望过去几年的架构演进过程,我愈发觉得:没有银弹,只有不断探索与试错。
从最初那个被双十一压垮的单体系统,到现在具备弹性扩缩容能力的云原生架构,每一步都不容易,但也正是这些“痛苦”让我们逐渐成长为一支更有战斗力的团队。
如果你正处在转型或重构的十字路口,不妨记住一句话:
架构的本质,是为了让系统更好地支撑业务发展。技术服务于业务,而不是相反。
愿你在技术的路上越走越远,少踩坑,多收获。如果你喜欢这篇文章,欢迎留言交流,我也很乐意听听你的故事。
📌本文作者:一位经历过架构演进全过程的Java工程师,现就职于某大型互联网公司,热爱后端开发、架构设计与性能优化。

评论 0