后端架构的十年漂流:从单体到云原生,一个Spark老炮儿的血泪史

独立产品实验室
2025-12-28 11:33
阅读 1433

去年双11前夕,我坐在深圳南山科技园的工位上,盯着监控面板上飙红的CPU曲线,心里默念:“这破单体应用,真该埋了。”
作为一个在腾讯系公司摸爬滚打三年的大数据开发,天天和 Spark、Hive、Kafka 打交道,原本以为自己这辈子就跟批处理和流计算锁死了。结果去年被临时抓壮丁,参与一个老系统的重构——理由是“你懂分布式,应该也懂微服务吧?”(领导原话)

我?一个 Vim 党,连 IDEA 都很少开的人,突然要搞后端架构演进?但转念一想,跳槽面试时“系统设计”题老被问到,不如趁机补补课。


一切始于那个“万能”的Java单体

五年前,我们有个核心服务,用 Spring Boot 写的,数据库是 MySQL,前端用 Thymeleaf 渲染页面,顺带嵌点 JavaScript 做交互。听起来是不是很复古?但它真的跑得动——直到业务量暴涨。

这个单体应用啥都干:用户管理、订单处理、日志记录、甚至还有个简易爬虫模块(别问,问就是产品经理说“竞品数据很重要”)。所有逻辑塞在一个 WAR 包里,部署在几台 Tomcat 上。每次发布,运维兄弟都要念三遍佛经,生怕出事。

最离谱的是,那个爬虫模块居然和交易逻辑共用同一个线程池!某次爬虫目标网站反爬机制触发,大量请求卡住,直接把整个交易链路拖垮。线上事故复盘会上,测试同事幽幽地说:“你们后端是不是觉得‘能跑就行’?”

那一刻,我知道:拆!


微服务:理想很丰满,现实全是坑

我们决定走微服务路线。拆成 user-service、order-service、crawler-service……每个服务独立部署,用 Dubbo + ZooKeeper 做 RPC。Java 还是主力语言,毕竟团队熟。

但问题来了:

  • 服务间调用爆炸:一个下单操作要跨 5 个服务,链路追踪全靠日志 grep。
  • 数据库耦合:虽然服务拆了,但所有服务还连同一个 MySQL 实例,DBA 看到我们就像看到仇人。
  • 部署复杂度飙升:以前 scp 一个包重启 Tomcat 就行,现在要管十几个服务的版本、配置、依赖。

更惨的是,那个爬虫服务经常因为目标网站改结构而挂掉,但它的崩溃会通过熔断机制间接影响主流程。有次凌晨三点,我被 PagerDuty 叫醒,发现是因为爬虫解析失败导致用户无法查看历史订单——就因为我们在前端 JavaScript 里硬编码了一个“最近抓取时间”字段。

教训:微服务不是银弹,拆得不好,只会把单体地狱变成分布式炼狱。


转向云原生:K8s 救我狗命

去年初,公司全面拥抱云原生。我们开始用 Kubernetes + Helm 管理服务,数据库也拆成了读写分离 + 分库分表(ShardingSphere 上场),消息队列换成 Pulsar(别问,问就是“比 Kafka 新”)。

关键转变在于:

  • 容器化:每个服务打包成 Docker 镜像,CI/CD 流水线自动推到 Harbor。
  • 声明式部署:用 YAML 定义 Deployment、Service、Ingress,再也不用手动 ssh。
  • 弹性伸缩:爬虫服务高峰期自动扩容,闲时缩到 0(对,用 KEDA 做事件驱动伸缩)。

举个例子,爬虫服务现在完全独立:

# crawler-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: crawler-service
spec:
  replicas: 2
  template:
    spec:
      containers:
      - name: crawler
        image: my-registry/crawler:v1.2
        env:
        - name: TARGET_URLS
          valueFrom:
            configMapKeyRef:
              name: crawler-config
              key: urls
        resources:
          requests:
            memory: "512Mi"
            cpu: "200m"
          limits:
            memory: "1Gi"
            cpu: "500m"

而且它不再直接暴露给前端。前端 JavaScript 调用的是 data-api 服务,后者从缓存(Redis)或异步任务结果表中读数据,彻底解耦。


架构对比:血与泪的数据说话

维度 单体架构 微服务(初期) 云原生架构
部署频率 每月 1~2 次 每周 3~5 次 每天 10+ 次(GitOps)
故障隔离 无(一挂全挂) 部分(但链路复杂) 强(Pod 级别隔离)
资源利用率 低(预留冗余) 高(HPA + VPA 动态调度)
爬虫模块稳定性 极差(拖垮主流程) 差(仍耦合数据) 优秀(独立 + 异步)
面试题命中率 “讲讲你的单体优化” “怎么保证最终一致性?” “K8s 网络模型了解吗?”

说到面试题——自从搞了云原生,我面试时终于能答出“如何设计一个高可用爬虫系统”这种题了。不再是背八股文,而是真有故事可讲:比如用 Kafka 做任务队列,Flink 做实时清洗,结果存入 Iceberg 表供 Spark 查询——这不就是我的日常?


别忘了前端那点事儿

很多人以为后端架构演进跟前端无关,大错特错。我们早期在 JavaScript 里直接调多个后端接口,导致浏览器并发限制、CORS 报错满天飞。后来引入 BFF(Backend For Frontend)层,用 Node.js 聚合数据,前端只对接一个入口。

// old way (bad!)
fetch('/user/profile')
fetch('/order/history')
fetch('/crawler/latest') // 这个经常404...

// new way (BFF)
fetch('/api/dashboard-data') // 由BFF聚合,加缓存

BFF 不仅提升了前端体验,还让后端服务可以安心做细粒度拆分,不用考虑“前端会不会调崩”。


写在最后:稳定压倒一切

折腾了快两年,系统终于稳了。上周五晚上,我看着 Grafana 面板上平稳的曲线,默默关掉 Vim,喝了口已经凉透的瑞幸。

作为大数据开发,我原本以为云原生是后端的事。但现实是:数据管道、实时计算、服务架构,早已融为一体。你不懂 K8s,怎么部署 Flink JobManager?你不了解服务网格,怎么排查 Spark Thrift Server 的网络延迟?

所以,别再把自己局限在“我是搞大数据的”或者“我只是写 Java 的”。技术栈在融合,边界在消失。下次面试官问“你怎么看后端架构演进”,你可以笑着说:“从单体到云原生,我踩过的坑,够写一本《程序员避坑指南》了。”

当然,如果他接着问“那爬虫模块怎么设计?”,你就知道——机会来了。

评论 0

最热最新
暂无评论
独立产品实验室Lv.1
0
影响力
0
文章
0
粉丝