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

王庆华△
2025-06-19 04:08
阅读 675

引言:为什么我愿意分享这个话题?

引言:为什么我愿意分享这个话题?

作为一名后端工程师,我在过去五年里亲历了多个项目的系统架构演变。从最初接手一个几十万行代码的单体应用,到后来参与微服务拆分、容器化部署,再到最近几年推动公司全面拥抱云原生架构,这段旅程并不容易,但也充满收获。

今天我想用第一人称,结合自己亲身经历过的项目场景,聊聊这套“从单体到云原生”的演进路径——不仅是技术上的改变,更是一种思维和工程理念的转变。


项目背景与挑战:我们是如何走到这一步的?

项目背景与挑战:我们是如何走到这一步的?

初期阶段:单体架构的甜蜜时光

三年前,我在一家中型互联网公司任职。当时我们的核心业务是一个基于Java的电商后端系统,采用Spring Boot + MySQL + Redis的经典组合。整个系统跑在几台物理服务器上,前后端不分离,部署方式是传统的Jenkins打包+ssh上传jar包+重启服务。

那时候团队不大,开发效率高,上线流程简单快捷。但随着用户量增长、功能越来越多,问题也逐渐显现出来:

  • 启动慢:一次完整的Spring Boot项目启动要5分钟以上(别问我是怎么知道的);
  • 发布风险大:小改动也要全量发版,稍有不慎就导致整个系统不可用;
  • 数据库瓶颈明显:高峰期经常出现慢查询,锁表频发;
  • 水平扩展困难:虽然加了几台服务器做负载均衡,但由于Session共享、本地文件等问题,效果有限。

爆发点:一场真实的事故

真正让我意识到必须重构的,是一次生产事故。那天凌晨3点,运维突然打电话说首页加载缓慢,很多接口超时,甚至报错。排查发现是因为某位同事修改了一个订单导出模块的SQL语句,没有加索引,结果把整个数据库打爆了。

这直接导致当天营收下降10%,老板脸色铁青。事后总结会上,CTO一句话让我印象深刻:“我们要想办法让局部问题不影响整体。”

这就是我们架构演进真正的起点。


拆分微服务:从单体走向分布式

服务器部署方案-1

目标与思路

我们决定尝试微服务架构。第一步不是盲目拆分,而是先梳理清楚业务边界。我和团队花了一周时间画业务流程图、识别核心领域,最终将原有系统拆分为以下几个子系统:

  • 用户服务
  • 商品中心
  • 订单服务
  • 库存中心
  • 支付中心
  • 搜索服务

每个服务使用独立的数据库实例,通过RESTful API或RPC进行通信(初期选用了HTTP,后来迁移到gRPC)。

技术选型与实践

接口设计原则

  • 所有接口统一JSON格式
  • 使用Swagger生成文档,强推接口版本控制
  • 要求每个服务对外暴露健康检查和Metrics监控点

数据库拆分策略

  • 垂直分库:按业务逻辑划分数据库实例
  • 主从复制:读写分离,减少主库压力
  • 分布式事务?暂时不考虑。先从业务层面规避,实在绕不开的就靠异步补偿机制(比如消息队列)

配置中心引入

为了避免配置混乱,我们引入了Spring Cloud Config,并搭建了配置中心服务。关键配置如下:

spring:
  cloud:
    config:
      server:
        git:
          uri: git@github.com:company/configs.git
          search-paths: '{application}'

这样不同环境(dev/test/prod)的配置都能统一管理,大大提升了部署效率。


云原生:迈向弹性可扩展的新时代

上云准备:基础设施现代化

我们选择了阿里云作为上云平台。主要做了以下几件事:

  • 将所有应用容器化,Docker镜像构建标准化
  • 使用Kubernetes做编排管理,实现自动扩缩容
  • 引入Prometheus + Grafana做监控告警
  • 使用ELK做日志收集分析

容器化改造小例子

以订单服务为例,其Dockerfile大致如下:

FROM openjdk:8-jdk-alpine
COPY order-service.jar app.jar
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]

然后配合K8s的Deployment进行滚动更新:

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

服务治理:熔断限流成为标配

我们引入了Sentinel做服务治理。它不仅支持QPS限流、线程池隔离,还提供了实时监控面板。

举个简单的限流规则配置示例:

[
  {
    "resource": "/order/create",
    "limitApp": "default",
    "grade": 1,
    "count": 1000,
    "strategy": 0,
    "controlBehavior": 0,
    "clusterMode": false
  }
]

这条规则的意思是:/order/create这个接口每秒最多允许1000次请求,超过后快速失败。上线后有效防止了因突发流量导致的服务雪崩。


踩过的坑和那些深夜加班的日子

微服务带来的新烦恼

  • 分布式事务难搞:我们试过Seata,但性能和稳定性都不理想。后来改成了事件驱动模式,通过MQ解耦,再配合本地事务表进行补偿。

  • 跨服务调用延迟:开始没重视链路追踪,遇到问题只能抓包查日志。后来引入SkyWalking后简直是如获至宝,服务间依赖、接口耗时一目了然。

  • 数据库冷热数据混用:早期没做历史数据归档,导致线上数据库越来越大。后来引入Elasticsearch做离线查询,MySQL只保留近半年的数据,效果显著。

容器化部署的坑

  • 镜像体积过大:最开始用openjdk:8,镜像动辄几百MB。后来换成alpine版,最后改成distroless镜像,减小了不少。

  • K8s滚动更新卡顿:一开始没有合理设置readinessProbe和livenessProbe,导致Pod切换过程中出现大量4xx错误。优化健康检查逻辑后问题解决。

  • 网络不通引发的血案:K8s Service之间互相调用走的是DNS解析,曾因为CoreDNS异常导致服务调用全部失败,紧急恢复才避免更大损失。

这些教训都在不断提醒我们:云原生不仅仅是换工具,更重要的是工程方法论的整体升级。


架构升级后的收益与改变

经过两年多的努力,我们的系统发生了翻天覆地的变化:

指标 改造前 改造后
单机部署故障影响面 整个系统瘫痪 最多影响一个子系统
发布频率 两周一次 日常每日多次灰度发布
平均响应时间 500ms+ <200ms
QPS承载能力 1万左右 峰值可达10万
故障定位时间 小时级 分钟级
成本结构 固定资源浪费严重 按需伸缩节省30%成本

不仅如此,整个研发流程也发生了质变:

  • CI/CD流程成熟,每次提交都有流水线保驾护航;
  • 所有服务具备可观测性,问题不再“黑盒”;
  • 开发人员对性能和健壮性有了更高要求;
  • 我们也开始尝试A/B测试、蓝绿发布等高级玩法。

个人经验总结与建议

如果你也在考虑系统架构升级或者已经处于这个过程之中,我想给几点真心建议:

1. 架构演进不是一蹴而就的事情

微服务也好,云原生也罢,都是解决问题的手段,而不是目的。不要为了“追时髦”而盲从新技术。我的建议是:

  • 先搞清楚现有系统的瓶颈所在;
  • 明确当前阶段的核心目标(性能?扩展性?易维护?);
  • 再去选择合适的技术方案。

2. 技术债要早还,越拖越疼

曾经我们也觉得:“现在上线最重要,以后再说”。结果后期花十倍代价去修复。例如,早期没做接口版本控制,后期升级API时各种踩坑;早期没规范异常处理,到处try-catch乱飞。

技术债不会自己消失,反而会像滚雪球一样越积越大。

3. 多关注非功能性需求

很多时候我们太专注于完成功能需求,忽略了性能、监控、自动化等维度。其实这些才是一个系统能否长期稳定运行的关键。

  • 要求每个服务必须提供健康检测;
  • 统一日志格式、埋点标准;
  • 提供通用SDK封装常用操作(如Redis连接、DB访问);
  • 自动化回归测试要跟上节奏。

4. 云原生不是终点,而是新的起点

很多人以为上了K8s就是云原生了。其实不然。真正的云原生还包括:声明式API、Operator模型、Serverless化、Service Mesh……

我们现在正在尝试将部分低流量服务迁移至函数计算FC(Function Compute),进一步降低运营成本。同时也在评估Istio是否值得在我们这样的中型系统落地。

未来的路还很长,但我相信方向是对的。


写在最后:架构演变背后的人文思考

回顾整个过程,我最大的感悟不是技术如何先进,而是人——团队协作、沟通机制、工程文化的建设才是支撑复杂架构演化的基石。

每一次架构调整的背后,是无数个深夜讨论、争执又妥协的过程。有时候我们在会议室吵得不可开交,但第二天依然坐在一起喝着咖啡调试代码。

也许这才是最珍贵的部分。

如果你问我:“这条路值得吗?”我会毫不犹豫地说:“值得。”因为它不仅改变了我们的系统,更改变了我们自己。

希望这篇文章能给你带来一些启发,在你的架构演进路上有所帮助。如果有任何疑问或共鸣,欢迎随时留言交流,我们一起成长。


作者简介:一位坚持一线写代码的架构师,热爱后端开发与性能优化,擅长从小处着手改进系统的健壮性和可维护性。GitHub@jimmyzhou,公众号「程序员架构笔记」。

评论 0

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