微服务架构设计实战:从单体到分布式 —— 我在一次系统重构中的真实经历
引言:为什么我决定踏上微服务之路?

我叫老张,是一名后端开发出身的技术负责人,目前负责某电商平台的系统架构。2021年底,我们遇到了一个典型问题:原本采用单体架构的电商平台,在用户量和业务复杂度逐渐增长之后,系统性能开始下滑,部署成本越来越高,代码维护也越来越困难。
那时候我们的系统是Spring Boot + MyBatis搭建的一个巨石应用,前端Vue.js通过HTTP API与后端交互。最开始一切看起来都挺正常,随着促销活动、新功能不断上线,问题逐步暴露出来:
- 每次发版都提心吊胆,一个小改动都可能导致整个系统出错;
- 数据库压力越来越大,多个模块共享同一套数据表结构,锁表、慢查询频繁发生;
- 团队协作效率下降,不同小组改同一个模块,代码冲突不断;
- 扩展性差,某些高并发的模块无法单独扩容,必须整体升级服务器。
于是,我们决定启动架构改造计划——把单体应用拆分成基于微服务的分布式系统。今天,我就想结合这段真实的经历,聊一聊我在实际工作中如何做微服务架构设计,以及过程中踩过的坑和收获的经验。
项目背景:从单体到微服务的起点

项目名称是我们自研的“XX商城”,定位是一个中型B2C电商系统,包括商品管理、订单中心、会员系统、优惠券系统、支付对接等核心模块。
原系统的痛点总结如下:
| 模块 | 存在的问题 |
|---|---|
| 商品服务 | 数据量大、读多写少,经常成为瓶颈 |
| 订单中心 | 高并发下单时响应延迟明显 |
| 用户中心 | 和其他模块耦合严重,修改风险高 |
| 优惠券系统 | 规则复杂,逻辑嵌套多,难以维护 |
| 系统整体 | 每次发布都要全量重启,灰度发布几乎不可行 |
于是我们在22年初成立了一个专项组,目标是在半年内完成向微服务架构的迁移,同时保持线上服务平稳运行。
挑战来了:拆分不是简单的事情
1. 划分服务边界是个难题
一开始,我们尝试按照传统的“垂直划分”方式来拆分服务,比如商品服务、订单服务、用户服务等等。但很快发现一个问题:有些数据或逻辑存在跨服务调用需求,怎么处理?
例如用户在下单的时候,需要检查库存是否足够、验证优惠券是否可用、扣减用户积分等多个操作,这些分别属于不同的服务。如果使用远程调用串联,可能会引入分布式事务一致性问题。
举个例子:订单服务调用了库存服务进行库存扣减,结果在后续调用优惠券服务时失败了怎么办?是不是要回滚前面的操作?这在没有合适机制的情况下非常容易出问题。
所以,我们在最初阶段就意识到:服务边界的划分不仅要看业务模块,更要考虑数据归属和一致性要求。
2. 技术选型也头疼
我们要从零构建微服务架构,面临的第一个选择就是框架和组件:
- 使用 Spring Cloud 还是 Dubbo?
- 注册中心用 Eureka、Zookeeper 还是 Nacos?
- 分布式配置中心用 Config 还是 Apollo?
- 如何实现服务治理(负载均衡、熔断限流、降级)?
- 是否需要网关统一接入?
- 如何保证日志追踪、链路监控?
我们当时做了不少技术调研和内部对比,最后选择了以下几个组合:
| 技术栈 | 选型理由 |
|---|---|
| Spring Cloud Alibaba | 更适合国内场景,对阿里生态支持好 |
| Nacos | 注册中心+配置中心一体化,易于集成 |
| Sentinel | 流量控制和熔断能力强,中文文档友好 |
| Gateway | 统一入口,路由、鉴权集中处理 |
| Sleuth + Zipkin | 实现完整的分布式链路追踪 |
| RocketMQ | 解耦异步消息队列 |
这套技术栈虽然成熟,但在实际落地过程中也有不少磕碰,后面我会详细说。
设计思路:从“粗粒度”拆分开始,逐步细化
我们并没有一开始就追求所谓的“完美领域驱动设计”(DDD),而是采用了相对保守的方式:
先按照主实体划分服务边界
- 商品服务:负责商品基本信息、库存、SKU等;
- 订单服务:订单创建、状态流转、退货退款;
- 用户服务:用户信息、地址管理;
- 营销服务:优惠券、满减规则、折扣策略;
- 支付服务:对接第三方支付平台、异步通知处理;
设计服务之间的接口通信方式
- 同步调用用 Feign + OpenFeign;
- 异步解耦用 RocketMQ;
- 跨域权限认证用 OAuth2 + JWT;
- 所有请求走 Gateway 做统一认证和流量控制;
数据库设计方面严格隔离
- 每个服务拥有独立的数据库实例;
- 表结构只暴露给内部访问,对外通过接口提供;
- 对于强一致性的数据,如库存,我们引入了本地事务 + 半消息机制确保最终一致;
基础设施同步搭建
- 使用 Docker 容器化部署;
- 每个微服务对应一个独立的 Jenkins 构建任务;
- Kubernetes 编排实现滚动发布;
- 监控方面集成了 Prometheus + Grafana;
代码实践:部分关键代码片段分享
这里展示几个我们在实际开发中常用的代码示例,供参考。
1. Feign 接口定义(用户服务调用商品服务)
@FeignClient(name = "product-service", fallback = ProductServiceFallback.class)
public interface ProductServiceClient {
@GetMapping("/products/{id}")
ResponseEntity<ProductDTO> getProductById(@PathVariable("id") Long productId);
}
配合fallback类:
@Component
public class ProductServiceFallback implements ProductServiceClient {
@Override
public ResponseEntity<ProductDTO> getProductById(Long productId) {
// 返回默认产品信息或抛出异常
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(null);
}
}
这样即使商品服务暂时不可用,订单服务也能快速失败或降级,避免雪崩效应。
2. 使用 Sentinel 实现限流熔断
我们在Gateway层集成Sentinel限流插件,对每个接口设定QPS阈值,并设置自动降级策略:
spring:
cloud:
sentinel:
datasource:
ds1:
nacos:
server-addr: nacos:8848
dataId: gateway-rules.json
groupId: DEFAULT_GROUP
data-type: json
对应的JSON限流规则如下:
[
{
"resource": "/order/create",
"limitApp": "default",
"grade": 1,
"count": 200,
"strategy": 0,
"controlBehavior": 0
}
]
以上表示对/order/create接口设置每秒最多200个请求,超过自动限流。
踩坑经验:那些年我们一起掉进的陷阱
1. 微服务拆得太细反而拖累效率
我们在初期有个误区:认为拆得越细越好,后来才发现很多小服务之间相互依赖严重,反而让接口数量暴增,调用关系混乱,甚至出现“循环调用”的问题。
最终解决方案是:合并一些低耦合的服务,适当放宽边界,优先解决高频调用的痛点。
2. 忽视数据一致性带来的问题
有一次在订单创建流程中,我们漏掉了对优惠券扣减失败的回滚,导致出现了优惠券被错误发放的情况。后来我们引入了一种本地事务+消息确认的方式来兜底:
- 在优惠券服务中记录“已使用”状态;
- 发送一条确认消息到MQ;
- 如果下游失败,根据消息重试或人工补偿。
类似这样的机制帮助我们大幅减少了线上故障率。
3. 日志追踪缺失导致排查困难
刚上线那会儿,我们遇到一个接口报500,但不知道是哪个服务出了问题,只能一个一个查日志。后来我们引入了Sleuth + Zipkin,所有调用链上都有trace id,极大提高了问题定位效率。
改造后的效果和收益
经过几个月的努力,我们完成了核心模块的微服务化重构,并逐步将流量切换过去。效果如下:
| 指标 | 改造前 | 改造后 |
|---|---|---|
| 单节点吞吐能力 | 600 QPS | 提升至 1200 QPS |
| 发布稳定性 | 30% 的版本有回滚 | 降至 5% 左右 |
| 故障隔离能力 | 全站受影响 | 局部故障不影响全局 |
| 开发协同效率 | 多人改同一工程易冲突 | 各自独立开发提升30% |

更关键的是,我们实现了灵活的弹性扩缩容:高峰时段可以单独扩增订单服务和商品服务,平时节省资源投入。
经验分享:送给正在转型微服务的你
如果你也在考虑微服务架构,以下几点是我的亲身建议:
✅ 1. 不要为了拆而拆
微服务不是万能药,它适用于业务逻辑复杂、团队规模较大、有长期维护预期的项目。如果是中小型项目,或者团队没有相关经验,不要盲目拆分。
✅ 2. 服务边界设计比技术选型更重要
很多时候我们把过多精力放在“用哪个框架”,却忽略了更重要的:服务职责划分是否合理。记住:好的服务边界应该满足“高内聚、低耦合”。
✅ 3. 一定要提前规划运维体系
微服务带来的不仅仅是代码的变化,还有运维、部署、监控、日志收集等一系列挑战。建议一开始就搭建一套自动化CI/CD流水线,否则后期维护成本极高。
✅ 4. 拿捏好“同步”和“异步”的比例
并不是所有调用都需要远程调用。对于非实时性要求高的操作,尽量通过消息队列解耦,既能提高系统稳定性,又能减少网络开销。
✅ 5. 分布式事务是个坎,别硬上
除非你真的需要强一致性,否则推荐使用“最终一致”的方案,如本地事务+补偿、TCC、Saga模式,或者干脆设计成幂等接口+异步对账。
结语:这条路值得走下去吗?
回头看这一年多的时间,确实不容易。我们经历了从焦虑到迷茫,再到信心满满的转变。微服务架构带来了更高的灵活性和可扩展性,但也伴随着更高的学习成本和运维复杂度。
如果你问我:“你现在还会选择微服务吗?”我的答案是:
“如果业务需要,当然会。不过前提是要做好准备,不要低估它的复杂性。”
希望这篇文章能帮你少走些弯路,少踩点坑。如果你有类似经历,也欢迎留言交流,一起成长。
作者简介:老张,十年后端开发老兵,经历过千万级系统架构演进,喜欢研究架构、性能优化和新技术实践。公众号【程序员老张】持续分享一线实战经验。

评论 0