从单体到云原生:一段后端架构演进的实战记录

Spring打工人
2025-06-26 15:27
阅读 656

背景介绍:我们的起点,是那台“老朋友”

背景介绍:我们的起点,是那台“老朋友”

我第一次接触后端架构演进,是在一家做企业服务的公司。那会儿项目刚启动不久,技术选型还比较保守,用的是 Java + Spring MVC + MySQL 的经典组合。整个系统就是一个庞大的单体应用,代码全部放在一个工程里,数据库也只有一主一从。那时候我们觉得这种架构已经足够满足需求了——毕竟业务初期数据量小、用户也不多。

但随着客户数量的增加,以及功能模块越来越多,这台“老朋友”开始有点撑不住了。部署一次要十几分钟,测试环境和生产环境常常打架,团队之间协作也越来越困难。我记得有一次上线前夜,开发同事改了一个权限校验的逻辑,结果影响到了订单流程,导致灰度发布的节点直接崩溃。

这些“小事”其实都是背后更深层的问题在发酵:单体架构的复杂性正在失控,我们需要做出改变


挑战来了:系统开始“喘不过气”

挑战来了:系统开始“喘不过气”

1. 难以维护的代码结构

最明显的问题就是代码越来越难看懂。不同模块之间的依赖关系越来越复杂,改一个简单的 bug,常常需要动到十几个类文件,风险高不说,测试也变得非常繁琐。

比如,有一个权限管理的模块被订单、用户等多个模块引用,原本是一个工具包,后来因为需求不断扩展,它变成了包含数据库访问逻辑、业务判断、接口定义的大杂烩。每次改动都牵一发而动全身。

2. 高频率的发布冲突

我们采用的是集中式 SVN 管理(没错,那几年 SVN 还没完全淘汰),每次发布都要合并所有分支。由于没有严格的接口隔离机制,经常出现多人同时修改某个核心模块,最后上线时才发现功能互相干扰。

3. 部署和扩容瓶颈

随着用户增长,服务器压力也越来越大。我们尝试横向扩容,但由于单体应用状态耦合严重,加机器并不能解决根本问题。有些请求处理慢,整个应用就卡住了。特别是夜间跑报表的时候,前端响应延迟能有几秒钟。

4. 开发效率低下

新功能迭代周期越来越长。产品经理的需求一来,我们往往需要先梳理这个功能会影响哪些旧逻辑,再评估是否需要重构。有时候,为了赶进度,只能“硬着头皮上”,埋下更多隐患。


转折点:我们决定拆分单体

转折点:我们决定拆分单体

真正促使我们迈出第一步的,是一次严重的事故:某次线上发布,一个配置项改错了,导致整个系统无法登录,持续了将近 2 小时。那次之后,老板终于点头同意我们进行架构改造。

我们选择的道路是微服务化 + 容器化 + DevOps 工具链升级。听起来很标准,但在实际操作中,每一步都充满了细节和技术挑战。


实施过程:从0到1搭建微服务架构

第一步:服务拆分设计

我们并没有一开始就大规模拆分,而是选了一个非核心模块作为试点——通知系统。

通知系统原本嵌在用户中心里面,负责发送邮件、短信、站内消息。它的特点是业务相对独立、对其他系统的依赖少,并且可以异步处理。非常适合练手。

我们做了几个关键决策:

  • 按业务边界划分服务:不再是按技术层次(controller, service, dao)来分,而是从业务角度出发。
  • 使用 API Gateway 做统一入口:引入 Zuul 作为网关,实现路由、鉴权、限流等功能。
  • 服务通信走 REST + JSON:虽然性能不如 RPC,但调试方便,适合初期快速验证。

拆完之后,通知服务被单独部署,日志、监控也可以独立分析,不再受其他服务的影响。

第二步:数据库拆分策略

服务拆了之后,数据库成了新的问题。我们最初想用共享数据库,但很快发现这样还是容易产生耦合和锁竞争。

于是采用了每个服务拥有自己的数据库实例,并通过事件驱动的方式同步数据。比如订单服务发生变更,通过 Kafka 发布一条事件,库存服务监听并更新自己的本地状态。

当然,这也带来了新的问题:如何保证最终一致性?我们做了两件事:

  1. 对关键数据写入时加入幂等校验。
  2. 提供补偿机制,比如定时扫描不一致的数据进行修复。

这在我们后续接入审计日志、风控系统时发挥了重要作用。

第三步:容器化与 CI/CD 流水线构建

有了微服务之后,部署变得频繁而复杂。这时候我们引入了 Docker 和 Kubernetes,并基于 Jenkins 构建了一套自动化流水线:

  • 开发人员提交代码后自动触发单元测试;
  • 测试通过后构建镜像,打上版本标签;
  • 自动推送到私有镜像仓库;
  • 由 Kubernetes 控制滚动更新。

这套流程一开始并不顺利。我们遇到过很多问题,比如:

  • 镜像体积过大,拉取时间长;
  • 不同环境的配置差异大,导致上线失败;
  • Jenkins Job 配置混乱,维护困难。

后来我们做了优化:

  • 使用 Multi-stage Build 减小镜像体积;
  • 抽象出通用部署模板,通过 Helm 配置参数;
  • 引入 GitOps 思想,把 Kubernetes 配置纳入 Git 管控。

现在每次上线只需要一个 git push,系统就会完成构建、测试、部署全流程,效率提升显著。


效果总结:不只是技术上的胜利

这次架构升级带来的收益远超预期,不仅仅是性能上的提升,还有组织层面的变化。

技术方面:

  • 响应能力提升:平均请求响应时间从原来的 800ms 下降到 350ms;
  • 运维效率提升:借助 Prometheus + Grafana,我们可以实时查看服务状态;
  • 故障隔离增强:某个服务出错不会影响其他服务,减少了雪崩效应;
  • 弹性扩容实现:Kubernetes 支持根据负载自动扩缩容,高峰期也能轻松应对。

组织方面:

  • 开发协作更顺畅:各小组专注于自己的服务,减少合并冲突;
  • 迭代速度加快:新需求可以在不影响主干的前提下并行推进;
  • 新人上手更容易:每个服务职责清晰,文档完善,学习成本降低。

更难得的是,我们在后期对接 SaaS 化平台时也更加灵活。很多服务可以直接复用到其他客户环境,大大减少了重复工作。


经验分享:给准备转型的同学几点建议

1. 不要盲目追求新技术

我见过一些团队上来就说“我要搞微服务 + Istio + Service Mesh”,结果连基本的服务拆分都没做好。我的建议是:

先解决服务边界、数据一致性、部署流程等问题,再考虑高级特性。

2. 服务拆分不是越细越好

很多人觉得微服务拆得越细越好,其实不然。服务粒度过细,反而会带来更多的沟通成本和服务治理难题。

我的经验是:一个服务应该对应一个可交付的业务功能,而不是一个类或方法。

3. 技术债必须提前规划

你在架构调整过程中可能会做很多临时妥协,比如绕开事务、忽略监控等。这些都可以接受,但一定要记得:

每一个“临时方案”都要有替代计划,否则将成为长期的技术负担。

4. 工具链要跟上节奏

微服务不是光靠 Spring Cloud 和 Nacos 就能搞定的。你需要一整套配套:

  • 日志聚合(ELK)
  • 分布式追踪(SkyWalking / Zipkin)
  • 服务注册与发现(Nacos / Consul)
  • 监控告警(Prometheus + Alertmanager)

如果你没有完整的工具体系,微服务只会让你更痛苦。

5. 文化比技术更重要

最后我想说的是,技术变革的背后,其实是团队文化的转变。

如果你只是换个架构而不改变协作方式,那本质上只是换了件外衣。


结语:技术的路,走得稳才走得远

回顾这段旅程,我最大的感受是:后端架构的演化,从来都不是一个“换框架”的动作,而是一个持续改进的过程

我们从最初的单体架构一路走到今天的云原生环境,经历了无数次的试错、回滚和反思。每一次踩坑,都是一次成长的机会。

如今,云原生已经成为主流趋势,Service Mesh、Serverless、Event Driven Architecture 等概念层出不穷。但我始终相信,架构的核心是为业务服务,是为团队赋能。

希望这篇文章能给正在面临类似困境的朋友一些启发,也欢迎大家留言交流你们的实践经验和困惑。我们一起在这个变化的时代里,走得更远,看得更远。


📦 附录:部分技术栈参考(截至本文撰写时)

  • 微服务框架:Spring Boot + Spring Cloud Alibaba
  • 注册中心:Nacos
  • 网关:Zuul
  • 配置中心:Apollo
  • 数据库中间件:MyCat(后期过渡到 ShardingSphere)
  • 容器编排:Kubernetes(自建+阿里云ACK混合)
  • 监控体系:Prometheus + Grafana + AlertManager
  • 日志体系:ELK + Filebeat
  • 发布流程:Jenkins + Helm + GitOps(ArgoCD)

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝