前端转全栈后我眼中的后端架构演进之路

孙娜_移动端
2026-06-08 18:01
阅读 7644

坐标成都,最近这边的天气终于放晴了,周末去玉林路喝了个盖碗茶,感觉生活节奏还是那么巴适。不过一回到公司,面对那堆密密麻麻的需求,血压立马就上来了。

熟悉我的老读者都知道,我是个写了快六年 Vue 和 React 的纯前端。前两年公司搞“全栈化”转型,领导天天开会画饼,说前端不能只懂画页面,得懂数据流,得懂服务端。得,被连哄带骗之下,我硬着头皮开始学 Node.js,算是半只脚踏进了全栈的坑。工作中虽然还是求稳,用成熟的方案,但私下里我这人就喜欢折腾新技术,最近后端架构演进这块儿,算是把我折磨得够呛,也学到了不少东西。

今天不聊前端那些组件化、状态管理的烂大街话题,咱们换个口味,聊聊我这段时间折腾后端架构,从看着“屎山”单体应用,到一步步把它改造成云原生微服务的一些踩坑记录。顺便也聊聊,现在 大模型 这么火,我是怎么用 AI 辅助我完成这波架构升级的。

面对“屎山”单体的绝望

刚接手后端重构那会儿,我看着那个跑了四五年的老系统,整个人是懵的。这是一个巨无霸级别的单体应用,核心业务逻辑全是用 Java 写的 Spring Boot 项目。虽然我是前端出身,但好歹也看过几本《Java 核心技术》,勉强能看懂点皮毛。

但这代码写得,真的是让人想砸键盘。所有的业务逻辑、数据库操作、甚至一些定时任务,全揉在一个工程里。最离谱的是,有个哥们为了图省事,把订单处理和报表导出的逻辑写在同一个 Service 里,导致每次跑月度报表,订单接口就卡得亲妈都不认识。

那时候的部署更是原始,运维老哥每天顶着黑眼圈,手动把打好的 jar 包扔到服务器上。每次前端发版,哪怕只是改个按钮的颜色,后端也得跟着重启,动辄五六分钟的启动时间,测试妹子天天在群里@我:“后端又挂了,我的用例跑不通了!”当时真的想顺着网线过去摇醒那个写代码的兄弟。

痛定思痛,跟技术总监拍桌子争取了半个月,终于拿到了架构重构的排期。目标很明确:拆分微服务,全面拥抱云原生。

摸着石头过河的微服务拆分

拆分微服务听起来高大上,什么 DDD(领域驱动设计)、领域边界,PPT 上画得漂漂亮亮,真落地的时候全是坑。我们团队没那么多时间去搞完美的领域建模,最后采取了最务实的办法:按业务模块粗暴拆分。

核心交易、用户、商品、营销,拆成了四个独立的 Java 微服务。而我负责的 Node.js 团队,则顺理成章地承担了 BFF(Backend For Frontend)层的建设。用 Node.js 做 BFF 简直太香了,异步非阻塞的特性用来做接口聚合和数据裁剪,性能杠杠的,前端同学写起来也得心应手,毕竟都是 JS 生态。

但拆分成微服务后,运维复杂度呈指数级上升。以前一个 jar 包走天下,现在几十个服务,加上 Redis、MySQL、MQ,手动部署简直是做梦。Kubernetes (K8s) 成了唯一的选择。

写 K8s 的 YAML 配置文件那段时间,我真的是体会到了什么叫“面向复制粘贴编程”。Deployment、Service、Ingress、ConfigMap……各种资源对象看得我头晕眼花。稍微缩进错一个空格,Pod 就给你报个 CrashLoopBackOff,排查半天发现是环境变量没配对。

让大模型成为我的“外挂”

就在我被 YAML 文件和复杂的 SQL 迁移脚本搞得焦头烂额时,Augment Code 这类 AI 编程助手救了我的命。讲真,以前我觉得 AI 写写前端组件、补全几行代码还行,写这种严谨的后端部署配置和复杂 SQL,肯定不靠谱。但实际用下来,啪,打脸了。

我把公司的网络拓扑图和现有的 K8s 基础配置喂给 Augment Code,让它帮我生成各个微服务的部署模板。它不仅能把缩进、标签(Labels)、资源限制(Requests/Limits)写得明明白白,还能根据我提供的 Node.js BFF 层代码,自动推导出需要的环境变量和 ConfigMap 结构。这效率,比我手动抄模板快了至少十倍。

更骚的操作还在后面。我们公司在代码规范和 API 接口定义上有一套非常繁琐的内部标准。每次写完一个微服务接口,都要写一大堆 Swagger 注解和符合规范的单元测试。为了摆脱这种重复劳动,我突发奇想,决定对开源的 大模型 进行一次 Fine-tuning(微调)。

我让运维老哥帮忙,从 GitLab 上拉取了过去两年团队里写得最规范的 500 个 PR 代码,以及内部的 API 设计文档,清洗整理成 JSONL 格式的训练集。然后利用 LoRA 技术,对 Qwen 模型进行了微调。

你猜怎么着?微调后的模型简直成了我们团队的“代码规范审查员”。我把新写的 Java 接口丢给它,它不仅能自动补全符合公司规范的 Swagger 注解,还能直接生成覆盖率极高的 JUnit 单元测试。甚至连异常处理的兜底逻辑,都完美契合我们的全局异常拦截器。当时看到测试跑全绿的那一刻,我在工位上差点叫出声来,终于不用半夜爬起来改 Bug 了。

BFF 层与云原生部署实战

光说不练假把式,给大家看看我们 Node.js BFF 层聚合后端 Java 微服务的一段核心代码,以及对应的 K8s 部署配置。

// BFF 层聚合接口:获取用户首页推荐数据
// 使用 NestJS 框架,结合 gRPC 与后端的 Java 微服务通信
import { Controller, Get, Query } from '@nestjs/common';
import { GrpcClient } from './grpc.client';

@Controller('api/v1/home')
export class HomeController {
  constructor(private readonly grpcClient: GrpcClient) {}

  @Get('recommend')
  async getRecommendData(@Query('userId') userId: string) {
    try {
      // 并发调用后端的用户服务和商品服务,降低接口 RT
      const [userInfo, productRecommend] = await Promise.all([
        this.grpcClient.userService.getUserInfo({ userId }),
        this.grpcClient.productService.getRecommendList({ userId, limit: 10 })
      ]);

      // 数据裁剪,只返回前端需要的字段,减少带宽消耗
      return {
        code: 200,
        data: {
          nickname: userInfo.nickname,
          avatar: userInfo.avatarUrl,
          products: productRecommend.items.map(p => ({
            id: p.id,
            title: p.title,
            price: p.promotionPrice || p.originalPrice
          }))
        }
      };
    } catch (error) {
      // 统一异常处理,记录链路追踪 ID
      console.error(`[BFF Error] TraceId: ${request.traceId}`, error);
      return { code: 500, msg: '服务器开小差了' };
    }
  }
}

对应的 K8s Deployment 配置,这里我用了 Augment Code 优化过的版本,加入了健康检查和资源限制,防止某个 BFF 实例内存泄漏把节点拖垮:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: bff-home-service
  labels:
    app: bff-home
    version: v1.2.0
spec:
  replicas: 3
  selector:
    matchLabels:
      app: bff-home
  template:
    metadata:
      labels:
        app: bff-home
    spec:
      containers:
      - name: bff-node
        image: registry.company.com/frontend/bff-home:1.2.0
        ports:
        - containerPort: 3000
        resources:
          requests:
            memory: "256Mi"
            cpu: "200m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        # 存活探针:Node.js 进程挂了自动重启
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 15
          periodSeconds: 10
        # 就绪探针:确保服务完全启动再接收流量
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5

生产环境的毒打与运维心得

架构演进从来不是在 PPT 上画画图那么轻松。微服务拆分后,我们迎来了生产环境的“毒打”。

最痛的一次是去年双11前夕,压测的时候发现订单服务偶尔会出现超时。排查了整整两个通宵,最后发现是数据库连接池配置不合理,加上微服务之间网络抖动,导致连接没及时释放。当时看着监控面板上飙升的错误率,我手心全是汗,心想这次要是上了生产,年终奖绝对泡汤。

后来我们引入了 SkyWalking 做全链路追踪,在 BFF 层和所有 Java 微服务里埋点。只要接口超时,立马能看清是哪个微服务、哪条 SQL 拖了后腿。同时,规范了数据库设计,严禁在微服务之间直接连库查数据,必须走 RPC 接口。

这里给大家总结个表格,对比一下我们改造前后的运维指标,大家直观感受一下:

维度 改造前(单体架构) 改造后(云原生微服务)
部署频率 每周 1 次,每次提心吊胆 每天多次,灰度发布无感知
启动时间 5-8 分钟 容器秒级启动,BFF 层 10 秒内
故障隔离 一个模块 OOM,全系统挂掉 故障限制在单个 Pod,自动重启
资源利用率 峰值资源闲置,低谷资源不足 K8s HPA 自动扩缩容,利用率提升 40%
排查效率 靠肉眼看日志,大海捞针 链路追踪 + 集中式日志,分钟级定位

写在最后

回过头来看这波从单体到云原生的架构演进,我最大的感触是:技术架构没有银弹,所有的演进都是为了业务妥协。微服务解决了扩展性问题,但也带来了分布式事务、运维复杂度等一堆新麻烦。

作为一个前端出身的半吊子全栈,我其实挺庆幸能经历这次重构的。它打破了我对后端的恐惧,让我意识到,无论是前端的组件化拆分,还是后端的微服务演进,底层的设计哲学是相通的——都是为了解耦、复用和高内聚。

现在,借助 大模型Augment Code 这些利器,我们写代码、写配置的效率大幅提升,甚至还能通过 Fine-tuning 让 AI 深度参与代码审查。技术的边界正在变得模糊,未来可能真的不再区分纯粹的前端或后端,而是看谁能更好地利用工具解决业务问题。

不说了,周五了,今晚不用加班。成都的火锅已经沸腾,微服务也稳定运行,是时候去犒劳一下自己这周死掉的脑细胞了。咱们下篇博客再见!

评论 0

最热最新
暂无评论
孙娜_移动端Lv.1
0
影响力
0
文章
0
粉丝