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

孙平_云计算
2025-06-16 11:38
阅读 377

引言:为什么我要写这篇技术分享?

引言:为什么我要写这篇技术分享?

我在后端开发领域干了五年,亲历过从传统单体应用一路折腾到云原生架构的全过程。说实话,这中间踩过的坑、熬过的夜、调试过的代码、和运维杠过的锅,足够出一本书。

今天想结合自己参与的几个重要项目,聊聊这些年我经历过的后端架构演进过程:从单体架构 → 模块化拆分 → 微服务化 → 云原生改造。这是一条充满挑战的技术升级之路,但每一步都让我对架构设计有了更深的理解。

希望这篇文章能给你带来一些实用的参考,也顺便回忆一下那些熬夜改代码的往事。


背景介绍:我们的起点是个“四不像”

背景介绍:我们的起点是个“四不像”

2019年的时候,我在一家中型电商平台做后端开发。当时的项目结构很典型:Spring Boot + MyBatis 构建的一个单体后端服务,运行在两台物理服务器上,数据库是MySQL主从读写分离,前端用Vue,接口通过Nginx转发。

项目背景听起来还行,但实际跑起来问题不少:

  • 应用越来越臃肿,动不动就是几千个类文件
  • 发布频率低,每次上线都要停机一两个小时
  • 高并发时经常扛不住,稍微一促销,服务就挂
  • 不同模块之间耦合严重,比如订单模块频繁改动商品库存逻辑
  • 开发协作困难,一个需求可能牵扯三四个人,沟通成本极高

我们那时候最头痛的就是“改一个小功能要改十个地方”。更头疼的是,一旦出现生产故障,排查半天发现其实是某个看似无关的功能引发的问题。

那个时候我就意识到:系统不能再这样下去了,必须重构。


第一次转型:模块化拆分

第一次转型:模块化拆分

挑战描述

随着业务增长,系统的瓶颈逐渐暴露出来。尤其是在大促期间,整个服务经常出现超时甚至崩溃的情况。而且新业务需求越来越多,不同团队之间的开发节奏无法协调。

我们当时最大的问题是单一代码库+高耦合模块=维护地狱

解决思路

为了解决这个问题,我们决定先尝试进行模块化拆分。目标是:

  • 将核心业务模块解耦(用户、商品、订单、支付)
  • 每个模块独立打包、独立部署
  • 保持共用的底层服务(如日志、缓存、工具类)共享

当时我们用的是 Spring Boot,所以模块划分主要围绕 Maven 的 module 结构来展开:

├── backend/
│   ├── user-service/
│   ├── product-service/
│   ├── order-service/
│   ├── common-utils/
│   └── pom.xml

每个 service module 对应一个子项目,common-utils 是公共依赖。虽然本质上还是跑在一个 JVM 里,但模块化的结构让代码变得清晰了不少。

接口调用方面,我们用了本地方法调用,也就是模块之间直接 import 包,没有引入远程通信。

收益与反思

这一阶段的收益主要是:

  • 代码结构变清晰了,团队合作效率提升
  • 部署依旧是一个 jar 文件,但内部职责划分明确
  • 日常开发更可控,不至于动不动就牵一发动全身

但也存在明显的局限性:

  • 并发压力依然集中在一台服务上
  • 某个模块出了问题,整体服务还是会受影响
  • 扩展性依旧不够,无法动态伸缩资源

这时候我开始意识到:模块化只是第一步,真正的挑战还在后面。


第二次架构迭代:迈入微服务时代

第二次架构迭代:迈入微服务时代

新问题的出现

随着业务量的增长,尤其是双十一和年终促销的到来,系统再次暴露了严重的性能问题。特别是在订单高峰期,整个服务频频出现 timeout 和 Connection Refused,用户投诉不断。

我们发现:

  • 单实例处理能力到了瓶颈,扩容受限
  • 订单服务和库存服务频繁争抢资源,导致响应延迟陡增
  • 数据库连接池打满,慢查询频发

显然,光靠模块化已经不够用了。

微服务化改造

于是我们在 2020 年启动了微服务改造计划。这次选型采用 Spring Cloud Alibaba + Dubbo 作为服务框架。

技术方案简述:

  • 拆分为多个独立服务(user, product, order, inventory, payment等)
  • 使用 Nacos 做注册中心和服务发现
  • 服务间通信采用 Dubbo RPC
  • 网关统一接入,使用 Spring Cloud Gateway
  • 统一日志管理、链路追踪(SkyWalking)、配置中心(Nacos)
  • 数据库按业务垂直拆分,引入分库分表策略(ShardingSphere)

服务拆分逻辑示例:

// 用户服务接口定义
@DubboService
public class UserServiceImpl implements UserService {
    // ...
}

// 订单服务调用用户服务
@Reference
private UserService userService;

数据库拆分:

原单库 拆分后
mysql_single_db mysql_user_db / mysql_product_db / mysql_order_db

这种结构虽然复杂了一些,但在应对业务增长时确实有效缓解了压力。

实施过程中遇到的挑战

  • 数据一致性难题
    原来事务控制在同一个数据库中就可以搞定,现在服务分散,跨服务的数据操作变得复杂。我们引入了 TCC 模式来实现最终一致性。

  • 服务雪崩问题
    有一次因为支付服务出错,导致所有调用方都被阻塞,整个系统连锁崩溃。我们后来加上了熔断降级机制(Sentinel),并在网关层做了限流。

  • 配置管理混乱
    初期每个服务都有自己的 application.yml,非常难维护。后来我们统一迁移到 Nacos 配置中心,实现集中管理和动态刷新。

成果与体会

这次改造后,系统稳定性显著提升,QPS 提升了近3倍,线上故障频率降低了70%以上。虽然初期投入比较大,但从长远来看是值得的。

这个时候我也算是真正理解了那句话:“微服务不是银弹,但它是解决复杂业务的有效方式。”


第三次飞跃:拥抱云原生

又一次瓶颈到来

到了2021年底,随着业务进一步扩张,我们的基础设施又面临新问题:

  • 服务器数量越来越多,人工运维压力巨大
  • 环境不一致导致上线经常“在我机器上能跑”
  • 自动化程度低,CI/CD 流程卡顿严重
  • 成本越来越高,服务器闲置浪费严重

特别是有一次搞秒杀活动,预估流量暴涨,手动扩了十几台 ECS,结果活动结束后又得慢慢缩容,非常麻烦。

上云 & 容器化改造

我们决定全面向云原生转型,目标包括:

  • 基于 Kubernetes 构建容器编排平台
  • 所有服务 Docker 化打包部署
  • 建立完整的 CI/CD 流水线(Jenkins + Harbor)
  • 日志统一(ELK)、监控告警(Prometheus + Grafana)
  • 服务网格初步探索(Istio)

Docker 镜像构建样例:

FROM openjdk:8u292-jdk-alpine
COPY *.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

Kubernetes 配置片段:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
      - name: user-service
        image: harbor.example.com/backend/user-service:latest
        ports:
        - containerPort: 8080

CI/CD流水线流程图:

负载均衡配置-2

Git Push → Jenkins Build → Docker Build → Push to Harbor → Kubernetes Apply

整个流程自动化后,上线时间从原来的一小时缩短到5分钟以内,且版本回滚也非常简单。

遇到的实际问题及解决方案

  • 环境差异问题
    之前经常出现测试环境没问题,生产环境各种报错。后来我们强制要求所有服务统一基镜像,并标准化运行时参数。

  • Pod 启动失败
    有的微服务刚启动就因为依赖服务还没就绪而失败,导致重启次数过多。后来我们加上健康检查探针:

    livenessProbe:
      httpGet:
        path: /actuator/health
        port: 8080
      initialDelaySeconds: 30
      periodSeconds: 10
    
  • 服务发现失败
    在迁移到 K8s 的 Service Mesh 架构时,出现了部分服务注册不到 Nacos 的情况。排查后发现是网络策略配置问题,K8s 内部服务访问不通。我们调整了网络插件(Calico)的规则后恢复正常。

收益总结

这次云原生改造让我们彻底摆脱了过去靠人肉运维的日子。弹性伸缩、自动发布、统一监控这些能力让我们在后续的大促活动中从容了很多。

同时,成本方面也有明显改善——不再需要常年保留大量空闲资源,而是按需拉起 Pod,节省了将近30%的预算。


经验总结:技术演进中的心得体会

微服务架构示意图-1

这几年下来,我总结了几点非常关键的心得,想送给正在做架构决策的朋友:

1. 别怕从简单的做起

很多同学一上来就想上微服务、上K8s,结果把自己绕进去。其实应该根据业务规模逐步演进,先把代码结构理清楚,再谈拆分,最后再考虑容器化和编排。

2. 不要盲目追求新技术

我见过太多团队为了“炫技”硬上 Istio、Service Mesh,结果连基础的日志都没收集好。任何架构都是为了解决现有问题,而不是制造新的麻烦。

3. 重视基础设施建设

技术选型不能只看编码爽不爽,还得考虑运维是否跟得上。比如你上了微服务,如果没监控、没日志聚合、没有链路追踪,那你就是在给自己挖坑。

4. 文档、规范、Code Review一样都不能少

越是复杂系统,越要注重规范化管理。否则新人接手会很痛苦,问题定位也会变成灾难。

5. 灰度发布和回滚机制很重要

上线不一定都能成功,一定要留好退路。比如我们可以先放一部分流量到新版本,确认无误后再全量推上线。


展望未来:云原生之后的方向

现在我们已经在云原生的基础上运行稳定了一段时间。下一步我们计划尝试:

  • 探索 Serverless 架构,将部分非实时任务搬上去降低成本
  • 引入 APM 工具精细化分析接口耗时瓶颈
  • 用 Kafka 解耦异步任务,提升系统的可扩展性
  • 推动 DevOps 文化,让开发和运维更加一体化

也许几年后我会再来写一篇《后端架构演进2:从云原生走向AI原生》,谁知道呢?但可以肯定的是,这条技术成长之路远未结束。


最后的话

回顾这些年走过的路,有时候真的挺感慨的。从最初一个人吭哧吭哧敲代码,到现在带着团队一起做架构设计,技术的变化之快令人应接不暇。

但我始终相信一句话:“好的架构不是设计出来的,是在一次次问题中打磨出来的。”

希望我的这段经历对你有所启发,也能帮你避免一些不必要的弯路。技术这条路不容易,但只要坚持走下去,总会看到曙光。

如果你们也在经历类似的架构演变过程,欢迎留言交流,我们一起成长!


(全文约 3282 字)

评论 0

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