从微服务到高可用:我的 Spring Cloud Alibaba 生产实践之路

慵懒猫
2025-06-25 02:29
阅读 500

引言:为什么我们选择了 Spring Cloud Alibaba?

引言:为什么我们选择了 Spring Cloud Alibaba?

事情还要从几年前说起,当时我所在的公司正在经历一次比较大的架构升级。原本的单体系统已经难以满足日益增长的业务需求和流量压力,团队决定尝试往微服务架构转型。

最初考虑的是 Spring Cloud Netflix 套件,但随着社区对 Eureka、Zuul 等组件逐渐停止积极维护(尤其是 Hystrix 被官方宣布进入维护模式),再加上我们有不少基于阿里系中间件的历史项目,最终在技术选型阶段果断转向了 Spring Cloud Alibaba

这个决定不仅改变了我们的架构演进方向,也让我在后来几年中不断深入研究这套组合拳,并在多个实际项目中积累了宝贵的经验。今天我就想把这些踩过的坑、绕过的弯路,以及一些真实的生产经验分享出来,希望能帮到正走在微服务转型路上的你。


项目背景:电商平台重构之痛

项目背景:电商平台重构之痛

我们接手的是一套运行多年的电商平台后端系统,采用的是传统的单体架构,部署在几台老旧的物理服务器上。随着用户量和交易频次的增长,问题开始集中爆发:

  • 接口响应慢,高峰期偶发超时
  • 功能更新风险大,牵一发动全身
  • 部署过程繁琐,灰度发布困难
  • 扩展性差,核心服务无法水平扩容

于是,我们启动了“凤凰计划”——目标是将整个系统拆分为多个可独立部署、可伸缩的微服务模块,实现服务间解耦,并为后续的弹性扩容打下基础。


遇到的挑战:微服务带来新问题?

遇到的挑战:微服务带来新问题?

微服务确实带来了不少好处,但也带来了新的复杂性和运维成本。尤其是在初期搭建环境和服务治理时,我们遇到了很多棘手的问题:

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

我们最早用 Nacos 作为注册中心,但在压测环境下偶尔出现服务实例未及时注册或被错误剔除的问题,导致调用失败。

2. FeignClient 的连接池配置不当?

Fein 默认使用的是 JDK 自带的 HttpURLConnection,性能较差,且默认没有启用连接复用,导致并发稍高时出现大量 CLOSE_WAIT。

3. 网关限流策略不完善?

网关使用的是 Zuul,虽然能满足基本路由功能,但动态限流能力较弱,而且 Zuul 本身的性能也不足以支撑高并发场景。

4. 分布式事务怎么搞?

订单、支付、库存等服务各自独立,业务上又需要保证一致性。早期尝试用了 Seata,但在某些场景下回滚异常频繁,日志混乱难定位。

这些问题,一度让我们怀疑是否应该继续微服务路线。幸运的是,通过不断的探索与改进,我们逐步找到了适合自己的解决方案。


技术方案设计:Spring Cloud Alibaba 组合拳

技术方案设计:Spring Cloud Alibaba 组合拳

我们采用的技术栈主要如下:

模块 使用组件
服务注册 Nacos Server + 客户端
服务通信 OpenFeign + Ribbon + HTTP Client
网关 Gateway + Sentinel
配置管理 Nacos Config
熔断降级 Sentinel
分布式事务 Seata(TCC 模式)
监控追踪 SkyWalking + Prometheus + Grafana

下面重点讲几个关键模块的设计和实现思路。


关键代码示例与配置说明

1. 使用 Nacos 作为服务注册中心

spring:
  application:
    name: product-service
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.0.100:8848

服务启动后会自动注册到 Nacos,其他服务通过服务名即可调用,Ribbon 会自动完成负载均衡。

2. 使用 OpenFeign 发起远程调用

@FeignClient(name = "order-service")
public interface OrderServiceClient {
    @GetMapping("/orders/{userId}")
    List<Order> getOrdersByUserId(@PathVariable("userId") Long userId);
}

为了提升性能,我们将底层 HTTP Client 替换为 OkHttp:

feign:
  client:
    config:
      default:
        httpclient:
          enabled: false
          okhttp:
            enabled: true

同时,设置了合理的连接池参数,避免资源浪费和 CLOSE_WAIT 问题。

3. 网关层结合 Sentinel 实现限流熔断

我们在网关层引入了 Sentinel 来做入口级别的限流,防止突发流量冲垮下游服务:

spring:
  cloud:
    gateway:
      routes:
        - id: product-route
          uri: lb://product-service
          predicates:
            - Path=/api/product/**
          filters:
            - StripPrefix=1
            - name: Sentinel
              args:
                block-handler: com.example.gateway.handler.GatewayBlockHandler

并自定义了一个 BlockHandler:

public class GatewayBlockHandler {

    public static Mono<ServerResponse> handle(ServerRequest request, Throwable ex) {
        return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                             .bodyValue("请求过多,请稍后再试");
    }
}

这样可以在请求超过阈值时直接返回提示,而不是抛出异常或阻塞线程。


踩过的坑和解决办法

坑 1:Nacos 启动慢,影响本地调试

现象:本地开发环境 Nacos 启动耗时较长,每次重启都需要等待几分钟,影响效率。

解决:采用 Docker 快速启动一个轻量版 Nacos 服务:

docker run -d -p 8848:8848 --env MODE=standalone nacos/nacos-server

另外,也可以使用 nacos-mock 工具在本地模拟注册中心行为。

坑 2:Seata TCC 模式下的幂等性处理

我们在分布式下单操作中使用 Seata 的 TCC 模式,但在测试中发现部分事务重复执行,导致库存扣减两次。

分析原因:TCC 的 cancel 阶段可能因网络波动而重试多次,如果 cancel 方法没做幂等处理,就会发生数据错乱。

解决方案:

  • 在 Cancel 方法内部增加唯一业务标识(如订单号)和版本号控制
  • 使用数据库乐观锁或者 Redis 标记机制防止重复执行

坑 3:Sentinel 控制台持久化问题

上线之后我们发现一旦 Sentinel 控制台重启,之前配置的规则就丢了。

解决方法:结合 Nacos 实现规则持久化。

配置文件中添加如下内容:

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

然后在 Nacos 中手动创建对应的 dataId,写入规则 JSON:

[
  {
    "resource": "/api/order/create",
    "limitApp": "default",
    "grade": 1,
    "count": 200,
    "strategy": 0,
    "controlBehavior": 0
  }
]

成果与收益:架构升级带来的改变

改造完成后,系统在以下几个方面有了显著提升:

  • 性能稳定性增强:服务间通信延迟降低,网关层能扛住更高的并发请求。
  • 故障隔离能力增强:某个服务崩溃不会影响整个系统,Sentinel 和 Ribbon 协作完成优雅降级。
  • 研发效率提升:服务独立部署、灰度发布更方便,迭代速度加快。
  • 运维更加透明:SkyWalking 实现全链路追踪,定位问题时间缩短 80%。

更重要的是,这次转型让我们真正理解了技术服务于业务的本质。不是为了上微服务而上,而是要根据业务特点选择合适的技术组合。


我的经验分享:给你的几点建议

如果你也在用或者准备使用 Spring Cloud Alibaba,以下是我总结的一些实战经验,供参考:

1. 别盲目堆技术,先看清业务需求

不要看到别人用啥你就用啥。比如 Seata 并不是所有场景都适用,简单业务完全可以用最终一致性的消息队列方案替代,反而更稳定。

2. 注意版本匹配问题

SCA(Spring Cloud Alibaba)对 Spring Boot 和 Spring Cloud 的版本非常敏感,一定得查文档看兼容矩阵。我们有段时间踩过版本冲突,导致 Feign 调用一直报空指针,查了一天才发现是包冲突。

3. 运维自动化先行

Kubernetes + Helm 是个不错的组合,配合 Jenkins CI/CD 可以大幅提升部署效率。别小看这一块,上线后的日常运维比开发还花时间。

4. 日志和监控不能少

微服务最大的问题就是链路长、状态多,一定要建立统一的日志收集(ELK)、监控告警(Prometheus+Grafana)体系,否则出了问题很难排查。

5. 多读文档和源码

Spring Cloud Alibaba 虽然社区活跃,但有些问题官网文档不一定覆盖,建议没事看看 GitHub issue 或者翻翻源码,说不定就能找到问题答案。


结语:技术从来都不是终点,而是工具

回头来看,Spring Cloud Alibaba 并非完美无缺,但它的确帮助我们成功完成了平台级的架构升级。最重要的是,它教会了我如何用工程思维去平衡技术与业务之间的关系

在这条路上,我们会遇到无数新技术、新框架、新名词,但不要忘了我们最本质的职责:写出稳定、高效、易于维护的代码,来支持真实业务的发展。

希望这篇文章能对你有所启发。如果你有类似经验,也欢迎留言交流。技术路漫漫其修远兮,愿你我都能保持热爱,砥砺前行。


文章作者:阿林
原创首发于:个人博客 & 知乎专栏《Java 微服务那些事儿》
欢迎关注,持续输出一线开发实战经验

评论 0

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