一次 Spring Cloud Alibaba 在高并发场景下的实战踩坑记录

一帆风顺
2025-06-23 10:44
阅读 757

引言:为什么要用 Spring Cloud Alibaba?

引言:为什么要用 Spring Cloud Alibaba?

事情得从我们公司一个全新的业务线说起。为了应对即将到来的双十一大促,我们需要搭建一套可快速扩展、具备高可用性的分布式微服务架构系统。当时的后端技术栈主要基于传统的单体架构,随着用户量和功能复杂度的增长,维护成本越来越高。

考虑到公司已经有一些 Java 技术栈的基础,且未来希望在阿里云上有更深度的集成能力,我决定尝试使用 Spring Cloud Alibaba(SCA) 来构建整个系统的微服务框架。这个决定在团队内部引发了不少讨论,毕竟大家都没怎么深入接触过 Nacos、Sentinel 这类组件,但我也清楚,只有真正落地到生产,才能验证技术选型的有效性。

这篇文章想从我的亲身经历出发,分享我们在这次项目中遇到的一些挑战、踩过的坑,以及最终如何一步步优化并成功上线的过程。


项目背景与挑战

项目背景与挑战

我们的目标是为一个电商平台构建订单中心、库存中心、支付中心等核心模块,并实现各服务之间的高效通信、容错处理、限流降级等功能。

项目初期,我们选择了如下的 SCA 组件组合:

  • Nacos:作为注册中心 + 配置中心
  • OpenFeign + LoadBalancer:服务调用 + 负载均衡
  • Sentinel:服务熔断、限流、降级
  • Seata:分布式事务支持
  • RocketMQ:消息队列异步解耦
  • SkyWalking:APM 监控接入

听起来很完美对吧?但在实际开发和部署过程中,我们遇到了一系列问题。


遇到的第一个大坑:服务注册不及时,导致 Feign 调用失败

遇到的第一个大坑:服务注册不及时,导致 Feign 调用失败

场景描述

我们在测试环境中部署了多个服务实例,例如 order-serviceinventory-service。服务启动之后,在 Nacos 控制台可以看到实例信息,但是在通过 OpenFeign 调用的时候,经常会碰到如下异常:

feign.RetryableException: LoadBalancerException message: No instances available for service

分析过程

一开始我们怀疑是网络问题,排查了容器间的连通性、防火墙配置,结果都正常。后来发现 Nacos 的健康检查机制存在一定的延迟,默认的超时时间为 5 秒,而我们的某些服务在初始化阶段需要加载大量数据,启动时间较长。

这意味着服务虽然启动成功,但在完成数据初始化之前就已经被注册到 Nacos 中,其他服务却已经开始试图调用它,结果自然是找不到可用实例。

解决方案

我们在服务的 bootstrap.yml 文件中增加了如下配置:

spring:
  cloud:
    nacos:
      discovery:
        health-check-enabled: true
        metadata:
          wait-time-for-secondary-verification: 10000 # 增加等待时间

同时,在服务启动完成后手动触发健康上报逻辑,确保服务完全就绪后再对外提供服务。

另外,我们也优化了服务初始化逻辑,将一些可以在后台异步执行的操作进行了分离,加快主流程响应速度。

心得

不要低估服务注册到 Nacos 的“准备时间”!尤其是在资源不足或依赖多的情况下,建议适当增加健康检查的等待时间,并优化初始化逻辑。


Sentinel 熔断设置不合理,导致接口大面积降级

问题现象

某天压力测试期间,我们发现某个服务出现了大量请求失败,查看日志后发现 Sentinel 返回了:

Blocked by Sentinel (flow limit)

但我们当时并没有配任何限流规则,为什么会自动限流?!

问题定位

原来是我们在测试机上随便配了一套默认的 Sentinel 流控规则,没有做详细的压测评估,直接扔到了预发布环境。

更糟糕的是,这套规则设置的是“QPS 不超过 20”,但在大流量下,这个值根本不够用。

修改思路

我们立即进入 Sentinel 控制台,清除了之前的默认规则,并根据实际测试结果重新设置了自适应限流策略。这里用到了 Sentinel 的 热点参数限流 功能,针对不同用户 ID 设置不同的限流阈值。

同时,我们也启用了 熔断器(Circuit Breaker),当某个服务出现异常率达到一定比例时,会自动触发降级,防止雪崩效应。

以下是我们在 Sentinel 控制台配置的核心参数示例:

模块 限流类型 阈值 回路时间 降级规则
order-create QPS 100 1s 触发异常率 > 30%

效果提升

经过调整后,系统在后续的压力测试中表现稳定,即使有部分节点出现故障,整体可用率仍然保持在 98% 以上。


Seata 分布式事务踩坑:长事务阻塞资源

问题描述

我们在下单流程中使用了 Seata 的 AT 模式来保证跨服务事务一致性。但在压测中,经常出现数据库连接池耗尽的情况,特别是在高峰期。

日志显示很多 SQL 查询都在等待锁释放。

原因分析

Seata 在 AT 模式下会对数据库进行全局事务控制,每次操作都会插入 undo_log 并锁定相关行。由于我们的订单写入流程涉及到多次服务调用,整个事务周期较长,导致数据库锁迟迟未释放。

优化方法

我们采取了以下措施:

  1. 拆分长事务:将原本一个大事务拆分为多个小事务,每个服务只对自己负责的数据进行更新,配合 RocketMQ 发送事件通知后续服务。
  2. 引入 Saga 模式:对于非强一致性要求的场景,改用 Seata 的 Saga 模式,避免长事务带来的资源锁定问题。
  3. 优化索引与事务粒度:对频繁更新的字段建立合适的索引,减少锁冲突的概率。

此外,我们在每条事务链路上添加了埋点监控,方便后续追踪和性能分析。


RocketMQ 使用不当引起的消费积压问题

API接口文档-1

问题现象

我们使用 RocketMQ 来进行库存扣减的异步通知。但是在某个高峰期,发现库存迟迟未扣除,系统日志不断打印“重试”提示。

查看 RocketMQ 消费者状态,发现消费堆积达到了几千条消息。

根本原因

  1. 消费者处理逻辑过于复杂,每次处理一条消息需要调用多个外部接口。
  2. 消息消费没有开启并行处理,单线程串行消费效率低下。
  3. 未启用消费失败后的告警机制,问题发生后无法第一时间感知。

改进方案

我们将消费者代码进行了重构,主要包括:

@Bean
public RocketMQListener<String> inventoryConsumer() {
    return new RocketMQListener<>() {
        @Override
        public void onMessage(String message) {
            try {
                // 异步处理库存变更逻辑
                inventoryService.processInventory(message);
            } catch (Exception e) {
                log.error("库存处理失败,消息将重试", e);
                throw new RuntimeException("消费失败");
            }
        }

        @Override
        public ConcurrentlyStatus consumeConcurrently(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
            for (MessageExt msg : msgs) {
                this.onMessage(new String(msg.getBody()));
            }
            return ConcurrentlyStatus.CONSUME_SUCCESS;
        }

        @Override
        public MessageModel getMessageModel() {
            return MessageModel.CLUSTERING;
        }
    };
}

并在配置中启用了并行消费:

rocketmq:
  consumer:
    thread-count: 8
    max-retry-times: 3

同时,接入了企业微信报警机器人,一旦消息消费失败次数超过阈值,即可发送告警信息。


SkyWalking 数据采集过多影响性能

性能瓶颈显现

在系统运行一段时间后,我们发现部分服务 CPU 使用率明显上升。一开始怀疑是业务逻辑问题,直到查看 SkyWalking 的拓扑图才发现,SkyWalking 自动采集了很多不必要的方法,比如工具类、日志输出等。

如何优化?

我们对 SkyWalking 的插件目录进行了清理,关闭了不必要的自动探针采集。例如禁用了:

-rm apm-springmvc-plugin-*.jar
-rm apm-okhttp-plugin-*.jar

并在 agent/config/agent.config 中修改了采样频率:

sample=3000 # 每3000个请求采集一次,降低开销
ignore_suffix=.jpg,.css,.js # 忽略静态资源

最终效果

CPU 利用率下降约 20%,GC 压力减轻了不少。


总结:这些经验,值得你记下来

在整个项目的推进过程中,我们经历了从迷茫到熟悉、从踩坑到优化的过程。总结一下几个关键的经验点:

  • 服务注册要留足缓冲时间,特别是涉及数据库初始化的服务。
  • 限流规则必须结合压测数据动态调整,不能拍脑袋设定。
  • 分布式事务不是万金油,要根据业务一致性需求选择是否使用 Seata。
  • 消息中间件的设计要充分考虑消费能力和稳定性,否则很容易成为瓶颈。
  • APM 工具虽然好用,但也可能带来额外负载,记得合理控制采集范围和频率。

最后,我还想强调一点:技术没有银弹,关键是根据业务场景做出合适的选择。


写在最后:Spring Cloud Alibaba 的未来趋势

缓存策略对比-2

如今,Spring Cloud Alibaba 已经成为越来越多企业构建微服务架构的重要选择之一。Nacos 已进入 CNCF,Sentinel 在社区活跃度不断提升,而 Seata 也在逐渐成熟。我相信,只要我们持续关注社区动态,灵活运用各个组件的优势,就能构建出稳定、高效的分布式系统。

如果你正在考虑使用 SCA 或者已经在生产中落地,欢迎留言交流你的经验和想法。一起成长,一起避坑 😄


作者:一位在一线搬砖多年的 Java 工程师,热爱技术也爱写 Bug。

评论 0

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