后端架构演进:从单体到云原生

移动端App
2025-06-27 10:29
阅读 748

后端架构演进:从单体到云原生的实战之路


还记得2016年刚入职那会儿,我参与开发的第一个项目是基于Spring Boot搭建的一个商城系统。整个项目采用的是传统的单体架构:前端Vue + 后端Java + MySQL + Redis的一套组合拳。那时候我们用Maven打包部署,把代码丢到Tomcat跑起来就算完成了上线。

现在回过头来看,那时候的架构在业务初期确实很合适——部署简单、团队上手快、运维成本低。可随着业务增长,用户量从几千到几万再到几十万,问题也一个接一个冒出来。

这篇文章,我想以亲身经历的几个项目为线索,和大家聊聊我在后端架构演进过程中的一些真实体会和踩过的坑。


背景与挑战:单体架构撑不住了

背景与挑战:单体架构撑不住了

我印象最深的一次“事故”发生在一次促销活动期间。那天早上9点,我们按照计划上线了一个限时折扣的营销活动。结果不到1小时,整个平台几乎瘫痪了。

通过日志分析发现,大量请求集中在订单创建接口,导致线程池爆满,数据库连接数暴涨,进而影响到了商品浏览、库存查询等其他核心功能。更糟的是,由于所有服务都部署在一起,只要一处出错,整个应用就可能挂掉。

问题总结:

  • 性能瓶颈明显:所有模块运行在一个JVM进程里,容易出现某个模块异常拖垮整个系统。
  • 代码耦合度高:随着业务迭代,代码结构越来越复杂,新人难以理解整个系统。
  • 扩容困难:无法按模块进行独立扩缩容,只能整体扩容,成本高。
  • 发布风险大:一个小功能更新也要重启整个服务,稍有不慎就会引发故障。

那时候我们就在想:有没有一种方式,既能解决这些痛点,又能保持开发效率?后来,微服务架构成了我们的答案。


微服务架构的尝试:拆分开始

微服务架构的尝试:拆分开始

我们在2018年初启动了第一次架构拆分。当时团队对微服务的理解还不是很深入,只是想着先把用户、订单、商品这三大核心模块独立出来。

我们使用了Spring Cloud Alibaba + Nacos + Feign + Sentinel这套技术栈。拆分的方式也比较直接:先将原来的代码按照业务边界拆成多个Spring Boot工程,各自拥有独立的数据库、独立的服务入口,然后通过Feign调用彼此接口。

初期成果

  • 每个服务独立部署,出现问题的影响范围小了很多。
  • 可以根据业务需要单独做扩容,比如促销期间重点扩订单服务。
  • 不同团队负责不同模块,协作更清晰。

但很快我们也发现了几个大坑:


踩坑经验一:服务治理没跟上,雪崩效应来了

踩坑经验一:服务治理没跟上,雪崩效应来了

某次上线完一个新的会员功能之后,用户中心服务突然CPU飙升,响应变慢,进而导致依赖它的订单中心也出现了延迟。因为订单中心处理超时,又反过来压垮了支付中心。最终演变成了一个典型的**级联失败(Cascading Failure)**问题。

我们当时的应对方案是引入Sentinel来做服务限流和熔断,并配置了一些兜底策略。虽然临时解决了问题,但也让我们深刻意识到:微服务不仅仅是拆分,更要有一整套服务治理体系来保障系统的稳定性


踩坑经验二:数据一致性难搞

订单服务要修改库存,之前是一个事务搞定的事。现在拆成两个服务,怎么保证原子性?

一开始我们用了简单的本地事务+异步补偿机制。例如下单成功后发一条MQ消息,通知库存服务减库存,如果消费失败就重试三次。这种做法在测试环境中看起来没问题,但在并发场景下经常会出现数据不一致的情况。

后来我们引入了Seata分布式事务框架。通过全局事务控制,在业务逻辑中加入分支事务,让扣款和库存操作形成事务组。不过也带来了新的问题:性能下降了不少,而且一旦Seata Server宕机,整个链路都会阻塞。

这个阶段我们得出的经验是:

在微服务架构中,强一致性代价很大。如果不是金融级场景,可以考虑最终一致性 + 补偿机制的方式来实现。


再进一步:走向容器化 & 云原生

到2020年,我们的微服务数量已经增加到20+,各个服务之间调用关系错综复杂。这时候我们意识到:光靠人工维护部署流程、配置管理已经越来越吃力。

于是我们决定拥抱Kubernetes + Docker,全面容器化。

上云过程中的关键步骤:

  • 所有服务打成Docker镜像,推送到私有仓库;
  • 使用Helm Chart统一管理部署模板;
  • 引入Prometheus + Grafana做监控;
  • 配置集中管理用到了ConfigMap + Secret;
  • 日志统一收集到ELK体系中。

刚开始迁移到K8s的时候也是各种翻车:Pod频繁重启、网络不通、配置加载失败……但熬过了最初的磨合期后,收益也开始显现:

  • 环境一致性大幅提升:一套YAML文件搞定部署;
  • 自动化运维能力增强:自动扩缩容、滚动更新、健康检查等特性非常实用;
  • 系统可观测性提升:有了Metrics、日志和追踪,排查问题更轻松。

实战代码片段分享:一个服务的容器化部署配置

以订单服务为例,这是我们用于部署的deployment.yaml简化版本:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
        - name: order-service
          image: registry.company.com/order-service:latest
          ports:
            - containerPort: 8080
          envFrom:
            - configMapRef:
                name: order-config
          resources:
            limits:
              memory: "2Gi"
              cpu: "500m"

配合Service对外暴露访问入口:

apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector:
    app: order-service
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

再配合HPA(Horizontal Pod Autoscaler)做弹性伸缩:

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: order-service
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: order-service
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70

性能优化与数据库设计心得

除了架构层面的调整,我们在数据库方面也做了不少优化:

分库分表的尝试

我们早期使用的是MySQL主从模式,后来随着数据量增大,单表记录突破千万,查询速度明显变慢。

我们采用的是垂直分库 + 水平分表结合的方式:

  • 将用户相关的数据拆到user_db,订单拆到order_db;
  • 订单表按时间做水平切分,分为order_2021、order_2022、order_2023;
  • 查询层加了一层Sharding中间件(我们选择了MyCat)。

虽然中间遇到了很多适配问题,比如排序、分页、跨库JOIN等,但我们最终还是平稳过渡了过来。

缓存设计原则

我们采用的是Redis缓存热点数据,遵循以下原则:

  • 写穿透问题:使用空值缓存避免频繁查询落库;
  • 缓存雪崩:给不同的Key设置随机过期时间;
  • 缓存击穿:热门Key采用互斥锁更新机制;
  • 缓存降级:在Redis失效时,允许降级查询数据库,而不是直接报错。

技术趋势:Serverless 和 Service Mesh 的探索

去年我们开始接触Service Mesh,用Istio重构了部分服务间的通信治理。相比传统基于SDK的注册发现机制,Istio的Sidecar代理模型极大减少了业务代码侵入性。

我们也试水了AWS Lambda做一些轻量级任务处理,比如短信发送、图片压缩、日志清洗等。虽然目前还没完全推广,但我相信未来会有越来越多的任务往Serverless方向迁移。


架构演进的效果总结

从最初的单体架构,到现在全面拥抱云原生,这条路走了好几年。回顾一下带来的变化:

维度 单体时代 微服务时代 云原生时代
部署方式 Tomcat部署 多节点部署 容器编排部署
发布效率 手动/半自动 基本自动化 全流程CI/CD
故障隔离 影响全系统 限制在模块内 增加限流熔断机制
运维成本 高 → 稳定后中
开发效率 快速起步 因复杂度降低 工具完善后回升

最重要的是,我们现在能够快速响应业务需求,支撑更高的并发量。以前一场秒杀活动我们需要提前一周准备,现在半天就能完成扩容,从容应对高峰流量。


我的几点建议:送给还在路上的你

  1. 不要盲目追求新技术,适合自己的才是最好的
    微服务不是银弹,如果你的业务量不大、团队规模有限,单体未必不是更好的选择。

  2. 服务拆分要有章法,不能随便切
    拆分的边界必须清晰,建议以业务域为准,比如用户、订单、商品这些天然边界。

  3. 尽早引入监控和日志体系
    不管你现在用什么架构,监控和日志都是刚需。否则出了问题连日志都找不到,简直是地狱。

  4. 重视文档和技术沉淀
    很多时候新同事不知道该怎么查一个接口的调用链路,就是因为缺乏服务间的文档说明。

  5. 技术债要早点还
    比如我们曾经为了快速上线跳过了一些规范,比如接口版本管理、日志级别约定、异常统一处理等等。后来每改一处都要提心吊胆。

  6. 别忘了DevOps文化的落地
    没有高效的交付流程,再好的架构也可能变成沉重的技术负担。CI/CD、环境隔离、自动化测试缺一不可。


结语

这几年走下来,我最大的感触是:架构是为了解决业务问题而存在的。没有绝对完美的架构,只有不断演进的解决方案。

从最初那个小小的商城系统,到现在支撑百万级用户的分布式系统,每一次升级都不是一蹴而就的,背后都有无数个调试日志、深夜复盘会议和上线前的心惊胆战。

如果你正在面临架构选型或者转型的困惑,不妨先从最小可行方案做起,逐步演进。记住一句话:

“系统复杂了不要怕,怕的是你不去拆解它。”

愿你在技术的路上越走越远,少踩坑,多收获 😊


如果这篇实战分享对你有帮助,欢迎点赞或留言交流,我会继续输出更多一线开发者的思考与成长记录。

评论 0

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