在 activity-service 中调用 user-service

孙娜_移动端
2026-01-04 19:19
阅读 2542

从单体到云原生:一个杭漂后端仔的血泪演进史

上周五凌晨两点,我瘫在椅子上盯着阿里云控制台里那个飘红的 CPU 使用率,脑子里只剩一句话:“早知道当初就别图省事把所有接口塞在一个 Django 项目里了。”

我是浙大计算机大三的,坐标杭州,最近一边肝毕设一边疯狂刷 LeetCode 准备秋招。因为目标是投阿里和网易的后端岗,所以趁着暑假接了个小外包项目练手——结果一不小心把系统搞成了“生产事故模拟器”。这个项目最初只是个简单的活动报名系统,用 Python + Flask 搞定,不到 500 行代码跑得飞起。但产品经理一句“能不能加个实时排行榜+短信通知+用户行为分析”,直接让我从单体架构一脚踩进了微服务的深水区。


一切崩坏始于“快速上线”

项目初期,我和另一个同学用 Flask 写了个单体应用:用户注册、活动创建、报名提交全在一个服务里,数据库就一个 MySQL 表。本地跑着挺爽,部署到腾讯云轻量服务器也稳如老狗。直到上周双11预热期间,用户量突然从日活 200 暴涨到 2w,整个系统直接雪崩。

[ERROR] Worker timeout (pid: 1234)
[CRITICAL] WORKER TIMEOUT (pid:1234)

Gunicorn 日志刷屏,Nginx 返回 502,Redis 连接池耗尽……最离谱的是,连静态文件都加载不出来。运维大哥在钉钉群里@我:“兄弟,你这单体应用把整台机器干趴了,连监控 Agent 都卡死了。”

那一刻我深刻理解了什么叫“牵一发而动全身”——改个用户头像接口,可能让支付模块超时;数据库慢查询拖垮整个进程。面试题里常问“单体架构的瓶颈在哪?”,现在我能答八百字:扩展性差、故障隔离弱、技术栈绑定死、发布风险高


拆!拆成微服务,但别乱拆

痛定思痛,我决定重构。参考了阿里内部常用的“垂直拆分 + 领域驱动设计”思路,把系统拆成四个核心服务:

服务名 职责 技术栈
user-service 用户管理、鉴权 Python + FastAPI + JWT
activity-service 活动创建/查询 Python + Django ORM
notify-service 短信/邮件推送 Celery + Redis Queue
analytics-service 用户行为埋点分析 Flask + ClickHouse

拆分原则很简单:高内聚、低耦合。比如用户注册成功后需要发短信,不再直接调用 Twilio SDK,而是往 RabbitMQ 发一条消息,由 notify-service 异步消费。这样即使短信服务商挂了,也不影响主流程。

但拆完第二天就翻车了——服务间调用超时,链路追踪一片空白。原来忘了加熔断和限流!赶紧补上:

from tenacity import retry, stop_after_attempt, wait_exponential

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def get_user_info(user_id):
    resp = requests.get(f"http://user-service/api/v1/users/{user_id}", timeout=2)
    resp.raise_for_status()
    return resp.json()

配上 OpenTelemetry 埋点,终于能在 Grafana 里看到完整的调用链了。虽然代码多了三倍,但至少再也不会“一个 Bug 全站宕机”。


上云原生:不是换个 K8s 就完事了

微服务跑通后,老板(其实就是甲方)说:“能不能上云?我们想弹性伸缩。” 于是我又被推入云原生的坑。

一开始以为只要把 Dockerfile 写好,kubectl apply -f . 就万事大吉。结果发现:配置管理、服务发现、日志收集、自动扩缩容全是新问题。比如环境变量怎么传?总不能把数据库密码写死在 YAML 里吧?

解决方案:用 ConfigMap + Secret 管理配置,配合 Helm Chart 参数化部署。

# deployment.yaml 片段
env:
  - name: DB_HOST
    valueFrom:
      configMapKeyRef:
        name: app-config
        key: db_host
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: db-secret
        key: password

日志更是头疼。以前 print("debug") 就行,现在 Pod 一重启日志全丢。最后接入阿里云 SLS,用 Fluentd 收集 stdout,再配个告警规则:“错误日志 > 10条/分钟 → 钉钉通知”。

最爽的是 HPA(Horizontal Pod Autoscaler)。根据 CPU 和自定义指标(比如 RabbitMQ 队列长度)自动扩缩容:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: notify-service
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Pods
    pods:
      metric:
        name: queue_length
      target:
        type: AverageValue
        averageValue: "10"

上周压力测试,QPS 从 200 打到 5000,Pod 自动从 2 个扩到 15 个,CPU 始终压在 65% 左右。那一刻我仿佛看到了 P8 的影子(笑)。


Python 在云原生时代的定位

很多人说“Python 不适合微服务,性能太差”。其实要看场景。我的 user-service 用 FastAPI + Uvicorn(async),单核能扛 1200 QPS;notify-service 用 Celery 处理异步任务,稳定得很。关键是别在 I/O 密集型场景硬上多线程

不过,如果真要做高并发网关或实时计算,Go 或 Rust 确实更香。但对大多数业务系统来说,Python 的开发效率碾压一切。而且现在有 PyO3、Cython 等方案兜底,真遇到性能瓶颈也能局部优化。

顺便吐槽一句:面试官老爱问“为什么选 Python 做后端?”,我的答案是:“因为老板要三天上线,而我会 Python。”


综合来看:架构没有银弹

回过头看,从单体到云原生,不是技术炫技,而是业务复杂度倒逼架构演进。如果你的系统只有三个接口、日活不到一千,强行上 K8s 只会增加运维负担。

但如果你像我一样,被产品经理“温柔”地推进了高并发火坑,那这套演进路径值得参考:

  1. 先跑起来:单体快速验证 MVP
  2. 识别瓶颈:用 APM 工具(如 SkyWalking)找性能热点
  3. 垂直拆分:按业务域拆服务,优先解耦核心链路
  4. 容器化:Docker 标准化部署
  5. 上云原生:K8s + Service Mesh + GitOps

最近我把这段经历整理成了简历里的“项目亮点”,还押中了一道网易互娱的面试题:“请描述你参与的系统如何从单体演进到微服务?” 面试官听完直点头,估计秋招有戏。

最后送大家一句我在阿里园区墙上看到的话:“架构是演出来的,不是设计出来的。” 别怕踩坑,代码写烂了可以重构,但经验是真的。

(完)

P.S. 如果你也正在准备后端秋招,欢迎交流~ 我整理了一份《Python 后端高频面试题+云原生考点》,私信“云原生”自取。别卷了,早点睡,头发比 offer 重要。

评论 0

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