用 Spring Cloud Alibaba 打造高可用微服务系统:一次生产实战记录

一帆风顺
2025-06-15 14:42
阅读 393

开篇:为什么我选择了 Spring Cloud Alibaba?

开篇:为什么我选择了 Spring Cloud Alibaba?

大概是在一年前,我们团队接手了一个从单体架构转向微服务架构的项目。当时摆在我们面前的技术选型很多,Spring Cloud Netflix 系列、Dubbo、甚至还有 Service Mesh 方案。但经过几轮讨论和技术验证之后,我们最终决定采用 Spring Cloud Alibaba(后文简称 SCA)作为核心微服务框架。

原因很简单:业务发展快,需要快速搭建稳定的服务治理体系;同时我们也在阿里云生态中做部署,SCA 的集成和兼容性更有优势。更重要的是,它集成了 Nacos、Sentinel、Seata 这些耳熟能详的开源组件,在实际工作中解决过不少问题。

这篇文章会结合我亲历的一个真实项目案例,聊聊在生产环境下使用 Spring Cloud Alibaba 遇到的一些挑战、踩过的坑,以及最后的效果如何。希望能帮你在微服务实践中少走一些弯路。


项目背景:一个从0到1的微服务改造项目

项目背景:一个从0到1的微服务改造项目

项目的起点是一个运行多年的老系统,原本是基于 Spring Boot + MyBatis 实现的单体应用。随着业务量的增长,这个系统逐渐暴露出几个明显的问题:

  • 单点故障影响范围大
  • 各模块耦合严重,修改一处可能引发多处连锁反应
  • 性能瓶颈突出,数据库连接池时常被打满
  • 发布频率低,版本迭代缓慢,修复 bug 成本高

于是我们决定进行服务化拆分。拆分的目标很明确:

  1. 将订单、用户、支付、库存等核心模块拆分为独立服务;
  2. 实现服务注册发现与负载均衡;
  3. 提供统一配置中心;
  4. 支持限流降级,避免雪崩效应;
  5. 保证分布式事务一致性;
  6. 建立完整的可观测性体系(日志 + 监控 + 链路追踪)。

在评估了多个方案之后,我们最终采用了 Spring Cloud Alibaba 技术栈,主要组件如下:

组件名称 功能描述
Nacos 注册中心 + 配置中心
Sentinel 流控/熔断组件
Seata 分布式事务解决方案
Gateway 外部请求统一入口
OpenFeign 服务间通信

整个系统部署在 Kubernetes 上,配合阿里云的 ACM 做备份,SLB + Keepalive 实现高可用网关集群。


问题描述:从架构设计到落地中的“血泪”经历

问题描述:从架构设计到落地中的“血泪”经历

项目一开始我们就遇到了几个关键问题:

1. 服务注册发现不稳定?

最初我们使用的是 Eureka 作为注册中心。但很快发现了几个致命缺陷:服务摘除延迟严重、实例状态更新不及时导致调用异常。后来换成了 Nacos,虽然解决了大部分问题,但初期也出现了心跳丢失、重复注册等问题。

我们通过排查发现主要是网络环境问题+客户端配置不当引起的。

2. 微服务之间调用频繁超时?

服务拆分完后,订单服务调用用户服务经常超时,甚至出现偶发的 5xx 异常。调用链追踪显示,某些接口响应时间达到了 8s!

分析发现主要有两个问题:

  • 接口性能本身存在问题(如慢 SQL)
  • 负载均衡策略不合理,默认使用的是 Ribbon 的 RoundRobinRule,没有根据实际情况调整

3. 分布式事务怎么处理?

订单创建涉及多个服务的协同操作。比如一个下单流程包括:

  • 库存扣减
  • 用户余额变更
  • 订单状态变更

如果其中一个失败,必须全局回滚,不能部分成功。

最初我们试图手动加锁控制流程,结果引发了严重的并发问题。后来引入了 Seata 做 TCC 模式事务管理,才算真正解了这个问题。

4. 没有统一配置中心,改个参数都要重新发布

每次修改配置都得改 application.yml 并重启服务,效率极低。尤其在测试和灰度阶段频繁切换开关。

这促使我们使用 Nacos 做集中式配置管理,并且实现了自动刷新。


解决方案:一套稳扎稳打的技术组合拳

缓存策略对比-1

解决方案:一套稳扎稳打的技术组合拳

接下来我就重点说一下我们是如何逐步构建起一整套稳定微服务体系的。

1. 使用 Nacos 作为统一注册中心和配置中心

我们在三台服务器上部署了 Nacos 集群,并启用了 MySQL 持久化,防止服务信息丢失。

核心配置示例(application.yml):

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.100:8848,192.168.1.101:8848,192.168.1.102:8848
      config:
        server-addr: 192.168.1.100:8848
        extension-configs:
          - data-id: common.yaml
            group: DEFAULT_GROUP
            refresh: true

数据库设计模型-2

这样所有服务的元数据都在 Nacos 中注册,每个服务都能动态获取最新的配置而无需重启。我们还写了自定义监听器,在配置更新后触发某些逻辑动作,比如重载缓存、关闭特定功能开关等。

2. 使用 Sentinel 做服务容错

我们在 API 网关和各个核心服务中集成了 Sentinel,设置了不同维度的限流规则。

示例代码:为接口添加限流逻辑

@RestController
public class OrderController {

    @GetMapping("/order/{id}")
    @SentinelResource(value = "orderDetail", fallback = "fallbackOrderDetail")
    public ResponseEntity<Order> getOrder(@PathVariable String id) {
        return ResponseEntity.ok(orderService.getOrderByID(id));
    }

    public ResponseEntity<Order> fallbackOrderDetail(String id, Throwable ex) {
        // 返回默认订单或提示信息
        return ResponseEntity.status(503).body(null);
    }
}

我们还在 Sentinel Dashboard 中做了规则持久化,避免每次重启服务规则失效。通过实时监控图表也能迅速定位突发流量波动和服务异常。

3. 使用 Seata 管理分布式事务

我们采用了 Seata 的 AT 模式,对业务侵入小,只需加注解即可。

@Service
public class OrderServiceImpl implements OrderService {

    @GlobalTransactional
    public void createOrder(Order order) {
        inventoryService.decreaseStock(order.getItemId(), order.getCount());
        paymentService.deductBalance(order.getUserId(), order.getTotalPrice());
        orderRepository.save(order);
    }
}

这里需要注意几个点:

  • 每个参与事务的库表要建 undo_log 表用于回滚。
  • 要设置好 timeout 时间,不要让事务持续太久。
  • 不建议在高并发场景使用 AT 模式,建议换成 Saga 或者 TCC。

4. API Gateway 的路由和鉴权

我们使用 Spring Cloud Gateway 搭建了统一的 API 入口,实现路径路由、身份认证、权限控制等功能。

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
          filters:
            - StripPrefix=1
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
          filters:
            - StripPrefix=1

另外我们自己写了个 JWT 鉴权过滤器,判断 token 是否合法,携带的用户角色是否满足访问要求。Gateway 出现过内存泄漏的问题,后面我们将 Netty 模型调整为非阻塞线程模型,加上合理的超时控制,才彻底解决问题。


代码实践:核心组件的配置和调用方式

为了方便理解,我将展示几个核心组件的典型用法。

1. 服务间调用使用 FeignClient

@FeignClient(name = "user-service")
public interface UserServiceClient {

    @GetMapping("/users/{userId}")
    User getUserById(@PathVariable("userId") String userId);
}

调用方只需要声明接口即可发起远程调用,Feign 会自动从 Nacos 获取服务地址并完成负载均衡。

注意:Feign 默认使用 JDK 的 HttpURLConnection,容易出现连接泄漏。我们替换了 OkHttp 客户端,并增加了连接池复用。

2. 日志追踪:集成 Sleuth + Zipkin

在所有服务中我们添加了 Sleuth 和 Zipkin Starter:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

然后接入 Zipkin Server,实现全链路追踪。

spring:
  zipkin:
    base-url: http://zipkin-server:9411/
  sleuth:
    sampler:
      probability: 1.0 # 采样率设为100%,方便调试

有了这些链路数据之后,一旦某个接口出问题,我们就能快速找到具体耗时在哪一步,是 DB 查询还是 RPC 调用。


踩坑经验:那些年我们一起踩过的坑

1. Sentinel Dashboard 规则无法持久化?

刚开始我们只在控制台配了限流规则,但每次重启服务规则就没了。后来才知道需要开启 Nacos 数据源:

spring:
  cloud:
    sentinel:
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            data-id: ${spring.application.name}-sentinel
            group: DEFAULT_GROUP

在 Sentinel 控制台中还需要配置 PushRuleToNacos 类型的数据源才能实现持久化同步。

2. Nacos 高并发下 CPU 打满?

在某次压测过程中,我们发现当并发数达到 3000 QPS 以上时,Nacos 单节点的 CPU 使用率接近 100%。查日志发现大量客户端频繁发送心跳包。

我们做了几点优化:

  • 心跳间隔从默认 5s 改成 10s
  • 设置客户端本地缓存
  • 对 Nacos 做了横向扩容,采用集群部署

最终 CPU 使用率下降了 40%,稳定性大大提升。

3. FeignClient 调用超时却未熔断?

一开始我们以为只要加了 @SentinelResource 注解就会自动熔断,但实测下来发现并非如此。

最终我们意识到:Feign Client 的调用过程不在 Sentinel 的资源管控范围内,因此我们需要主动封装一层:

try {
    // 使用 Sentinel 包裹 feign 调用
    Entry entry = null;
    try {
        entry = SphU.entry("userServiceCall");
        response = userServiceClient.getUserById(userId);
    } catch (BlockException e) {
        // 被限流或熔断时的降级逻辑
        log.warn("请求被限流:{}", e.getMessage());
        return fallbackResponse();
    } finally {
        if (entry != null) {
            entry.exit();
        }
    }
} catch (Exception e) {
    // 处理其他异常
}

这样才能正确地将 Feign 调用纳入 Sentinel 的统计和保护机制中。


效果总结:上线后的收益分析

这套架构上线至今已经稳定运行超过半年,期间经历了几次促销活动的大流量冲击,整体表现如下:

  • 服务平均响应时间下降约 30%
  • 非预期服务中断次数减少 70%
  • 版本发布周期从两周缩短至两天以内
  • 系统可扩展性显著增强,后续新增服务成本降低

更关键是:我们建立了一套标准化的开发运维流程,包括:

  • 配置统一管理
  • 服务健康检查机制
  • 自动扩缩容预案
  • 链路追踪 + 日志聚合体系

这一切都为我们后续向 Service Mesh 转型打下了坚实的基础。


经验分享:给正在尝试 SCA 的你一些建议

如果你正准备用 Spring Cloud Alibaba 构建你的微服务系统,我愿意分享几点个人的经验,希望对你有所帮助:

1. 不要盲目追求“最新技术”,先搞清楚业务需求

我见过很多团队上来就用上 Sentinel、Nacos、SkyWalking 一大堆东西,结果连最基本的服务注册都没弄明白。先把基本功练扎实,再逐步加入高级能力。

2. 关键配置务必双备份

例如 Sentinel 规则、Nacos 的配置文件,一定保存一份 YAML 文件备份。遇到线上规则异常,可以通过脚本一键推送恢复。

3. 合理划分服务边界,不是越小越好

有些同学把服务切得特别碎,导致调用链太长,反而增加复杂度。建议从业务功能出发,保持接口职责单一即可。

4. 学会观察监控指标,而不是依赖经验判断

上线初期别光靠直觉去猜哪里慢了。要用 APM 工具采集数据,看 trace、看 CPU、看 JVM、看堆栈,这才是解决问题的根本手段。

5. 做好灰度发布和回滚预案

任何新改动都应先灰度上线,跑一段时间确认没问题后再全量发布。否则一次错误配置可能导致大规模宕机。


写在最后:微服务是一场长期主义的修行

回顾这一年的旅程,从最初的迷茫选择到如今的轻车熟路,中间经历了无数次“夜不能寐”的调试和“痛彻心扉”的踩坑。但也正是这些经历,让我对整个微服务体系的理解更加深入。

Spring Cloud Alibaba 是一个非常强大的工具,但它只是辅助手段。真正的微服务转型,考验的是我们对业务的理解力、对技术的掌控力,以及对复杂系统的驾驭能力。

如果你也刚刚起步,请相信这条路值得走下去。它并不完美,但却是目前我们能找到的一种相对成熟、可控的解决方案。

愿你在微服务的道路上越走越远,不踩坑,少加班。如有疑问,欢迎留言交流 😊


(全文共约 3261 字)

评论 0

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