从单体到微服务:一个双非学生的实战血泪史

TechAI
2026-01-03 11:36
阅读 282

大家好,我是小林,某双非院校大二在读(别问,问就是自学编程两年半的老油条),目前在一家百来人的电商创业公司打杂。白天写业务代码,晚上刷 LeetCode 准备跳槽——没错,就是那种“边搬砖边自救”的典型打工人状态。

最近我们组接了个硬核需求:把老掉牙的单体系统拆成微服务,老板说“双11前必须上线”,产品还画了个“全球秒杀架构图”给我看,我当时差点一口老血喷在 MacBook Pro 的键盘上。更离谱的是,运维大哥还在群里@我说:“小林啊,Go 服务部署记得打静态编译包,不然我半夜不想爬起来救火。”

行吧,既然躲不过,那就干!这篇技术分享,就聊聊我这一个月怎么从“单体狗”变成“微服务民工”的实战经历,顺便整理点面试题和踩坑记录,给同样想跳槽、搞分布式系统的兄弟们避避雷。


起因:那个要命的“单体巨兽”

我们的老系统是用 Python + Django 写的,三年没大重构,代码库已经膨胀到 50w+ 行。用户下单、库存扣减、优惠券核销、物流推送……全塞在一个项目里。每次改个小功能,CI/CD 跑半小时,测试同学一看到我的 PR 就叹气:“又来了?”

最要命的是去年双11,订单服务一崩,整个站都挂了。运维查日志发现是库存模块死锁,但因为所有逻辑耦合在一起,根本没法隔离故障。老板震怒:“必须拆!谁不拆谁走人!”——于是这个“光荣任务”落到了我头上(可能因为我简历写了“熟悉分布式系统”?其实是 B 站看多了)。


技术选型:Go 上场,Python 退居二线

虽然我主力语言是 Python,但考虑到高并发和部署轻量化,新微服务果断选了 Go。理由很现实:

  • 编译成二进制,扔服务器上直接跑,不用装 Python 环境(运维狂喜)
  • Goroutine 天然适合高并发场景(比如秒杀)
  • 生态成熟:gRPC、etcd、Go-kit 都很稳

不过老系统还是得兼容,所以定了个混合架构:

  • 新业务(如订单创建、库存服务)用 Go
  • 旧接口保留 Python,逐步迁移
  • 通信协议统一用 gRPC(HTTP/JSON 留给前端)

自嘲一下:当初学 Go 就是为了跳槽,结果被逼着实战,果然“学以致用”才是最快的提升方式。


拆分策略:不是随便切一刀就行

很多人以为微服务就是“把代码按功能拆成几个 repo”,Too young!真正难的是边界划分数据一致性

我们参考了《领域驱动设计》(DDD),先画了上下文映射图,最后拆出这几个核心服务:

服务名 职责 语言 数据库
user-service 用户信息、鉴权 Go MySQL
order-service 订单创建、状态管理 Go MySQL
stock-service 库存扣减、回滚 Go Redis + MySQL
coupon-service 优惠券发放、核销 Python MongoDB

注意:coupon-service 还用 Python,因为历史逻辑太复杂,团队没人敢动。但对外暴露 gRPC 接口,其他服务调它时完全无感。


关键问题 1:跨服务事务怎么办?

下单流程涉及三个服务:扣库存 → 创建订单 → 扣优惠券。任何一个失败,都得全部回滚。但在分布式系统里,没有 ACID,只有 BASE。

我们试过两种方案:

方案 A:两阶段提交(2PC)

理论完美,实测拉胯。网络抖一下,参与者就卡住,还得人工干预。直接 PASS。

方案 B:Saga 模式 + 补偿事务

最终选定这个。核心思想:每个步骤都有对应的“撤销操作”

比如:

  1. stock-service 扣库存 → 成功
  2. order-service 创建订单 → 失败!
  3. 调用 stock-service/CompensateStock 回滚库存

代码示意(Go):

// order-service 中的下单逻辑
func CreateOrder(ctx context.Context, req *pb.CreateOrderRequest) (*pb.CreateOrderResponse, error) {
    // 1. 扣库存
    _, err := stockClient.DeductStock(ctx, &pb.DeductStockRequest{SkuId: req.SkuId, Qty: req.Qty})
    if err != nil {
        return nil, status.Errorf(codes.Internal, "deduct stock failed: %v", err)
    }

    // 2. 创建订单(假设这里 panic 了)
    if err := db.CreateOrder(req); err != nil {
        // 触发补偿:回滚库存
        stockClient.CompensateStock(ctx, &pb.CompensateStockRequest{
            SkuId: req.SkuId,
            Qty:   req.Qty,
            TraceId: getTraceId(ctx), // 用于幂等
        })
        return nil, err
    }

    return &pb.CreateOrderResponse{OrderId: "xxx"}, nil
}

面试题预警:面试官常问“如何保证分布式事务一致性?”
别只会说“用 RocketMQ 事务消息”,得讲清楚业务场景、补偿机制、幂等设计!


关键问题 2:服务发现与调用链追踪

早期我们用 Nginx 做负载均衡,结果每次加机器都得改配置,运维骂娘。后来上了 etcd + gRPC 负载均衡,服务启动自动注册,客户端轮询调用。

但更头疼的是排查问题。以前单体时代,一个请求日志从头打到尾;现在一次下单涉及 4 个服务,日志散落在不同机器。

解决方案:OpenTelemetry + Jaeger

  • 每个服务注入 trace ID
  • gRPC 拦截器自动透传上下文
  • 日志格式统一带上 trace_id

效果拔群!上周五晚上线上偶发超时,我 5 分钟就定位到是 coupon-service 的 MongoDB 查询慢了——换作以前,估计得通宵。


数据库设计:别让 SQL 成为瓶颈

微服务不代表每个服务都能乱建表。我们定了几条军规:

  1. 禁止跨服务直接查 DB
    想拿用户信息?调 user-service 的 gRPC 接口,别偷偷连 user_db!

  2. 读写分离 + 分库分表预埋
    order-service 的订单表,主键用 Snowflake ID(避免自增瓶颈),未来可按 user_id 分库。

  3. 缓存策略统一
    Redis 用 service:key:id 格式命名,比如 stock:sku:1001,避免 key 冲突。

吐槽一句:产品经理曾要求“实时统计全站订单量”,我差点答应写个 SELECT COUNT(*) FROM orders……还好被 DBA 拦住了,改成用 Flink 实时聚合到 Redis。


性能压测:别信本地跑得快

本地 wrk -t12 -c400 -d30s http://localhost/order 跑出 5000 QPS,上线后直接 502。为啥?

  • 网络延迟:服务间调用多了 RTT
  • GC 压力:Go 服务对象太多,STW 时间长
  • DB 连接池:默认 10 连接,高并发下排队

优化措施:

  • gRPC 启用连接复用(grpc.WithInsecure() + 连接池)
  • Go 服务调整 GOMAXPROCS=CPU核数
  • 数据库连接池调到 100+

最终压测结果(4 核 8G 云主机):

场景 QPS P99 延迟
单体 Django 320 420ms
微服务 Go 2100 85ms

虽然 QPS 提升明显,但复杂度也爆炸了。运维说:“现在一个 bug 要查四个日志,你们开发是不是该请我喝奶茶?”


给想跳槽的同学:微服务面试题清单

最近面了几家公司,发现微服务是必考项。整理几个高频题:

  1. 如何设计一个高可用的微服务注册中心?(答 etcd/ZooKeeper 原理,脑裂处理)
  2. gRPC 和 RESTful 如何选型?(性能 vs 兼容性)
  3. 分布式 ID 生成方案有哪些?Snowflake 有什么坑?(时钟回拨!)
  4. 服务雪崩怎么防?(熔断 Hystrix / Sentinel,限流令牌桶)
  5. Python 能做微服务吗?(能,但高并发场景不如 Go/Java)

记住:别背八股文,结合项目讲。比如我说“我们在 stock-service 用 Redis Lua 脚本保证扣库存原子性”,面试官眼睛都亮了。


最后:微服务不是银弹

折腾一个月,系统终于跑起来了。双11 当天峰值 1800 QPS,零重大故障——老板请全组吃了顿火锅(人均 80,抠门但够意思)。

但我也看清了:微服务不是万能药。如果你的业务简单、团队小、迭代快,单体反而更香。我们现在的运维成本、监控复杂度、调试难度,比之前高了至少 3 倍。

不过嘛,作为想跳槽的双非学生,这段经历值了。简历上能写“主导微服务拆分,支撑日订单 10w+”,HR 筛简历时大概率不会把我扔进垃圾桶。

共勉吧,打工人!下次见面,希望是在大厂茶水间里吹空调,而不是在创业公司通宵修 Bug。

P.S. Windows 我只用来测 IE 兼容性(虽然现在没人用 IE 了),MacBook 键盘都被我敲出包浆了……

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝