从单体到云原生:我的后端架构演进实战分享

林芳
2025-06-19 21:39
阅读 748

作为一名从业多年的后端开发工程师,我经历过多个项目的架构变迁。最早接触的是传统的单体架构,随着业务增长、系统复杂度的提升,逐渐开始尝试微服务、容器化,最后走向了云原生。这个过程不是一蹴而就的,中间有踩坑、有试错,也有顿悟和收获。

今天我想结合自己参与的一个真实项目——一个电商类平台的后端重构之路,来聊聊我们是如何一步步从单体走向云原生的。


初期背景:从单体起步的故事

初期背景:从单体起步的故事

负载均衡配置-2

2019年,我加入一家初创公司,负责电商平台的后端开发。当时整个系统是一个Spring Boot写的单体应用,代码量大概在30万行左右,数据库用的是MySQL,部署方式是直接扔在阿里云ECS上跑Tomcat。

一开始确实挺爽的:开发快、调试容易、上线简单。但随着产品功能越来越多,团队人数也从原来的5人扩展到了20多人,问题开始频繁出现了:

  • 发布困难:一个功能改错,整个系统都要停机重发布。
  • 性能瓶颈:订单中心和用户中心混在一个服务里,高峰期经常互相影响。
  • 技术耦合严重:修改一处逻辑,牵动全局;新同学上手慢。
  • 运维复杂:虽然只有一个包,但由于依赖太多、配置繁杂,出问题时定位非常费劲。

这些问题积累得多了,终于触发了一个“爆点”——某次大促活动当天,系统因并发太高出现雪崩式崩溃,最终导致几百万的订单流失。

我们意识到:必须重构!


架构升级第一步:拆分成微服务

架构升级第一步:拆分成微服务

2020年初,我们决定把原有的单体应用拆分为几个核心服务。当时我们的目标是:

  • 降低模块间的耦合性
  • 提高系统的可维护性和可扩展性
  • 支持独立部署和快速迭代

我们选择了Spring Cloud作为技术栈,引入了以下组件:

  • 服务注册与发现:Nacos(替代了早期的Eureka)
  • 服务通信:OpenFeign + Ribbon
  • 网关:Spring Cloud Gateway
  • 配置中心:同样用Nacos托管配置
  • 链路追踪:SkyWalking进行分布式追踪

关键决策:如何划分服务边界?

这其实是整个微服务改造中最难的一部分。我们没有一开始就完全按照“领域驱动设计”的方法去抽象,而是采取了一个更务实的策略:

先按业务线划分粗粒度服务,再逐步优化

比如将订单服务、用户服务、商品服务、库存服务先拆出去。这些服务之间通过RPC调用完成协作。同时我们也预留了一些“中台服务”,如权限中心、通知中心等。

数据库方面,每个服务都有自己的独立数据库实例,并且我们采用了分库分表方案处理订单数据的增长。

遇到的问题与教训

  1. 服务间调用过于频繁
    我们最初为了复用代码,允许服务A调用服务B的私有API,结果形成了一张复杂的依赖图。后来我们统一采用事件驱动模型(Event Sourcing),并通过RocketMQ做异步解耦。

  2. 本地事务失效
    拆成服务后,本地事务无法保证一致性。我们引入了Seata分布式事务框架,但实际使用中发现其性能损耗较大。最终改为采用最终一致性+重试补偿机制,并配合状态机来管理核心流程。

  3. 接口定义不清晰导致集成测试成本上升
    后期我们强制所有服务提供Swagger文档,并通过自动化接口校验工具保证兼容性。另外还建立了一个“联调沙箱环境”,模拟各服务之间的交互。


容器化与CI/CD:迈向现代运维的第一步

容器化与CI/CD:迈向现代运维的第一步

2021年开始,我们引入了Docker和Kubernetes,正式迈入容器化阶段。

我们当时的痛点包括:

  • 发布版本混乱:每次手动打tag,容易出错
  • 环境差异大:开发、测试、生产三套环境配置不同,排查问题很痛苦
  • 资源利用率低:某些服务占用大量CPU内存,其他服务却被冷落

于是我们做了以下事情:

1. 将每个服务打包为Docker镜像

我们将每个服务的构建流程标准化,通过Jenkins Pipeline实现自动编译、打标签、推送到私有镜像仓库。

例如,一个典型的Pipeline步骤:

stages:
  - Build Jar
  - Build Docker Image
  - Push to Registry
  - Update Helm Chart Values
  - Deploy via ArgoCD or Helm CLI

2. 引入K8s进行调度与弹性伸缩

我们搭建了自己的K8s集群,服务以Deployment方式部署,使用Helm做版本管理,Ingress暴露对外访问入口。

刚开始我们对K8s的资源配额和QoS设置不够熟悉,出现过很多OOM Killer杀进程的情况。后来我们逐步完善了监控体系,引入Prometheus + Grafana,对Pod、Node级别进行了细粒度监控。

3. 统一日志与监控体系

每个Pod都挂载了Fluentd Sidecar,日志统一采集到ELK Stack中。对于关键服务的日志,我们会设置报警规则,比如错误日志突增、响应超时等。

这套体系搭建完成后,线上问题定位效率提升了50%以上。


进一步云原生化:拥抱Serverless & 服务网格

服务器部署方案-1

到了2022年,我们开始探索更“云原生”的玩法。因为我们已经迁移到阿里云上,所以也开始逐步利用云厂商提供的高级能力。

1. 使用KEDA实现弹性伸缩

有些服务是周期性爆发型流量,比如促销期间的优惠券服务。我们尝试用KEDA来基于消息队列积压数自动扩容,节省了不少资源。

2. 服务网格初步尝试

我们小范围试点了Istio,用来做精细化的流量治理,比如:

  • 蓝绿发布
  • 灰度切换
  • 请求熔断限流

但Istio的学习曲线确实比较陡峭,需要一定的投入成本。如果是中小型团队,建议先从Linkerd入手,更轻量级一些。

3. Serverless的试水

我们在一些定时任务和后台计算任务中引入了阿里云函数计算FC,特别是图片压缩、PDF生成这类场景,效果还不错:无需长期运行服务器,按需付费,运维压力小了很多。

不过也要注意函数执行时间限制、网络隔离等问题,在使用前评估好是否适合当前场景。


结果与收益总结

到现在为止,整个系统的架构已经经历了三代演进:

  • 第一代:单体应用,适合初期快速验证
  • 第二代:微服务 + 容器化,支持中大规模业务
  • 第三代:云原生 + 弹性调度,适应多变的业务需求和降本增效的需求

带来的变化非常明显:

维度 单体时代 微服务时代 云原生时代
发布频率 每周一次 每天多次 每小时多次(特定服务)
故障影响范围 全站瘫痪 局部不可用 仅限某个子服务
资源利用率 常驻浪费 动态伸缩 精准匹配负载
运维复杂度 自动化为主

更重要的是,这种架构让团队具备了快速响应业务变化的能力,也为后续AI服务能力接入打下了基础。


我的经验分享给正在转型的你

如果你也在考虑或正在进行类似的架构升级,我可以分享几点体会:

1. 不要追求一步到位,要根据业务节奏渐进演化

我们是从单体→微服务→容器化→云原生,每一步都不是拍脑袋决定的,而是因为遇到了具体的问题才推动的。架构服务于业务,脱离现实谈理想是危险的。

2. 技术债永远存在,关键是及时偿还

微服务拆分之后,你会发现“服务怎么越拆越多”。没错,这就是真实情况。所以一定要配套做好服务治理、接口管理和可观测性建设,否则后期会陷入维护地狱。

3. 重视运维体系建设比选择技术栈更重要

很多时候我们花太多时间纠结于选哪个注册中心、哪个服务网格方案,不如先建立起一套完整的CI/CD管道、日志收集体系和报警机制。

4. 团队文化和技术氛围一样重要

微服务和云原生意味着更高的协同成本,只有团队成员理解服务自治、接口契约、异常容忍这些理念,整个系统才能真正“活起来”。


写在最后

技术这条路走得越久,越觉得它不只是关于工具和代码的堆砌。从单体走向云原生,我们走过了坑、绕过弯,也看到了风景。希望这篇文章能给你一点启发,少走一点弯路。

如果你也在经历类似的架构升级,欢迎留言交流,我们可以一起探讨更好的实践方式。毕竟,真正的经验,都是摔出来的。💪


作者简介:一线互联网公司后端架构师,多年大型Java系统设计与落地经验,热爱写代码、聊架构、带团队。目前致力于云原生与智能服务融合方向的研究。

评论 0

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