Spring Cloud Alibaba 生产实践:一个普通一本CS狗的血泪踩坑记

Embedding收藏者
2025-12-19 01:44
阅读 2471

大家好,我是小李,普通一本计算机专业大四在读,目前手握杭州某大厂(就不点名了,免得HR找我喝茶)的后端开发offer,等7月入职。坐标杭州,平时混迹于阿里、网易周边的咖啡馆(其实是图书馆),对分布式系统有点兴趣,但也只是“看过几篇论文 + 跑过几个Demo”那种水平。

最近三个月,趁着毕业前的空档期,我接了个外包项目练手——给一家做电商SaaS的小公司重构他们的微服务架构。原本以为就是 CRUD + Spring Boot 套个模板,结果 PM 一句“我们想上云原生”,直接把我推进了 Spring Cloud Alibaba(SCA) 的深水区。更惨的是,上线 deadline 是618大促前一周,而今天已经是5月20号……没错,我正在用情人节的夜晚调 Nacos 配置中心的灰度发布。

这篇文章不是教程,也不是官方文档复读机,纯粹是一个即将社畜的“代码人生”实战复盘。如果你也在用 SCA,或者正被产品经理逼着搞微服务,希望我的踩坑经验能帮你少熬两个通宵。


为什么是 Spring Cloud Alibaba?

先说背景:老系统是单体架构,Spring MVC + MyBatis,数据库 MySQL 单实例,部署在一台 4C8G 的 ECS 上。去年双11直接崩了,老板震怒,要求“必须拆微服务,要高可用、可扩展、能扛流量”。

团队里没人搞过生产级微服务,但 HR 招人慢,领导一拍脑袋:“小李不是学分布式吗?你来牵头!”
我:???我简历上写的是“了解CAP理论”,不是“精通PaaS平台”啊!

选型时对比了 Spring Cloud Netflix(Eureka + Hystrix)、Dubbo + Nacos,最后定了 Spring Cloud Alibaba,原因很现实:

  • 公司服务器都在阿里云,Nacos、Sentinel、Seata 这些组件天然集成,运维成本低
  • 团队 Java 技术栈,SCA 基于 Spring Boot,学习曲线平缓
  • 网易隔壁组也在用,遇到问题还能“借”他们运维帮忙(后来发现他们也在摸着石头过河)

但理想很丰满,现实……emmm,后面你会看到。


第一个坑:Nacos 注册中心连不上?

项目启动第一天,我就被 com.alibaba.nacos.api.exception.NacosException: failed to req API 打脸。本地跑得好好的,一上测试环境就报错。

查了半天,发现是 Nacos 客户端版本和服务器不匹配!我们用了 SCA 2022.0.0.0(对应 Spring Boot 3.x),但测试环境的 Nacos 还是 1.4.2 —— 运维说“这版本稳定,别乱升级”。

行吧,降级 SCA 到 2021.0.1.0(支持 Nacos 2.0+),结果又遇到 gRPC 端口没开的问题。Nacos 2.0 默认启用了 gRPC 通信(9848/9849),但安全组只放行了 8848。

💡 生产建议

  • Nacos 一定要用 集群模式(至少3节点),别信“单机够用”的鬼话
  • 客户端和服务端版本严格对齐,看 SCA 官方版本说明
  • 开通安全组时,别忘了 9848/9849(客户端 gRPC)、7848(Jraft)
# bootstrap.yml
spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: nacos-cluster.example.com:8848
        namespace: prod # 别用 public!
        group: DEFAULT_GROUP
      config:
        server-addr: ${spring.cloud.nacos.discovery.server-addr}
        file-extension: yaml

namespace 和 group 一定要配!不然所有服务挤在 public 里,测试、预发、生产配置全混一起,迟早出事。


配置中心:动态刷新别信 @RefreshScope

需求来了:运营要能实时改商品折扣率,不能重启服务。

我信心满满地加上 @RefreshScope,改完 Nacos 配置,结果……没生效?!

翻源码才发现:@RefreshScope 只对 Spring Bean 有效,且必须是懒加载。如果 Bean 在启动时就被其他组件注入并调用,那它还是旧值。

更坑的是,@RefreshScope 性能很差,每次刷新都会重建整个 Bean,高并发下可能 OOM。

解决方案:用 Nacos Config 监听器手动处理。

@Component
public class DiscountConfig {
    private volatile double discountRate = 0.9;

    @Autowired
    private ConfigService configService;

    @PostConstruct
    public void init() throws NacosException {
        // 监听配置变化
        configService.addListener("discount.properties", "DEFAULT_GROUP", new Listener() {
            @Override
            public void receiveConfigInfo(String configInfo) {
                Properties props = new Properties();
                try {
                    props.load(new StringReader(configInfo));
                    discountRate = Double.parseDouble(props.getProperty("discount.rate", "0.9"));
                    log.info("Discount rate updated to: {}", discountRate);
                } catch (Exception e) {
                    log.error("Failed to parse discount config", e);
                }
            }

            @Override
            public Executor getExecutor() {
                return null; // 使用默认线程池
            }
        });
    }

    public double getDiscountRate() {
        return discountRate;
    }
}

这样性能好、可控性强,还能加日志和异常兜底。别再无脑用 @RefreshScope 了,尤其在核心链路上!


服务调用:OpenFeign + Sentinel 熔断实战

微服务拆分后,订单服务要调用库存服务。用 OpenFeign 很方便:

@FeignClient(name = "inventory-service", fallback = InventoryFallback.class)
public interface InventoryClient {
    @PostMapping("/deduct")
    Result<Boolean> deductStock(@RequestBody DeductRequest request);
}

但大促时库存服务一抖,订单服务线程池就爆了。这时候 Sentinel 就派上用场了。

关键配置:

# application.yml
feign:
  sentinel:
    enabled: true # 开启 Feign 对 Sentinel 的支持

spring:
  cloud:
    sentinel:
      transport:
        dashboard: sentinel-dashboard.example.com:8080
      datasource:
        ds1:
          nacos:
            server-addr: ${spring.cloud.nacos.discovery.server-addr}
            dataId: ${spring.application.name}-sentinel-rules
            groupId: SENTINEL_GROUP
            rule-type: flow # 流控规则

然后在 Nacos 里建一个 order-service-sentinel-rules 配置:

[
  {
    "resource": "POST:http://inventory-service/deduct",
    "limitApp": "default",
    "grade": 1,
    "count": 50,
    "strategy": 0,
    "controlBehavior": 0
  }
]

意思是:对库存服务的调用,QPS 超过 50 就熔断,走 fallback。

但这里有个巨坑:Sentinel 默认的 fallback 是返回 null,如果上层没判空,直接 NPE!所以我们统一包装了 Result 对象,并在 fallback 里记录日志 + 返回友好提示。

@Component
public class InventoryFallback implements InventoryClient {
    @Override
    public Result<Boolean> deductStock(DeductRequest request) {
        log.warn("Inventory service is down, request: {}", request);
        return Result.fail("库存服务繁忙,请稍后再试");
    }
}

上线后,618压测时库存 DB 慢查询导致超时,Sentinel 成功保护了订单服务,没让雪崩发生。那一刻,我觉得自己像个真正的后端工程师了(虽然工资还没发)。


分布式事务:Seata 别乱用

最头疼的是下单流程:扣库存 + 创建订单 + 扣优惠券。三个服务,必须原子性。

我一开始想上 Seata 的 AT 模式,结果发现:

  • 老系统用的 MyBatis-Plus,自动生成 ID,Seata 的 undo_log 表主键冲突
  • MySQL 5.7 不支持 savepoint,AT 模式回滚效率低
  • 最致命:Seata Server 单点故障,我们没资源搭 HA

最后妥协方案:最终一致性 + 本地消息表

  1. 订单服务创建订单(状态为“待支付”)
  2. 同步调用库存服务扣库存(失败则订单取消)
  3. 异步发送 MQ 消息给优惠券服务
  4. 优惠券服务消费消息,失败则重试 + 告警

虽然不完美,但简单可靠。记住:不是所有场景都需要强一致,业务允许的情况下,最终一致性更稳。


Go?你说 Go?

哦对,标题里有 Go。其实我们内部有个日志采集 Agent 是用 Go 写的(运维大佬的杰作),通过 Sidecar 模式部署,把应用日志实时推到 ES。Spring Boot 应用本身还是 Java,但 SCA 组件(如 Nacos、Sentinel)都提供了 Go SDK,未来如果要用 Go 重写某个服务,也能无缝接入。

不过对我这种 Java 狗来说,Go 还是太遥远了。先把 SCA 玩明白再说吧。


性能与监控:别等线上爆炸才看指标

上线前,我们做了三件事:

  1. Arthas 接入:随时在线诊断 JVM、线程、方法耗时
  2. Prometheus + Grafana:监控 Nacos 注册实例数、Sentinel QPS、JVM GC
  3. 日志规范:所有接口打印 traceId,用 MDC 透传

特别强调:一定要监控 Nacos 的连接数!我们有一次因为客户端没关 watchListener,导致 Nacos 内存打满,全站注册实例掉线。血的教训。

指标 正常范围 告警阈值
Nacos 客户端连接数 < 500/节点 > 800
Sentinel Block QPS 0 > 10
Feign 调用 RT < 200ms > 1s

写在最后:代码人生,就是不断填坑

从4月接到需求,到5月底压测通过,我掉了5斤肉,写了30+个 Nacos 配置,debug 了无数个 NullPointerException。但当看到 618 大促当天系统稳如老狗,QPS 从 200 扛到 3000+,心里还是有点小骄傲的。

Spring Cloud Alibaba 不是银弹,但它在阿里云生态下确实降低了微服务落地的门槛。关键是要 理解每个组件的原理,别盲目 copy-paste

作为即将入职的新人,这次实战让我明白:学校教的是理论,生产教的是敬畏。每一行配置背后,都可能是线上事故;每一个 fallback,都是对用户的负责。

如果你也在杭州,欢迎约咖啡(我请,反正快领工资了)。如果你也在用 SCA,评论区聊聊你的踩坑故事?

代码人生,继续搬砖。

P.S. 本文所有代码已脱敏,如有雷同,纯属巧合。
P.P.S. 产品经理今天又提了新需求:“能不能加个 AI 推荐?” 我:……(默默打开了招聘软件)

评论 0

最热最新
暂无评论
Embedding收藏者Lv.1
0
影响力
0
文章
0
粉丝