后端架构演进:从单体到云原生 —— 一次真实踩坑与重构的旅程
引言

刚入行那会儿,我参与了一个内部系统开发项目。当时我们团队规模不大,产品需求也相对简单,于是采用了最熟悉的“单体架构”搭了个Java Spring Boot应用,配上MySQL做数据持久化。一切看起来都很顺利,直到系统上线后开始出现各种问题。
随着业务的增长,这个最初跑得还挺溜的服务逐渐暴露出一堆“中年危机”。比如响应变慢、部署复杂、维护困难,甚至在高峰期还经常挂掉。后来我们决定做一次彻底的技术重构,把整个后端从单体架构一步步拆解、改造成了现在的云原生架构。
这篇文章就来聊聊我们是怎么走过这段“阵痛期”,又从中踩出了哪些坑,以及最终获得的收益。
第一阶段:单体架构的甜蜜与苦涩

项目背景
项目是一个面向企业内部使用的OA(办公自动化)系统,包括审批流程、考勤打卡、文档管理等模块。一开始需求不复杂,团队规模小,大家一致决定采用Spring Boot搭建一个单一应用,所有代码都放在一个Maven工程里,数据库用的MySQL。
听起来没问题对吧?确实,在初期一切都运行良好。但好景不长……
遇到的问题
部署复杂
每次修改一个小功能都要打包重启整个服务,稍有不慎就会引入新Bug,影响其他模块。性能瓶颈
所有接口都在同一个JVM里面跑,某个模块处理慢了,整个应用都会受到影响。尤其是一些报表类请求特别耗资源,直接拖垮整台机器。扩展性差
新增模块时代码耦合严重,改动牵一发动全身。比如加个权限控制逻辑,可能需要修改多个地方。运维成本高
线上环境一旦出问题,排查起来特别麻烦。日志多得像大海捞针,监控也没统一。技术栈难以升级
升级Spring Boot版本或者换个ORM框架都必须全局替换,风险极高。
这些痛点让我们意识到不能再这么搞下去了——我们必须重构架构,寻找更适合当前发展阶段的技术方案。
第二阶段:迈向微服务架构的第一步
技术选型与思路
我们决定先从小处着手,尝试将原来的单体应用拆分成几个独立的服务模块:
- 用户中心(User)
- 审批服务(Approval)
- 考勤服务(Attendance)
- 文档中心(Document)
每个服务之间通过HTTP API调用通信,使用Spring Cloud提供的Eureka做服务注册与发现,Feign做远程调用,Ribbon做负载均衡。
同时,我们也引入了Spring Cloud Config做配置管理,Zuul作为网关进行统一鉴权和路由。
实践中的挑战
服务划分不合理 最初我们只是机械地按照模块来拆分,没考虑清楚服务的边界在哪里。结果某些接口频繁跨服务调用,导致响应延迟飙升,反而比原来的单体还要慢。
小插曲:有一次上线审批服务新版本,由于依赖文档服务接口未更新,直接导致审批列表加载失败,用户集体投诉……
分布式带来的复杂度 数据一致性变成了难题,两个服务之间的事务怎么保证?后来我们上了Seata做分布式事务管理,但这套方案在生产环境调试起来并不轻松,一度让我们怀疑人生。
监控和链路追踪缺失 微服务多了之后,日志分布在不同的服务器上,查问题的时候就像玩拼图一样痛苦。后来我们接入了SkyWalking做了链路追踪,效果还不错,但前期集成和配置也花了不小功夫。
初步成果
虽然过程中踩了不少坑,但还是收获了很多经验:
- 服务粒度要合理,避免过度拆分
- 接口设计要考虑幂等性和兼容性
- 日志收集 + 链路追踪是微服务的基础设施
- 分布式事务不是万能钥匙,慎用!
第三阶段:拥抱云原生,构建弹性基础设施
转折点:一次线上事故
某天晚上,我们的其中一个服务因内存泄露导致Pod不断重启,但由于Kubernetes默认的自动恢复机制设置得过于宽松,未能及时扩容,最终整个系统几乎陷入瘫痪。
这次事故让我们意识到,仅仅拆成微服务还不够,真正的稳定性和扩展性来自于基础设施的现代化。
于是我们开始向云原生全面转型。
我们做了什么?
容器化 & Kubernetes 编排 使用Docker封装各个微服务,并部署到阿里云ACK集群上。每个服务都以Deployment+HPA方式运行,具备自动扩缩容能力。
服务网格初步探索 引入Istio管理服务间通信,做流量控制、熔断限流和灰度发布。虽然 Istio 的学习曲线陡峭,但它的确让我们的运维变得更加灵活可控。
统一配置中心 & Secret 管理 改用ConfigMap和Secret管理敏感信息,结合Vault做密钥存储,避免硬编码密码。
CI/CD 流水线搭建 使用GitLab CI配合K8s Job构建持续集成流程,实现自动构建镜像并推送到私有仓库,再由ArgoCD做自动部署。
监控告警体系升级 Prometheus抓取各服务指标,Grafana展示大盘,AlertManager发送预警。再加上之前的SkyWalking链路追踪,排查问题效率提升了不止一个档次。
成果展示
- 弹性伸缩:节假日高峰时服务自动扩容,平时自动收缩,节省了不少计算资源。
- 故障隔离增强:某个服务宕机不会导致整体雪崩,熔断机制可以及时切断故障传播。
- 上线效率提升:从提交代码到上线不到十分钟,极大提升了交付速度。
- 运维更轻松:有了完善的监控和告警系统,再也不怕半夜被叫醒了 😅
第四阶段:优化与沉淀 —— 架构设计与细节打磨
到了这个阶段,我们开始关注一些底层架构的优化和长期维护问题。
数据库设计调整
起初我们为每个微服务配了一个独立的MySQL数据库,初衷是为了隔离。但实际中发现很多聚合查询或报表生成非常不方便。
后来我们做了如下调整:
- 读写分离:针对读多写少的服务,增加只读副本,使用ShardingSphere做路由。
- 数据聚合层建设:新增一个BI模块,定时同步关键数据到ClickHouse,用于复杂报表分析。
- 数据库迁移工具:使用Liquibase管理Schema变更,确保每次发版都能安全升级结构。
- 分表策略完善:用户行为日志类数据采用按月分表策略,避免大表扫描造成性能下降。
接口设计规范
为了减少服务间的协作成本,我们制定了一套内部API规范:
- 统一错误码格式:定义全局的错误码规范,便于客户端统一处理
- OpenAPI + Swagger:所有接口自动生成文档,并提供Mock测试
- 版本控制机制:使用URL路径带版本号
/api/v1/user,支持平滑过渡旧接口 - 异步回调机制:对于耗时操作,返回任务ID并支持回调通知,提升用户体验
生产环境运维经验
- 金丝雀发布:优先将新版本发布给少量用户,确认无误后再全量推广
- 日志标准化:统一采用JSON格式输出日志,并打上上下文Trace ID,方便ELK检索
- 健康检查机制:K8s探针定期检查存活状态,异常节点快速剔除
- 限流降级:使用Sentinel在网关层和业务层做熔断保护,防止突发流量压垮系统
总结与建议
回望这几年走过的架构演进之路,真的是一边踩坑一边成长的过程。总结下来有几点我想送给正在经历类似阶段的同学:
1. 架构没有银弹,适合自己的才是最好的
不要盲目追求“高大上”的技术名词,要根据团队能力和业务阶段选择合适的技术栈。有时候,简单的方案反而是最优解。
2. 提前设计好服务边界
服务拆分不是越细越好,一定要从业务模型出发,明确每个服务的职责范围。否则只会带来更多的通信成本和维护负担。
3. 基础设施先行
微服务落地的前提是有良好的监控、日志、配置、流水线等基础设施保障,不然你会被各种“看不见摸不着”的问题折磨疯。
4. 小步快跑,逐步演进
不要想着一蹴而就就把整个系统重构一遍。可以先从一个核心模块入手,验证可行性后逐步扩大范围。
5. 多做自动化,别让运维手工操作
自动化部署、自动化测试、自动化监控,越早建立这套机制,后面就越省心。
结语
现在再看当年那个小小的Spring Boot项目,已经变成了覆盖十几个微服务、部署在云平台上的成熟系统。这中间经历了无数次踩坑、修复、重试,也留下了许多深夜加班的回忆。
但我始终坚信一点:好的架构不是设计出来的,是不断迭代试错出来的。
希望这篇基于我真实项目经历的分享,能对你们有所启发。如果你也在经历架构演进的迷茫期,欢迎留言交流,我们一起踩坑、一起成长 💪
By the way,下次如果让我再选一次,我会一开始就用云原生起步,而不是从单体慢慢拆……真的太折腾了 😂

评论 0