微服务架构设计实战:从单体到分布式
去年双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 流水线:
- GitLab 提交代码 → 触发 Jenkins
- Jenkins 打包 Docker 镜像 → 推到 Harbor
- 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,定位到具体服务,该谁值班谁上。
给想转型的兄弟几点建议
- 别追求一步到位:先拆核心链路,边缘功能慢慢来
- 监控必须先行:没链路追踪的微服务,等于蒙眼开车
- 自动化是生命线:手动部署超过 3 个服务,你会崩溃
- 接受不完美:微服务不是银弹,它解决协作问题,但带来运维复杂度
最后说句实在话:作为背着房贷的北漂,我搞微服务不是为了技术理想,而是让自己在职场更有议价权。技术是手段,不是目的。
如果你也在经历类似的重构,欢迎留言交流(或者一起吐槽产品经理)。下期我打算写《微服务压测实战:如何扛住双11流量》,记得关注!
对了,刚收到消息,下个月房租又要涨……看来今晚还得再肝一会儿。

评论 0