从“服务雪崩”到稳定交付:Spring Cloud Alibaba 在高并发项目中的实战经验分享

胡红_开发者
2025-06-12 21:41
阅读 674

去年我参与了一个金融类平台的重构项目。这个平台是一个典型的中大型微服务架构,原本采用的是 Spring Cloud Netflix 的技术栈,包括 Eureka、Zuul、Ribbon 等组件。但由于某些原因,公司决定转向 Spring Cloud Alibaba(简称 SCA),并以 Dubbo + Nacos 作为核心框架进行重构。

当时我对 Spring Cloud Alibaba 还没有深入实战经验,只是大致了解它提供的一些能力——比如服务发现、分布式配置管理、限流熔断等。但真正在生产环境使用时,才发现它的优势和坑远比想象中多得多。

这篇文章我会结合项目的实际背景,详细聊聊我们是如何一步步落地 SCA 栈,并在解决“服务雪崩”、“数据库压力大”以及“接口设计混乱”等问题上积累的经验。


项目背景与挑战

项目背景与挑战

我们的系统每天有超过百万级的请求量,涉及用户账户、风控评估、交易流水等多个模块。由于原系统基于 Spring Cloud Netflix 搭建,服务之间调用链复杂、故障传播快,在某个服务出现异常时,很容易引发服务雪崩

更糟糕的是,随着业务扩展,原有的网关 Zuul 成为性能瓶颈,响应延迟经常突破 1 秒以上。同时,配置中心和限流机制也存在不少问题,运维同学对这套体系越来越难以维护。

于是我们决定重构整个后端架构,选择 Spring Cloud Alibaba 作为新一代微服务框架。目标很明确:提高系统的稳定性、可维护性和性能表现


技术选型与整体架构设计

技术选型与整体架构设计

我们并没有一开始就全盘替换原有架构,而是逐步过渡。以下是最终确定的技术栈:

  • 服务注册发现:Nacos(支持 AP 和 CP 切换)
  • RPC 框架:Dubbo 3.x(比 Feign 更适合高频调用场景)
  • 服务网关:Gateway + Nacos 动态路由(替代 Zuul)
  • 限流熔断:Sentinel(内置热点参数限流是亮点)
  • 消息队列:RocketMQ(国产自研,兼容性好)
  • 分布式事务:Seata(用于跨账户转账等关键流程)

此外,我们在数据库方面做了分库分表的设计,采用了 ShardingSphere 来做读写分离与数据路由;缓存则使用了 Redis + Caffeine 双层缓存结构来减轻 DB 压力。

整个架构图大概是这样:

[ Gateway ]
    ↓
[ Nacos 注册中心 ]
    ↙     ↘
[ User Service ]   [ Risk Service ]   [ Trade Service ] ...
    ↓               ↓               ↓
[ Sentinel 限流 ]  [ RocketMQ 异步通信 ]
    ↓
[ Seata 分布式事务控制 ]
    ↓
[ ShardingSphere 分库分表 ]

实战中遇到的问题与解决方案

实战中遇到的问题与解决方案

问题一:服务间调用频繁导致线程阻塞,进而引发雪崩效应

这是最棘手的一个问题。最初我们采用的是 Dubbo 默认的同步调用方式,结果在高峰期一旦下游某台服务器挂掉或延迟高一点,上游服务会大量等待阻塞,线程池被打满,直接崩溃。

解决方案:

我们将部分非核心调用改为异步方式,并配合 RocketMQ 做消息解耦。例如风控评估结果可以通过 MQ 广播给其他系统,而不是每次都等一个返回结果。

另外,我们在服务入口加上了 Sentinel 的限流规则,设置了 QPS 与线程数双维度的限流策略,保障核心路径的可用性。

举个例子:

spring:
  cloud:
    sentinel:
      datasource:
        flow:
          nacos:
            server-addr: nacos-host:8848
            data-id: user-service-flow-rules
            group: DEFAULT_GROUP

然后我们在 Nacos 中配置具体的限流规则 JSON:

[
  {
    "resource": "/api/user/get",
    "limitApp": "default",
    "grade": 1,
    "count": 2000,
    "strategy": 0,
    "controlBehavior": 0
  }
]

通过这种方式,我们实现了动态限流,而且能在控制台实时调整规则而无需重启应用。


问题二:服务注册失败频繁,Nacos 出现抖动

有一次上线过程中,多个节点启动失败,日志显示连接不上 Nacos。排查之后发现是网络分区引起的,再加上客户端重试机制不合理,造成服务注册不及时,后续调用全部失败。

解决方案:

我们统一将 Nacos 客户端升级到了最新版本,并引入了心跳机制优化,设置健康检查频率为 5s,超时时间为 2s,提升感知速度。

同时我们在部署层面采用了 Nacos 集群模式,三节点部署 + MySQL 外部存储元数据,提升了可靠性和容灾能力。

Dubbo 配置如下:

dubbo:
  registry:
    address: nacos://nacos-host1:8848,nacos://nacos-host2:8848,nacos-host3:8848

问题三:接口设计混乱,服务边界模糊

早期很多服务之间的接口没有统一设计规范,造成了接口复用差、参数冗余、文档缺失等一系列问题。

解决方案:

我们引入了 OpenAPI 规范(Swagger + Knife4j),每个服务都必须提供标准化的接口文档,并通过统一网关进行鉴权和路由。所有对外暴露的服务都走 Gateway,禁止绕过网关直连。

此外我们还制定了接口命名规范、异常编码标准,并通过 AOP 实现通用的日志记录和异常拦截:

@Aspect
@Component
@Slf4j
public class RequestLogAspect {

    @Pointcut("execution(* com.example..*.controller.*.*(..))")
    public void requestLog() {}

    @Before("requestLog()")
    public void doBefore(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = 
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        log.info("URL : " + request.getRequestURL().toString());
        log.info("HTTP_METHOD : " + request.getMethod());
        log.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        log.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
    }
}

踩过的几个坑与避坑建议

1. Dubbo 泛化调用性能不如预期

我们曾尝试用泛化调用来实现一些通用逻辑处理,后来发现每次都要构建 Map 参数对象,序列化反序列化开销较大。最终我们还是改成了定义 DTO 对象的方式,虽然开发效率低点,但运行性能更好。

2. Sentinel 不支持 Spring MVC 的注解限流

一开始我们想对 Web 层 Controller 接口直接加 @SentinelResource 注解,但默认只适用于方法级别的资源名,无法自动识别 URL 路径。后来我们通过自定义 Filter + Sentinel Rule API 的方式实现了统一控制。

3. Seata 在高并发下出现死锁问题

Seata 在 TCC 模式下需要手动编写 Confirm 和 Cancel 方法。我们早期在库存扣减逻辑中使用不当,导致在并发下单时出现事务卡死的问题。最后通过对数据库加行锁、优化本地事务提交顺序,才缓解了这一问题。


最终效果与收益总结

经过半年的努力,我们成功完成了整套系统的迁移到 Spring Cloud Alibaba 架构。迁移后的效果非常明显:

指标 改造前 改造后
网关平均 RT 1100ms 400ms
服务间调用失败率 5% ~ 7% <0.5%
全链路成功率 92% 99.6%
日均故障次数 8次 ≤2次

更重要的是,现在的架构具备良好的可观测性可维护性,运维同学可以轻松地通过 Nacos 控制台查看服务状态、Sentinel 查看实时监控指标、Seata 查看事务日志,大大节省了排障时间。


经验总结与建议

如果你也在考虑或者已经开始使用 Spring Cloud Alibaba,以下几点是我踩坑后总结下来的心得:

✅ 架构设计建议

  • 优先使用 Dubbo 替代 Feign,尤其是 RPC 调用密集的场景。
  • 结合 Sentinel 实现动态限流,避免单一 QPS 的限制。
  • 使用 Nacos 做统一配置中心,配置文件要精细化划分。
  • 所有服务都走 Gateway,严禁服务间跳过网关直连。

✅ 性能调优方向

  • 合理设置线程池大小,防止资源耗尽;
  • 使用本地缓存 + Redis 缓存结合,减少 DB 访问;
  • 接口尽量保持幂等,利于限流降级和回滚;
  • 对关键链路进行埋点,方便定位性能瓶颈。

✅ 开发协作建议

  • 统一接口规范,使用 Swagger/Knife4j 自动生成文档;
  • 所有服务命名统一规划,如 user-service、order-service;
  • 异常码统一格式,避免服务间理解歧义;
  • 接口调用统一封装成 SDK,降低依赖耦合。

写在最后:一次架构的进化,不止于代码

回顾整个过程,我深刻体会到:一个好的架构不是一开始就能设计出来的,而是在一次次线上事故、一次次需求迭代中打磨出来的

Spring Cloud Alibaba 提供了一套非常完整的微服务解决方案,但它也像一把刀,怎么使用取决于使用者的经验和水平。

如果你刚接触这个生态,不妨从小项目入手,先掌握 Dubbo + Nacos 的基本用法,再逐步引入 Sentinel、Seata、RocketMQ 等进阶组件。只有真正动手实践过,才能感受到这些工具背后的原理和价值。

希望这篇文章能给你带来一些启发,少走一些弯路。如果你们也有类似的实战经验,欢迎一起交流探讨!


如果你觉得这篇文章对你有所帮助,别忘了点赞、收藏、转发哦~你的支持是我继续写作的最大动力!

评论 0

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