微服务架构设计实战:从单体到分布式

云边有个仓库
2025-12-15 19:04
阅读 407

去年双11凌晨三点,我蹲在公司茶水间啃着冷掉的包子,盯着 Grafana 上那条陡峭的红色曲线——系统响应时间飙到了 8 秒。老板站在背后幽幽地说:“小张啊,咱们这个单体架构……是不是该动一动了?”

那一刻,我脑子里只有两个念头:一是赶紧把这堆烂代码拆了,二是房贷下个月还差 3000 块没凑齐。

作为一个北漂程序员,坐标回龙观,每天通勤 1 小时挤地铁,白天写 Java,晚上刷 LeetCode 准备跳槽面试。本来只想安安分分搬砖还贷,结果被领导“委以重任”——牵头把公司那个运行了五年的单体电商系统,改成微服务架构。

说实话,一开始我是拒绝的。毕竟谁不知道“微服务”三个字背后藏着多少坑?但想想简历上能加一行“主导微服务改造”,说不定明年跳槽能多要 5k,咬咬牙也就上了。


起点:那个又大又臭的单体应用

我们的老系统用的是 Spring Boot + MyBatis,数据库是 MySQL 单实例,部署在一台 16C32G 的云服务器上。代码仓库里光 controller 就有 200+ 个,service 层更是乱成一锅粥——订单模块调库存,库存又偷偷调用户积分,用户积分还能反向触发优惠券发放……典型的“牵一发而动全身”。

最要命的是,每次上线都得全量发布。上周五产品经理临时改了个“满 199 减 20”的规则,结果测试发现支付回调出问题了,整个系统回滚,运维同事差点把键盘砸了。

而且性能瓶颈越来越明显。高峰期一来,数据库连接池直接打满,JVM 频繁 Full GC。我们试过分库分表,但业务耦合太深,改起来像在雷区里跳舞。


拆!但怎么拆?

微服务不是简单地把代码切成几块就完事了。我翻遍了《微服务设计模式》、看了 N 多 B 站教程,甚至去 GitHub 扒了几个大厂开源项目的结构,最后定了个原则:按业务边界垂直拆分,先粗后细

我们画了张领域模型图(其实就是白板上涂涂改改一整天),最终划分出四个核心服务:

  • user-service:用户注册、登录、个人信息
  • product-service:商品管理、分类、搜索
  • order-service:下单、支付状态、订单查询
  • inventory-service:库存扣减、回滚、预警

📌 面试题来了:微服务拆分依据是什么?
别背八股文!真实答案是:看团队协作成本 + 业务变更频率。如果两个模块总是被不同小组同时修改,那就该拆!

拆分过程中最头疼的是数据一致性。比如下单要扣库存,以前在一个事务里搞定,现在跨服务了怎么办?

我们试过:

  • 同步 HTTP 调用:简单但容易雪崩(库存服务挂了,订单全失败)
  • 本地消息表:可靠但代码侵入性强
  • 最终选了 RabbitMQ + 幂等消费:订单创建后发消息给库存,库存服务幂等处理(用订单 ID 做唯一键防重)
// 库存服务中的幂等扣减逻辑
@Transactional
public void deductStock(String orderId, Long productId, Integer quantity) {
    if (deductionRecordExists(orderId)) {
        return; // 已处理,直接返回
    }
    
    int updated = inventoryMapper.deduct(productId, quantity);
    if (updated == 0) {
        throw new InsufficientStockException();
    }
    
    recordDeduction(orderId, productId, quantity); // 记录已处理
}

通信:别让服务变“孤岛”

拆完之后,服务之间怎么对话?REST 还是 RPC?

我们内部吵了一架。后端老哥觉得 gRPC 性能高,前端出身的产品经理非要 REST(方便他用 Postman 调)。最后折中:内部服务用 Feign(基于 HTTP/JSON),高性能场景再考虑 gRPC

Feign 配合 Ribbon + Hystrix,基本能满足需求:

# order-service 的 feign 配置
feign:
  client:
    config:
      inventory-service:
        connectTimeout: 2000
        readTimeout: 5000
  hystrix:
    enabled: true

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000

但很快遇到新问题:链路追踪。用户投诉“下单失败”,你总不能一个个服务查日志吧?

于是上了 SkyWalking。接入超简单,加个 agent 启动参数就行:

-javaagent:/opt/skywalking-agent/skywalking-agent.jar
-Dskywalking.agent.service_name=order-service

现在在 UI 上点一下就能看到完整调用链,连哪个 SQL 慢都标得清清楚楚。运维同事终于不用半夜打电话问我“是不是你写的代码又慢了?”(虽然大部分时候确实是我写的 😅)


数据库:每个服务一个库,真香!

拆库是微服务的关键一步。我们给每个服务配了独立的 MySQL 实例(其实都是同一个 RDS 下的不同 database,省钱嘛,北漂懂的)。

服务 数据库名 主要表
user-service db_user users, user_profiles
product-service db_product products, categories
order-service db_order orders, order_items
inventory-service db_inventory stocks, deduction_logs

好处立竿见影

  • 发布互不影响:改用户模块再也不怕搞崩订单
  • 容量规划清晰:订单库 IO 高?单独给它升配
  • 团队自治:前端组只对接 product-service,不用懂库存逻辑

当然,跨库查询成了新痛点。比如“查看我的订单及商品信息”,以前一条 JOIN 就搞定,现在得先查订单,再批量查商品。

解决方案:

  • 冗余字段:订单表里存商品名称、图片 URL(牺牲一致性换性能)
  • 聚合服务:新建一个 order-composite-service,专门组装数据
  • ES 同步:关键字段同步到 Elasticsearch,支持复杂查询

💡 血泪教训:别为了“纯粹”拒绝冗余!微服务里,读写分离 + 最终一致才是王道。


算法?别笑,微服务里真用得上!

很多人觉得算法和架构无关,但在分布式系统里,算法决定上限

举个例子:库存扣减的并发控制。

最初我们用数据库行锁:

UPDATE stocks SET quantity = quantity - 1 WHERE product_id = 123 AND quantity > 0;

结果大促时数据库 CPU 100%,因为大量请求在等锁。

后来改用 Redis + Lua 脚本实现原子扣减:

-- stock_deduct.lua
local stock = redis.call('GET', KEYS[1])
if tonumber(stock) >= tonumber(ARGV[1]) then
    redis.call('DECRBY', KEYS[1], ARGV[1])
    return 1
end
return 0

Java 调用:

String script = getResourceAsString("stock_deduct.lua");
Boolean success = redisTemplate.execute(
    new DefaultRedisScript<>(script, Boolean.class),
    Collections.singletonList("stock:" + productId),
    String.valueOf(quantity)
);

性能直接起飞!TPS 从 300 提升到 5000+。这时候才明白,面试官问你“手写一个分布式锁”,真不是闲得慌


运维:没有 DevOps 的微服务都是耍流氓

拆成 10 个服务后,手动部署?做梦!

我们搭了简易 CI/CD 流水线:

  1. GitLab 提交代码 → 触发 Jenkins
  2. Jenkins 打包 Docker 镜像 → 推到 Harbor
  3. Ansible 自动滚动更新 K8s Deployment

配置文件也统一用 Nacos 管理:

# order-service-dev.properties
db.url=jdbc:mysql://xxx:3306/db_order
rabbitmq.host=rabbit-prod.company.com

服务发现靠 Spring Cloud LoadBalancer,不用再维护一堆 IP 列表。新服务上线?注册到 Nacos 就自动被发现。

最爽的是配置热更新。上周运营说“把优惠券门槛从 199 改成 188”,我改个 Nacos 配置,服务自动刷新,全程不用重启!


效果如何?值不值得?

改造花了三个月,中间熬了无数个夜,踩了无数个坑(比如忘了处理分布式事务导致超卖,被老板叫去喝茶)。

但结果是值得的:

  • 系统可用性从 99.2% → 99.95%
  • 单次发布影响范围缩小 80%
  • 团队并行开发效率翻倍
  • 我的简历上多了“微服务架构”关键词(跳槽面试已收到 3 个 offer,薪资+30%)

更重要的是,心里不慌了。以前半夜报警,第一反应是“完了又要通宵”,现在看一眼 SkyWalking,定位到具体服务,该谁值班谁上。


给想转型的兄弟几点建议

  1. 别追求一步到位:先拆核心链路,边缘功能慢慢来
  2. 监控必须先行:没链路追踪的微服务,等于蒙眼开车
  3. 自动化是生命线:手动部署超过 3 个服务,你会崩溃
  4. 接受不完美:微服务不是银弹,它解决协作问题,但带来运维复杂度

最后说句实在话:作为背着房贷的北漂,我搞微服务不是为了技术理想,而是让自己在职场更有议价权。技术是手段,不是目的。

如果你也在经历类似的重构,欢迎留言交流(或者一起吐槽产品经理)。下期我打算写《微服务压测实战:如何扛住双11流量》,记得关注!

对了,刚收到消息,下个月房租又要涨……看来今晚还得再肝一会儿。

评论 0

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