用 Spring Cloud Alibaba 撸一套稳定、可扩展的微服务架构
背景与起因:为什么我们会选择 Spring Cloud Alibaba?
事情要从去年说起。我们公司在那会儿正处于业务快速增长阶段,原来基于单体架构的系统已经明显力不从心了。响应变慢、功能迭代困难、部署风险大等一系列问题接踵而来。于是我们决定进行一次全面的技术重构,目标是将原来的系统拆分为多个微服务,并引入更成熟的分布式治理方案。
在调研主流技术栈的时候,Spring Cloud 的确是一个不错的选择,但随着深入分析我们的需求和团队情况,我们也发现一些痛点:
- Spring Cloud Netflix(比如 Zuul、Ribbon)部分组件更新慢甚至进入维护状态;
- 需要在国内环境下良好支持国产云生态(例如阿里云);
- 希望集成消息队列、配置中心等常见基础设施时能有开箱即用的体验。
这时候我们就把目光转向了 Spring Cloud Alibaba(以下简称 SC Alibaba),这个由阿里开源并持续维护的微服务生态项目。
项目背景简述


这次我们要重构的是一个电商中台系统,涉及商品管理、库存、订单、促销等多个核心模块。系统需要支持高并发、快速上线能力以及良好的监控运维手段。
我们的目标很明确:
- 实现服务之间的解耦与独立部署;
- 提升整体系统的稳定性;
- 构建一个可持续演进的基础平台。
整个项目大约由6个主要服务组成,每个服务采用 Spring Boot 构建,配合 Nacos 作为注册中心和服务配置中心,Dubbo + Feign 两种方式做服务间通信,Sentinel 控制流量降级熔断,Seata 处理分布式事务。
可以说,我们几乎使用到了 SC Alibaba 的“全家桶”。
遇到的挑战与问题

尽管 Spring Cloud Alibaba 文档齐全,社区活跃,但在生产实践中我们还是遇到了不少坑。下面我重点讲几个最影响项目推进的关键问题。
1. 注册中心选型之困:Eureka?Consul?还是 Nacos?
我们在早期尝试过 Eureka,结果发现它的延迟较高,尤其在集群节点变更时,服务列表更新不及时,容易导致调用失败;Consul 性能不错,但安装部署复杂,对于我们想迅速上线的目标来说略显拖沓。
最后选定 Nacos,不仅因为它可以同时满足注册中心 + 配置中心的能力,而且和 Dubbo 亲和性特别好。更重要的是,它在国内的应用非常广泛,文档丰富,社区活跃。
不过刚上手时,Nacos 的一些机制不太直观,比如健康检查、临时实例 vs 持久化实例的区别,都让我们走了点弯路。
2. 服务间通信的抉择:Feign 还是 Dubbo?
刚开始我们统一用了 Feign 做远程调用,毕竟它是 Spring Cloud 的标准做法,写法简单、集成方便。但随着业务复杂度提升和调用量增大,出现了两个比较明显的问题:
- 性能瓶颈:Feign 底层依赖 HTTP 协议,在频繁的内部调用中会带来一定延迟;
- 协议约束:HTTP 接口定义不够严格,服务提供方与消费方容易因为参数格式问题出错。
后来我们做了折中处理:关键服务之间采用 Dubbo + Protobuf 的 RPC 调用模式,非核心、外部调用继续保留 Feign,这样既保证了核心路径的性能,也保留了开发上的灵活性。
3. 分布式事务难题:如何处理跨服务操作?
举个例子:当用户下单的时候,订单服务要创建订单,同时库存服务需要扣减库存,这两个操作必须要么一起成功,要么一起回滚。
我们一开始尝试的是本地事务+消息最终一致性,但实现起来太复杂,尤其是在网络异常或服务宕机的情况下,经常会出现数据不一致的问题。
后来引入了 Seata,通过 AT 模式实现了对数据库的全局事务控制。虽然初期搭建 Seata Server 有一些配置细节需要注意,但一旦跑通之后,代码侵入性并不大,对开发人员比较友好。
4. 熔断限流怎么做?Hystrix 不再推荐!
我们知道 Hystrix 已经不再更新,而 Spring Cloud Gateway 自带的限流也不是那么灵活。这个时候我们选择了 Sentinel,这是阿里巴巴出品的一款轻量级的限流降级中间件,支持 QPS 限制、线程隔离、热点防护等功能。
但真正开始集成 Sentinel 到生产的时候,我们才发现:
- 默认的规则保存在内存里,重启后就没了;
- 生产环境下必须结合持久化 + 动态推送机制;
- DashBoard 只是辅助工具,不能直接用于动态下发规则。
我们最终的解决方案是:自己搭了一套基于 Nacos 的规则同步逻辑,配合 Sentinel Dashboard 来做实时查看,从而达到了动态配置 + 持久化的双重能力。
我们的解决方案设计

基于上面提到的各种挑战,我们的架构大致如下图所示:
┌──────────┐ ┌─────────────┐
│ 用户请求 │──────▶│ API Gateway │
└──────────┘ └─────────────┘
│
┌────────────────────┴────────────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────────┐ ┌────────────┐
│ 订单服务 │────▶│ 商品服务 │────▶│ 库存服务 │
└─────────┘ └─────────────┘ └────────────┘
│ │
▼ ▼
┌────────────┐ ┌─────────────┐
│ 支付服务 │────▶│ 优惠券服务 │
└────────────┘ └─────────────┘
整个体系围绕着以下几大核心组件构建:
| 组件名 | 功能 |
|---|---|
| Nacos | 服务注册中心 + 配置中心 |
| Sentinel | 流量控制、降级、热点防护 |
| Seata | 分布式事务协调器 |
| Dubbo / Feign | 服务间通信 |
| RocketMQ / Kafka | 异步消息通知 |
此外,所有服务的日志统一接入 ELK,链路追踪则采用 SkyWalking,加上 Grafana + Prometheus 做监控告警,整个体系才算闭环。
核心代码实践示例
下面分享一些真实项目中的代码和配置片段,帮助你了解如何快速搭建这些组件。
1. Nacos 注册中心配置(application.yml)
server:
port: 8080
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: nacos-server:8848
config:
server-addr: nacos-server:8848
file-extension: yaml
namespace: your-namespace-id # 如果开启了命名空间
extension-configs:
- data-id: common.yaml
group: DEFAULT_GROUP
refresh: true
2. Sentinel 熔断配置(Java 配置类)
@Configuration
public class SentinelConfig {
@PostConstruct
public void init() {
// 为某个资源设置默认降级策略
DegradeRuleManager.loadRules(Collections.singletonList(
new DegradeRule("order-create")
.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO)
.setCount(0.5) // 错误率超过50%就熔断
.setTimeWindow(30) // 熔断时间窗口30秒
));
}
}
3. Seata 全局事务入口(Controller 示例)
@RestController
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
@PostMapping("/order")
@GlobalTransactional // 开启全局事务
public Result createOrder(@RequestBody CreateOrderDTO dto) {
return Result.success(orderService.create(dto));
}
}
注意:Seata 要求数据库表加行锁支持,且 undo_log 表结构要手动创建。
4. Dubbo RPC 接口定义
// 接口定义
@DubboService
public class InventoryServiceImpl implements InventoryService {
@Override
public boolean deductStock(String productId, int quantity) {
// 扣减逻辑
}
}
// 消费方调用
@DubboReference
private InventoryService inventoryService;
踩过的坑 & 经验总结

这里我想分享一下那些让我熬夜加班的小坑,希望你能少走些弯路。
1. Nacos 本地缓存失效引发服务不可达
有一次发布新版本服务后,旧版本的服务被下架了,但新的服务启动得很慢,导致网关还在尝试调用旧服务,结果出现大量超时错误。
后来排查发现是因为 Nacos 客户端开启了本地缓存(出于性能考虑),但未开启自动刷新,这就导致客户端没有及时感知到服务变动。
解决办法是:
spring.cloud.nacos.discovery.
ephemeral: true # 临时实例,便于故障清理
metadata:
preservestaticfilecache: false # 关闭静态缓存
同时建议在部署脚本里加入等待探针健康的逻辑,避免服务启动完成前就被注册。
2. Dubbo 启动报 “No provider available” 问题
这个问题在我刚迁移到 Dubbo 的时候出现得特别多,原因其实很简单——消费者比提供者先启动了,无法发现服务。
为了避免这种问题,我们做了两件事:
- 在 Kubernetes 中为 Dubbo 服务设置 readiness probe,确保服务完全初始化后再开放流量;
- 使用 dubbo.application.name 做服务分组管理,避免混淆不同环境的服务。
3. Sentinel 规则未持久化导致重启丢失
早期测试环境中我们只是通过 Sentinel Dashboard 添加了几个规则,没做持久化存储,结果每次服务重启规则都没了。
后来我们采用了一个基于 Nacos 的 Sentinel Rule Push 方案:
- Sentinel DashBoard 改造成可以从 Nacos 读取配置;
- 所有规则保存在 Nacos DataID 中;
- Sentinel 客户端监听该 DataID,实现规则热加载。
这对我们后续的灰度发布和弹性伸缩也非常有用。
最终效果与收益
自从这套架构上线以来,我们的整体系统表现稳定多了,尤其是在双十一这类大促期间也没有出现重大故障。
具体收益如下:
| 维度 | 改善 |
|---|---|
| 平均响应时间 | 从 800ms 降低至 300ms 内 |
| 服务可扩展性 | 可随时横向扩容新服务副本 |
| 上线速度 | 模块化后,功能上线周期缩短 50% |
| 故障隔离 | 出现问题基本定位明确,影响范围可控 |
| 运维效率 | 基于 Sentinel+Nacos 的配置中心节省了大量重复工作 |
| 团队协作 | 接口边界清晰,联调对接更顺畅 |
给新手的一些建议
如果你也打算用 Spring Cloud Alibaba 做架构设计,下面几点建议或许对你有帮助:
✅ 把 Nacos 当成标配来用
无论是注册中心还是配置中心,Nacos 都提供了极强的易用性,值得投入精力去掌握。特别是配置中心 + 动态刷新的功能,是未来服务自治的重要组成部分。
✅ 微服务通信方式要合理选用
- 小规模、轻量级交互优先用 Feign;
- 高并发、低延迟场景建议上 Dubbo;
- 对接口强类型要求的可以用 Protobuf + Dubbo;
- 外部调用建议保留 RESTful,兼容 OpenAPI。
✅ 别急着上 Seata,除非真的需要
Seata 的确强大,但也增加了不少复杂度。如果你的业务可以通过本地事务 + MQ 做最终一致性,就没必要一开始就搞分布式事务。
✅ 监控、日志、链路跟踪要尽早规划
别等到系统出问题再去补这些东西。越早整合 SkyWalking、Prometheus 和 ELK,越早发现问题、优化性能。
✅ 结合 Kubernetes 做容器化部署更有优势
SCA 的各个组件天然适配 K8s,你可以用 Operator 方式部署 Nacos、Sentinel Dashboard,甚至 Seata Server,大大简化了部署流程。
写在最后:关于技术选型的感悟
说实话,Spring Cloud Alibaba 并不是银弹,它只是一个工具集合。真正决定成败的,是我们如何理解业务、规划架构、设计服务边界、保障线上运行。
记得项目上线前夜,我和同事盯着监控面板反复刷屏看 QPS 和成功率,心里七上八下的。那一刻才深刻体会到,“稳定压倒一切”,不是一句口号,而是实打实的经验教训。
希望这篇实战经验文章能帮你在踏上微服务之路前,看清一些可能的坑,找到一些合适的答案。也希望我们能在未来的项目中共同成长!
欢迎留言交流你的微服务实践心得,一起进步!

评论 0