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

创新之云端
2025-12-14 14:58
阅读 764

大家好,我是小张,一个大专毕业的前端“野生选手”。别看我 title 是前端,但自从去年被我们组后端大佬临时拉去“救火”之后,我的技术栈就有点跑偏了——现在写 Vue 的时间还没看 Spring Cloud 多。不过也好,反正我对交互和动画感兴趣,微服务里那些接口响应、状态同步,其实也挺像做动效的:节奏要对,不能卡顿,还得能回退。

说起来,写这篇文章的契机,还是上周五晚上 9 点,产品经理又在群里@全体成员:“双11大促需求提前上线,老系统扛不住,必须拆成微服务!”我当时正在调试一个 CSS 动画的 transform-origin,手一抖差点把咖啡泼键盘上——这都第几次了?每次都是 deadline 逼着人成长。

老系统:一台服务器撑起整个业务

我们原来的系统,是个典型的“巨石应用”(monolith)。前后端混在一起,数据库就一个 MySQL 实例,所有功能模块——用户中心、订单、商品、促销——全塞在一个 Spring Boot 项目里。代码仓库 Git 提交记录都快 5w 行了,每次改个小功能,CI/CD 跑半小时,测试兄弟直接翻白眼。

最离谱的是去年双11,凌晨 2 点,订单服务挂了,结果整个网站都打不开。运维大哥一边重启一边骂:“你们后端能不能别把所有鸡蛋放一个篮子里?”——这话扎心,但属实。

当时我就在想:是不是该拆了?可问题来了:怎么拆?拆完怎么管?服务之间怎么通信?数据一致性咋搞?

技术选型:别光听 PPT,得看落地成本

我们团队总共 6 个人,3 前端 2 后端 + 1 全栈(也就是我这种半吊子)。领导明确说了:“别整那些花里胡哨的,能跑就行,预算有限。”

于是我们开始横向对比主流微服务方案。先说结论:

技术栈 优点 缺点 我们是否采用
Spring Cloud Alibaba (Nacos + Sentinel + Seata) 中文文档全、阿里背书、生态成熟 侵入性强,学习曲线陡 ✅ 最终选择
Dubbo + ZooKeeper 高性能、轻量 配置复杂,社区活跃度不如 Spring Cloud
Kubernetes + Istio 云原生、强大流量控制 运维门槛高,小团队玩不转 ❌(哭着放弃)
Node.js + NestJS + Redis Pub/Sub 前端友好,快速上手 生态弱,事务支持差 ⚠️ 仅用于非核心模块

说实话,一开始我想推 NestJS,毕竟我前端出身,TS 写起来顺手。但被后端老王一句“你敢用 Node 做订单服务,出了问题你背锅?”给怼回来了。行吧,尊重专业。

最后定下 Spring Cloud Alibaba,主要是因为:

  • Nacos 当注册中心 + 配置中心,UI 界面贼清爽
  • Sentinel 做熔断限流,规则动态可调,不用重启
  • Seata 解决分布式事务,虽然配置麻烦点,但至少有方案

而且!阿里云上有现成的托管版,省了自己搭集群的力气——对我们这种没专职运维的小团队,简直是救命稻草。

拆分过程:从“不敢动”到“真香”

第一步:按业务域垂直拆分

我们没搞什么 DDD 领域驱动设计(太重了),而是简单粗暴按功能拆:

  • user-service:用户登录、资料管理
  • product-service:商品查询、库存
  • order-service:下单、支付回调
  • promotion-service:优惠券、满减活动

每个服务独立数据库,禁止跨库 join。这点一开始开发很痛苦,比如查订单详情要同时拉用户昵称、商品图片、优惠信息,得调三个接口。后来我们加了个 BFF 层(Backend For Frontend),由我这个“伪全栈”用 Spring WebFlux 写了个聚合服务,前端只调它,体验好多了。

第二步:解决服务通信

同步调用用 OpenFeign,异步用 RocketMQ。举个例子:用户下单成功后,要发短信、扣库存、更新积分。这些操作不能阻塞主流程,所以 order-service 发一条 MQ 消息,其他服务监听处理。

// order-service 中发送消息
rocketMQTemplate.convertAndSend("order_created", orderDTO);
// user-service 中消费
@RocketMQMessageListener(topic = "order_created", consumerGroup = "user-group")
public class OrderConsumer implements RocketMQListener<OrderDTO> {
    public void onMessage(OrderDTO order) {
        // 更新用户积分
    }
}

但这里踩了个大坑:消息重复消费!因为网络抖动,RocketMQ 可能重试多次。我们一开始没做幂等,结果用户积分被加了三次……后来所有消费者都加了 messageId 去重表,才算稳住。

第三步:分布式事务:Seata 的痛与甜

最头疼的是下单时的“创建订单 + 扣库存”事务。要么都成功,要么都失败。我们用了 Seata 的 AT 模式:

# seata.conf
service.vgroup_mapping.my_tx_group = default
client.undo.log.table = undo_log
@GlobalTransactional
public void createOrder(OrderRequest req) {
    orderMapper.insert(req);
    productClient.decreaseStock(req.getProductId(), req.getCount()); // Feign 调用
}

看起来很美,但第一次压测就崩了:undo_log 表锁表!因为高并发下大量事务回滚,MySQL InnoDB 锁竞争激烈。后来我们做了两件事:

  1. 把 undo_log 表单独建库,避免影响业务表
  2. 对非关键操作(比如发通知)改用最终一致性,不再强依赖 Seata

运维与监控:上线不是终点

微服务拆完,你以为就完了?Too young!

我们很快发现:日志分散、链路追踪难、服务雪崩。于是赶紧补课:

  • 日志:用 ELK(Elasticsearch + Logstash + Kibana),每个服务加 traceId,前端请求头透传
  • 监控:Prometheus + Grafana,看 QPS、错误率、JVM 内存
  • 链路追踪:SkyWalking,一眼看出哪个服务拖慢了整个流程

记得有一次半夜报警,order-service CPU 100%。打开 SkyWalking 一看,是 product-service 接口超时,导致 Feign 默认重试 3 次,线程池被打满。立马在 Nacos 控制台把超时时间从 5s 改成 1s,再加个 Sentinel 规则限流,10 分钟搞定。那一刻,真的觉得微服务值了——单点故障不再波及全局

开发心得 & 给新人的建议

作为一个非科班、靠自学上岸的大专生,这次架构升级让我深刻体会到:

  1. 微服务不是银弹:如果你的业务量还没到单机扛不住,别急着拆。我们拆之前 QPS 才 200,纯属“为了微服务而微服务”,走了不少弯路。
  2. 工具链比代码更重要:Nacos、Sentinel、SkyWalking 这些工具,极大降低了运维成本。建议新人先玩熟这些,再深究原理。
  3. 自动化是生命线:CI/CD、自动化测试、一键回滚,缺一不可。我们现在 Jenkins 流水线跑通了,提交代码自动部署到测试环境,幸福感飙升。
  4. 沟通成本暴涨:以前改个字段,前后端对一下就行;现在得通知 3 个服务 owner。所以接口契约(OpenAPI/Swagger)必须严格维护

学习资源 & 工具推荐

如果你也在折腾微服务,这些资源救过我命:

  • 教程
  • 书籍
    • 《微服务架构设计模式》——理论扎实,但别死磕
    • 《凤凰项目》——小说体,讲 DevOps 文化,超有趣
  • 工具
    • Postman:调试多服务接口必备,记得用 Collection 管理
    • Arthas:线上 Java 诊断神器,watch 命令看参数太香了
    • Draw.io:画架构图,免费又好用

最后:大专生也能搞架构?

经常有人问我:“你大专学历,怎么敢碰架构?”
我说:谁还不是边干边学呢?

两年来,我从只会写页面,到现在能参与系统设计,靠的就是——遇到问题别怂,先 Google,再 Stack Overflow,最后看源码。微服务这东西,听起来高大上,拆开看就是一堆 HTTP 请求 + 消息队列 + 数据库事务。只要你愿意动手,没有搞不定的。

现在每次看到订单系统稳稳扛住流量高峰,心里就特踏实。虽然工资没涨多少(笑),但简历上能写“主导微服务改造”,跳槽底气足多了。

共勉吧,打工人!下次双11,希望我能准时下班,而不是在公司吃泡面 debug。

评论 0

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