在 activity-service 中调用 user-service
从单体到云原生:一个杭漂后端仔的血泪演进史
上周五凌晨两点,我瘫在椅子上盯着阿里云控制台里那个飘红的 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 只会增加运维负担。
但如果你像我一样,被产品经理“温柔”地推进了高并发火坑,那这套演进路径值得参考:
- 先跑起来:单体快速验证 MVP
- 识别瓶颈:用 APM 工具(如 SkyWalking)找性能热点
- 垂直拆分:按业务域拆服务,优先解耦核心链路
- 容器化:Docker 标准化部署
- 上云原生:K8s + Service Mesh + GitOps
最近我把这段经历整理成了简历里的“项目亮点”,还押中了一道网易互娱的面试题:“请描述你参与的系统如何从单体演进到微服务?” 面试官听完直点头,估计秋招有戏。
最后送大家一句我在阿里园区墙上看到的话:“架构是演出来的,不是设计出来的。” 别怕踩坑,代码写烂了可以重构,但经验是真的。
(完)
P.S. 如果你也正在准备后端秋招,欢迎交流~ 我整理了一份《Python 后端高频面试题+云原生考点》,私信“云原生”自取。别卷了,早点睡,头发比 offer 重要。

评论 0