Spring Cloud Alibaba 生产实践:一次真实项目的演进之路
大家好,我是后端开发工程师小林。今天我想和大家分享一下我在一个实际项目中使用 Spring Cloud Alibaba 的一些经验。这篇文章不会堆砌一堆技术术语,而是用我自己的语言,讲清楚我们团队在实际开发过程中遇到的问题、选型的考量、踩过的坑以及后来的优化思路。
整个故事发生在一个电商平台的重构项目中,目标是把原本单体架构的系统拆分为微服务架构,提升系统的可维护性、扩展性和稳定性。Spring Cloud Alibaba(简称 SCA)在这个过程中起到了至关重要的作用。接下来我会以第一人称的视角,从背景、问题出发,讲讲我们的实践过程和技术决策。
一、背景介绍:从单体到微服务的必然选择

我们原来的系统是一个基于 Spring Boot 的单体应用,所有的业务模块都集中在一个代码库中。随着用户量和订单量的增长,系统开始暴露出一系列问题:
- 接口响应慢,尤其是在促销活动期间
- 发布频繁导致部署不稳定
- 新功能上线需要整套测试流程,效率低下
- 某个模块出错会影响整个系统运行
这些问题让我们意识到,必须进行微服务化改造了。
选型方面,由于公司内部很多老项目已经使用了阿里云相关的产品,所以自然而然地选择了 Spring Cloud Alibaba。这套框架在当时(2021年)已经具备比较完善的生态组件,比如 Nacos、Sentinel、Seata 等,能够很好地支撑我们的微服务改造需求。
二、项目结构设计:分而治之,逐步迁移

整个项目我们采用的是 渐进式拆分策略。首先将核心模块按业务划分成多个独立的服务,主要包括:
- 用户中心(user-service)
- 商品中心(product-service)
- 订单中心(order-service)
- 支付中心(payment-service)
- 库存中心(inventory-service)
每个服务都是一个标准的 Spring Boot + MyBatis Plus 项目,通过 Spring Cloud Alibaba 的能力实现服务发现、配置管理、限流降级等功能。
一开始我们也尝试过一次性全部拆解,结果反而让集成测试变得异常复杂。于是我们改为先抽离出用户服务和商品服务,再逐步接入其他模块。
小插曲:有一次我们在做服务注册的时候,本地启动两个 user-service 实例时,发现其中一个怎么也注册不到 Nacos 上。最后排查发现是因为两者的
spring.application.name写错了,导致被识别为不同的服务。这个细节虽然简单,但提醒我们要特别注意配置的一致性。
三、服务注册与发现:Nacos 扛起重任
我们选用 Nacos 作为服务注册与配置中心,它不仅支持服务的注册发现,还能统一管理各个服务的配置文件。
刚开始的时候,我们只是简单地使用 Nacos 做服务注册。后来随着服务数量增加,配置项越来越多,我们就逐渐引入了它的配置中心功能。
举个例子,在支付服务中,我们有一个第三方支付平台的回调地址,会根据不同环境配置不同域名。以前这些信息硬编码在代码里,现在我们把它写入 Nacos 配置文件,并通过 @Value 和 @RefreshScope 实现实时刷新。
# application.yml
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.1.100:8848
config:
server-addr: 192.168.1.100:8848
extension-configs:
- data-id: payment.yaml
group: DEFAULT_GROUP
refresh: true
这样的做法极大提高了配置的灵活性,特别是在压测或者灰度发布的时候,只需要改 Nacos 配置,无需重新打包部署。
四、服务间通信:OpenFeign + Ribbon 的组合拳
微服务之间免不了要调用接口。我们早期尝试使用 RestTemplate 直接调用 URL,结果发现维护成本非常高,特别是当某个服务实例 IP 变动后就会导致调用失败。
后来我们改用 OpenFeign + Ribbon 的方式来实现声明式远程调用,效果显著。
比如从订单服务调用库存服务减库存:
@FeignClient(name = "inventory-service")
public interface InventoryServiceClient {
@PostMapping("/decreaseStock")
ResponseDTO decreaseStock(@RequestParam("productId") Long productId, @RequestParam("quantity") Integer quantity);
}
Feign 会自动结合 Ribbon 做负载均衡,请求会被转发到可用的服务实例上,非常方便。
注意:早期我们没有配置 Feign 的超时时间,默认是 1 秒钟,结果在高峰期经常出现“连接超时”错误。后来我们在配置中加上了:
feign: client: config: default: connectTimeout: 3000 readTimeout: 5000这样才能更好地适应生产环境的网络波动情况。
五、熔断降级:Sentinel 成为关键防护盾
微服务拆分之后,服务间的依赖链变长,调用失败的风险大大增加。如果某一个下游服务不可用,可能会引发“雪崩效应”。
于是我们引入了 Sentinel,用于做接口级别的限流、熔断和降级。
比如说,用户中心对外暴露了一个 /getUserById 接口,在高并发时可能会因为数据库瓶颈导致响应延迟变大。这时候我们可以给这个接口设置一个 QPS 阈值,超过后直接返回兜底数据或提示语。
@GetMapping("/getUserById")
@SentinelResource(value = "getUserById", fallback = "fallbackGetUser")
public UserDTO getUserById(@RequestParam Long id) {
return userService.getUserById(id);
}
// 降级方法
public UserDTO fallbackGetUser(Long id, BlockException ex) {
log.warn("用户查询接口被降级");
return new UserDTO();
}
Sentinel 的控制台也提供了非常直观的监控面板,可以实时查看每个资源的调用次数、QPS、异常数等指标。
经验总结:不要等到系统出问题再去考虑限流,要提前埋点,预留降级逻辑。不然一旦出事,很难快速止损。
六、分布式事务:Seata 解决一致性难题
在订单创建的流程中,涉及到订单服务、支付服务、库存服务等多个系统的数据变更,如何保证事务一致性是个大问题。
我们最终采用了 Seata 的 AT 模式,它是一种无侵入的分布式事务解决方案。
具体来说,就是在全局事务入口打上 @GlobalTransactional 注解,Seata 会自动为我们生成 Undo Log 和提交/回滚操作。
@Override
@GlobalTransactional
public void createOrder(OrderCreateRequest request) {
// 1. 创建订单
orderService.create(request);
// 2. 减库存
inventoryService.decreaseStock(request.getProductId(), request.getQuantity());
// 3. 扣款
paymentService.deduct(request.getUserId(), request.getTotalAmount());
}
不过需要注意,AT 模式对数据库有要求:
- 必须使用支持 JDBC 的数据库(MySQL 是主流选择)
- 表格需要新增 undo_log 字段
- 必须配合 Seata Server 使用,不能只靠客户端
一开始我们是在测试环境跑单机版的 Seata Server,但在压力测试中发现性能无法支撑高并发场景,后来升级为集群模式,并且搭配 Sentinel 进行限流保护。
小感悟:分布式事务本质还是妥协的艺术,没有万能的方案。有时候为了高性能可能不得不牺牲强一致性,这就需要从业务角度去取舍。
七、运维与监控:别忘了日志和链路追踪
微服务带来的另一个挑战就是日志管理和链路追踪。为了应对这个问题,我们引入了:
- ELK Stack:收集各服务的日志,统一可视化展示
- SkyWalking 或 Zipkin:用来追踪一次请求经过的所有服务,分析调用链耗时
- Prometheus + Grafana:监控服务的健康状态、TPS、错误率等关键指标
这三种工具的配合使用,极大地提升了我们排查线上问题的效率。
比如有一次用户反馈订单付款后状态不更新,我们通过 SkyWalking 查到是某个异步任务卡住了,很快定位到了 Kafka 消费积压的问题。
八、实战中的思考:SCA 并非银弹,合理使用最关键
Spring Cloud Alibaba 确实为我们解决了不少微服务架构下的痛点问题。但我也想提醒大家一点:
不要盲目追求组件数量,适配业务最重要。
有些时候我们会看到别人用了 XX 中间件,就想着自己也要上一套。但往往忽略了当前团队的技术储备和业务规模是否匹配。
举个真实的例子:我们之前也在纠结是不是要用 RocketMQ 做消息队列,但发现其实用 RabbitMQ 已经够用了,而且学习成本更低,于是我们果断放弃了 RocketMQ,节省了很多人力。
九、成果回顾:系统表现更稳定,研发效率更高
自从完成这次架构升级后,我们获得了以下几个明显的好处:
| 方面 | 原始单体系统 | 微服务架构 |
|---|---|---|
| 接口响应速度 | 普遍较慢 | 提升 40% 以上 |
| 系统可用性 | 单点故障风险高 | 容错能力强 |
| 功能迭代速度 | 耦合严重,牵一发动全身 | 按服务独立交付 |
| 日常运维难度 | 定位问题困难 | 有完整监控体系 |
而且更重要的是,团队对于微服务的理解加深了很多,后续新项目几乎都采用了类似的架构模板。
十、建议与经验总结
如果你也在准备或正在经历微服务架构转型,下面是我根据这次项目得出的一些实用建议:
先从小范围试点开始,逐步推进拆分
- 别贪心,一开始就拆太多容易失控
- 把基础组件(如网关、注册中心、配置中心)先行搭建起来
重视服务治理能力,而不是仅仅依赖组件本身
- 比如接口粒度、超时重试、降级策略,都需要业务侧介入设计
文档和沟通比代码更重要
- 微服务天然意味着团队协作增多,一定要建立良好的接口文档和沟通机制
监控体系建设越早越好
- 日志、链路、指标三位一体,是保障稳定性的基石
持续学习和拥抱变化
- 微服务相关的技术更新快,要时刻关注官方文档、社区动态
结语:架构是手段,不是目的
最后,我想说一句:技术永远是为了解决实际问题服务的。我们之所以选择 Spring Cloud Alibaba,是因为它适合我们的业务场景;我们之所以坚持做微服务拆分,是因为它真的带来了生产力的提升。
希望我的这段经历,能对你有所启发。如果你正在用 Spring Cloud Alibaba,欢迎留言交流你们的实战心得。我们一起成长,一起进步!
作者:小林
全栈开发者|5年后端开发经验|热爱架构设计与性能调优
GitHub:github.com/xiaolin-tech
邮箱:xiaolin@example.com

评论 0