从单体到微服务:一个奶爸程序员的深夜实战手记
上周五晚上十点半,娃终于睡了。我轻手轻脚地坐到MacBook前,打开终端,心里只有一个念头:再不把那个祖传单体应用拆了,我怕是连简历都投不出去。
入职新公司快两个月了,作为两个娃的奶爸程序员,白天写代码的时间少得可怜——不是在开会就是在救火,要不就是被产品经理追着问“这个功能明天能上线吗?” 晚上回家?等娃睡了才有自己的时间。但这次真拖不下去了:系统一到下午三点就慢如蜗牛,DB连接池爆满,运维大哥看我的眼神都带着怜悯。
而最扎心的是,前两天刷招聘软件,清一色写着“熟悉微服务架构、K8s经验优先”。我瞅了瞅自己那套跑在Docker Compose里的Python Flask单体应用,默默关掉了页面——这玩意儿放简历上,HR怕是要以为我还在2016年打工。
起点:那个“什么都干”的单体应用
我们原来的系统是个典型的Python Flask单体应用。用户管理、订单处理、支付回调、消息推送……全塞在一个代码库里。本地开发用flask run,测试环境靠docker-compose up,生产直接扔到一台4核8G的云服务器上,Nginx反代一下完事。
听起来是不是很熟悉?没错,这就是大多数创业公司或中小团队的起点。问题不大时,它确实香——改个接口五分钟上线,调试方便,日志集中。但一旦业务量上来,问题就来了:
- 一个模块出问题(比如支付回调超时),整个服务卡死
- 数据库表越建越多,耦合严重,改个字段要全盘评估
- 部署必须全量更新,哪怕只改了一行前端JS
- 团队协作困难:后端A改订单逻辑,后端B改用户模块,PR经常冲突
最要命的是,上次双11预演,系统直接崩了。监控显示PostgreSQL CPU飙到100%,连接数打满。原因?一个低效的SQL查询在高并发下被放大成灾难。那一刻,我知道:微服务不是“要不要做”,而是“必须马上做”。
拆!但别瞎拆
很多人一听微服务,立马开干:用户服务、订单服务、商品服务、通知服务……拆它十个八个。但作为一个被娃折腾得只剩半条命的老父亲,我深知——重构不是炫技,是解决问题。
我和架构师(兼CTO,也是我直属领导)对齐了几个原则:
- 按业务边界拆,不是按技术分层
别搞什么“数据库服务”、“缓存服务”,那叫中间件,不是微服务。 - 先拆高频、高风险模块
支付和订单是痛点,优先动刀;静态内容(比如帮助中心)先不动。 - 通信方式统一用HTTP + JSON
别整gRPC那一套,团队没人熟,后期维护成本高。简单稳定最重要。 - 每个服务必须可独立部署、可独立扩缩容
于是第一阶段,我们只拆出两个核心服务:
auth-service:负责用户注册、登录、鉴权order-service:处理订单创建、状态变更
原单体保留其他功能,作为“遗留系统”逐步迁移。
工具链:奶爸的效率命脉
带娃的人没时间折腾花里胡哨的东西。工具必须简单、可靠、文档齐全。以下是我们的“微服务全家桶”:
| 类别 | 工具 | 选择理由 |
|---|---|---|
| 语言框架 | Python + FastAPI | 异步支持好,自动生成OpenAPI文档,比Flask更现代 |
| 容器化 | Docker | 公司已有镜像仓库,运维熟悉 |
| 编排 | Kubernetes (K8s) | 新公司用EKS,正好练手 |
| 服务发现 | K8s Service + DNS | 不引入Consul/etcd,减少复杂度 |
| API网关 | Nginx Ingress | 现成,支持JWT验证 |
| 日志 | Loki + Grafana | 轻量,比ELK省资源 |
| 监控 | Prometheus + Alertmanager | 标配,K8s生态友好 |
特别说下FastAPI。以前用Flask总觉得少了点啥,FastAPI的Pydantic模型校验 + 自动生成Swagger,让我这个天天被产品改需求的人泪流满面——接口文档再也不用手写了!
# order-service 示例
from fastapi import FastAPI, Depends
from pydantic import BaseModel
app = FastAPI()
class CreateOrderRequest(BaseModel):
user_id: int
items: list[str]
total_amount: float
@app.post("/orders")
def create_order(req: CreateOrderRequest):
# 自动校验 req 是否符合模型
# 如果传了个字符串给 user_id,直接422报错
...
这种“契约先行”的设计,在微服务间通信时简直是救命稻草。
坑?那可太多了
坑1:数据库怎么拆?
最头疼的不是代码,是数据。原来所有表都在一个PostgreSQL实例里。现在要拆,意味着:
auth-service用auth_dborder-service用order_db
但订单里要有 user_id 啊!跨库关联查不了,怎么办?
我们的解法是:服务间通过API获取必要数据,不共享数据库。
比如订单服务需要用户昵称?调 GET /users/{id} 接口。虽然多一次网络调用,但换来的是彻底解耦。为了性能,我们在订单创建时把关键用户信息(如昵称、手机号)冗余存一份——最终一致性,总比强一致拖垮系统强。
坑2:本地开发怎么联调?
以前 docker-compose up 一套全跑起来。现在五个服务,总不能每改一行代码就推镜像到K8s吧?
解决方案:Telepresence + Skaffold
- Telepresence:把本地进程“注入”到K8s集群,就像你在集群里运行一样
- Skaffold:监听代码变化,自动 build & deploy
我在Mac上改完代码,5秒内就能在K8s里看到效果。Windows?那台老伙计只用来测IE兼容性了,微服务开发?不存在的。
坑3:分布式事务怎么搞?
用户下单 → 扣库存 → 创建订单 → 发通知。任何一个环节失败,都要回滚。
我们没上Seata那种重量级方案,而是用Saga模式 + 补偿事务:
- 创建订单(状态:pending)
- 调库存服务扣减
- 成功:更新订单状态为 confirmed
- 失败:调补偿接口(比如释放预占库存),订单状态设为 cancelled
- 异步发通知(失败可重试)
配合Kafka做事件驱动,失败任务进DLQ(死信队列),人工介入。虽然不是100%自动化,但在当前业务规模下,够用且可控。
效果:终于敢在简历写“微服务”了
重构花了三周,其中两周在改测试用例(别问,问就是血泪教训)。上线后:
- 系统吞吐量提升3倍(从300 QPS到900+)
- 订单服务可独立扩容,大促时CPU从95%降到40%
- 一次支付回调故障,只影响支付模块,其他功能正常
- 我终于能在简历的“项目经验”里堂堂正正写上:“主导单体应用向微服务架构演进,基于Python/FastAPI/K8s”
更重要的是,上周五晚上十点半,我提交完最后一个PR,看着Grafana里平稳的曲线,轻轻合上MacBook——娃还没醒,老婆在追剧,世界如此宁静。
或许这就是中年程序员的小确幸:代码跑得稳,娃睡得香,简历能投出去。
给同行的几句真心话
- 别为了微服务而微服务:如果团队不到10人,业务没到一定规模,单体可能更合适。
- 自动化测试是生命线:微服务多了,手工回归测试会疯掉。我们要求每个服务有90%+单元测试覆盖率。
- 监控和日志不是可选项:没有可观测性,微服务就是分布式噩梦。
- 简历可以写“微服务”,但别吹“高并发”:除非你真扛过百万QPS,否则容易被面试官问穿。
最后,如果你也是个下班后才有时间学习的奶爸/妈程序员,我想说:慢慢来,别焦虑。每一个深夜敲下的代码,都是未来跳槽时简历上闪亮的一行。
毕竟,我们不仅要写好代码,还要给娃换尿布呢。

评论 0