Spring Cloud Alibaba 生产实践:从架构落地到踩坑突围

栈里有风
2025-06-19 14:33
阅读 262

开篇

开篇

去年底我加入了一个新项目组,目标是为公司搭建一套面向服务的微服务架构系统。彼时正值 Spring Cloud Alibaba 技术栈开始被大规模应用于企业级项目,而我们团队也是第一次尝试将其用于生产环境。在几个月的开发和部署过程中,我们遇到了不少问题:Nacos 服务注册的异常、Seata 分布式事务的使用陷阱、Sentinel 的限流规则配置不生效……这些问题背后往往隐藏着对框架理解的不到位,以及技术选型与业务场景之间磨合的挑战。

今天我想借这篇文章分享一下我们团队在使用 Spring Cloud Alibaba 构建微服务系统过程中的真实经历,包括架构设计、技术选型背后的思考、踩过的一些“深坑”,以及最终收获的稳定性和效率提升。希望这些实战经验能给同样在走这条路的同学一些启发。


项目背景与初始痛点

微服务架构示意图-1

项目背景与初始痛点

我们项目的前身是一个传统单体架构的 Java 应用,所有业务逻辑耦合在一起,随着功能越来越多,代码越来越臃肿,维护成本日益增加。最严重的一次线上事故是因为某个模块修改导致整个系统崩溃,严重影响了运营节奏。

于是我们决定拆分系统为多个独立的服务,并引入 Spring Cloud Alibaba 做为技术主干来支撑微服务治理能力。目标是:

  • 实现服务之间的隔离
  • 提高系统的可维护性
  • 支持灰度发布、服务降级等高级特性
  • 在未来具备支持弹性扩展的能力

初期规划如下:

模块 描述
用户服务 管理用户信息
订单服务 负责下单、支付、状态变更等
商品服务 商品目录与库存管理
网关层 Zuul + Sentinel 统一路由 & 限流控制
注册中心 Nacos 集群
配置中心 Nacos Config Server
分布式事务 Seata TCC 模式
日志/监控 ELK + Promethues + Grafana

听起来是不是很理想?但理想很丰满,现实很骨感。


第一个挑战:服务发现不稳定

项目初期,我们基于 Spring Boot + Spring Cloud 整合 Nacos 作为服务注册与发现中心。本地开发环境没问题,但是在部署到测试环境之后,经常出现服务找不到的情况,例如某次调用 OrderService 报错 LoadBalancerException: No instances available for service...

排查了很久才发现两个关键点:

  1. 心跳机制设置不合理:Nacos 默认的心跳间隔(5 秒)对于我们某些机器性能较弱的节点来说太高频率了,导致部分实例频繁失联。
  2. 负载均衡器未适配:Spring Cloud 中默认使用的是 Ribbon,但在 Spring Cloud Alibaba 下应该优先使用 Nacos 内置的负载均衡策略,否则就会出现服务地址缓存未更新的问题。

解决方案也很简单:

nacos:
  discovery:
    server-addr: 127.0.0.1:8848
    heartbeat-interval: 3000 # 心跳间隔改为 3s,避免心跳丢失

另外在入口处启用 LoadBalancer 并指定正确的客户端:

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
    return new RestTemplate();
}

Tip:如果你用了 OpenFeign,请确保 FeignClient 是通过服务名而非 IP+Port 进行访问。

这一轮调整后,服务发现变得稳定了许多。


第二个挑战:分布式事务 Seata 不稳定

订单服务中存在跨服务的数据一致性需求,比如下单扣库存操作,我们需要保证库存服务扣减成功,订单才能真正生成。于是决定引入 Seata 实现分布式事务。

我们在订单提交接口中使用了 Seata 的 TCC 模式,但在一次压力测试中发现了几个问题:

  • TC(Transaction Coordinator)节点频繁报超时
  • RM(Resource Manager)上报状态失败,事务无法结束
  • 数据库锁竞争严重

后来我们重新梳理了一下流程:

  1. 优化 TM 超时设置

    seata:
      client:
        tm:
          commit-retry-count: 3
          rollback-retry-count: 3
        undo:
          queue-size: 2000
    
  2. 降低事务粒度:将原来一个事务内完成的三个步骤拆分为两段事务,中间使用本地事务加 MQ 异步处理。

  3. 数据库连接池调整:我们之前使用的 HikariCP 最大连接数为 20,在并发较高的情况下导致死锁等待时间变长。后续改为了 50,并增加了 SQL 执行超时限制。

最终通过一系列参数调优和业务逻辑重构,Seata 事务稳定性有了明显提升。


第三个坑:Sentinel 限流失效?

我们的网关服务使用了 Sentinel 对 API 接口进行限流,以防止突发流量击穿下游服务。然而有一次促销活动中,大量请求集中涌入导致部分订单服务直接崩溃。

事后分析发现是规则没有实时同步到各个节点上。我们最初的做法是静态写入 SentinelRule 文件:

[
  {
    "resource": "/api/order/create",
    "count": 100,
    "grade": 1,
    "limitApp": "default"
  }
]

但实际上,这种方式只适用于开发阶段或者规则变化不大的场景。上线后必须采用动态数据源方式加载规则,推荐使用 Nacos 作为 Sentinel 规则存储中心:

spring:
  cloud:
    sentinel:
      datasource:
        ds1:
          nacos:
            server-addr: 127.0.0.1:8848
            data-id: order-service-sentinel-rules
            group: DEFAULT_GROUP

同时,可以在应用中监听配置更新,实现规则热加载。

这个教训让我意识到,很多问题其实不是框架的问题,而是我们对组件的使用方式出了偏差。


性能优化与架构再设计

随着业务规模增大,我们逐渐暴露出了几个性能瓶颈:

1. 网关压力过大

一开始我们使用 Zuul 作为 API 网关,但在压测时发现性能表现一般。经过对比 Netty + Reactor 架构的 Gateway 实现(如 Spring Cloud Gateway),我们决定替换网关为 SCG + Sentinel 组合:

  • 使用 WebFlux 异步非阻塞模型
  • 结合 Sentinel 的 reactive 支持,实现精准限流
  • 利用 RSocket 或者 gRPC 减少传输开销

迁移后的 QPS 提升明显,TP99 控制在毫秒级。

2. 微服务通信延迟高

早期采用了 RESTful + JSON 的方式进行服务间调用,但随着服务数量增多,出现了明显的网络延迟和序列化开销问题。

我们最后选择对核心高频服务间调用切换为 Dubbo 协议 + Protobuf:

  • 优势在于协议更轻量级,调用链路更短
  • 支持异步调用,提高吞吐能力
  • 与 Spring Cloud Alibaba 兼容性良好,无需额外改造

Dubbo 调用示例:

@Reference(version = "1.0.0")
private InventoryService inventoryService;

public void createOrder(OrderDTO dto) {
    boolean success = inventoryService.reduceStock(dto.getProductId(), dto.getQuantity());
    if (!success) throw new RuntimeException("库存不足");
    // ...
}

数据库设计与缓存策略

服务拆分后,数据库自然也拆成了多份,但我们发现有些查询需要关联多个服务的数据。为了避免多次 RPC 调用,我们在以下几个方面做了优化:

  • 引入 Redis 缓存热点数据,例如商品详情页中的评分、销量、价格浮动
  • 使用 Canal 监听 MySQL Binlog,将变更数据异步写入 ES,供搜索模块使用
  • 针对复杂查询建立数据聚合层(Data Service Layer),统一处理跨域数据读取

缓存的使用也带来了新的问题,比如缓存穿透和雪崩。我们为此做了一些补偿策略:

  • 空值缓存空对象(Null Object Pattern)
  • 设置随机 TTL 时间
  • 加入本地 GuavaCache 作为二级缓存,减少外部 Redis 的压力

上线运维:从 CI/CD 到监控报警

微服务一多,运维复杂度也急剧上升。我们逐步搭建了以下基础设施:

  • Jenkins + GitLab 实现自动化构建和部署
  • Kubernetes 管理容器编排,支持灰度发布和回滚
  • Prometheus + Grafana 实时监控 JVM、GC、线程池、请求耗时
  • ELK 收集日志,快速定位故障
  • 基于 AlertManager 实现钉钉告警接入

其中一个印象深刻的运维事件是我们一次升级中误将旧版本的配置推送到生产环境,导致部分服务启动失败。得益于完善的健康检查机制,服务很快被标记为异常,触发熔断降级,才避免了一次大范围故障。


成果与收益

缓存策略对比-2

这套基于 Spring Cloud Alibaba 搭建的微服务体系,经过半年多的打磨和验证,已基本趋于稳定:

方面 改进效果
开发效率 多人并行开发无冲突,迭代速度加快
系统可用性 完成率超过 99.9%,高峰期稳定运行
可维护性 各模块职责清晰,便于定位问题
性能表现 核心接口响应控制在 100ms 以内
拓展性 新增服务仅需 1 小时即可接入现有体系

我的几点建议

结合我们的实践经验,我有以下几点建议,送给正在或将要使用 Spring Cloud Alibaba 的朋友们:

  1. 别一开始就追求“完美架构”,先跑起来,再根据业务演进来不断优化
  2. 组件选型一定要符合业务特征,比如你如果不需要强一致性事务,就别硬上 Seata
  3. 不要低估基础设施的重要性,CI/CD、监控报警、日志收集,哪个都不能少
  4. 保持学习和敬畏之心,微服务本质不是银弹,只是把问题复杂度转移给了开发者自己
  5. 多参与社区讨论,你会发现很多“bug”其实是自己没看文档 😂

写在最后

写这篇文章的时候,我也回想了一遍从初次接触 Spring Cloud Alibaba 到如今熟练掌握的过程。技术本身没有绝对的好坏,只有是否适合当下业务的需要。而作为开发者,最重要的永远是对业务的理解、对工程的负责,以及持续学习的态度。

如果你也在微服务的路上,不妨静下心来,一步步打磨你的系统。它可能不会一下子带来什么质的变化,但当某天你发现服务稳定、团队协作顺畅、运维不再焦虑,你就知道,那条路走对了。

欢迎留言交流,一起成长 🚀

评论 0

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