Spring Cloud Alibaba 生产实践:从面试题到线上事故的血泪总结

深巷里的服务器
2025-12-15 02:38
阅读 455

上周五晚上十点,我瘫在工位上盯着 Grafana 面板,心里默默问候产品经理全家——又一个“临时加个功能,明天上线”的需求。这已经是入职新公司的第二个月了,说好的“技术驱动、注重架构”呢?怎么感觉还是“能跑就行”?好在我有我的老伙计 GitHub Copilot(付费版用了快两年了,真香),不然这种 deadline 压顶的日子,怕是得秃成地中海。

说到架构,最近团队决定把老掉牙的单体 Spring Boot 应用拆成微服务,还指名要用 Spring Cloud Alibaba(SCA)。领导拍板时一脸自信:“Nacos 做注册中心,Sentinel 熔断限流,Seata 分布式事务,一套全家桶,稳!”
我内心 OS:你怕是没经历过生产环境凌晨三点被 PagerDuty 叫醒的痛。

不过也好,正好趁这个机会把之前跳槽面试时被问烂的 “Spring Cloud Alibaba 和 Spring Cloud Netflix 有什么区别?” 这类 面试题挑战 给落地实操一遍。纸上谈兵谁都会,线上炸了才是真考验。


起手式:别被“全家桶”骗了

刚接触 SCA 的人容易有个误区:以为装个 starter 就万事大吉。现实是,配置不对,服务注册不上;熔断不配,雪崩分分钟教你做人

我们项目是个典型的前后端分离架构:前端 Vue 写死在 Nginx,后端是几个 Spring Boot 微服务(用户中心、订单、支付、商品)。前端同学最烦的就是后端改个接口不通知,结果页面白屏。所以我们在设计 API 时特别强调 契约先行 —— 先写 OpenAPI Spec,再开发,避免“前端联调=猜字段”。

但微服务一拆,问题来了:前端要调多个服务,总不能每个域名都暴露出去吧?于是网关成了刚需。Spring Cloud Gateway + Nacos 动态路由,安排!

关键配置:Nacos 注册中心

# bootstrap.yml
spring:
  application:
    name: user-service
  cloud:
    nacos:
      discovery:
        server-addr: ${NACOS_HOST:localhost}:${NACOS_PORT:8848}
        namespace: prod # 别用 public!生产务必隔离 namespace
        group: DEFAULT_GROUP
      config:
        server-addr: ${NACOS_HOST:localhost}:${NACOS_PORT:8848}
        file-extension: yaml
        namespace: prod

💡 血泪教训:一开始我们没设 namespace,测试和生产共用一个 Nacos,结果测试环境的服务注册到生产,前端调着调着突然返回 {"code":500, "msg":"test data"}……运维差点把我叉出去。


Sentinel:不是配了就安全,得会“预热”

微服务最怕什么?级联失败。一个服务慢,拖垮整个链路。这时候 Sentinel 就派上用场了。

但很多人只配了 @SentinelResource,以为万事大吉。错!流量控制策略必须结合业务场景

比如我们的下单接口,高峰期 QPS 3000+。如果直接设置 QPS = 1000,那超出的部分全拒,用户体验爆炸。所以我们用了 Warm Up(预热)模式

// 在初始化时加载规则
@PostConstruct
public void initFlowRules() {
    List<FlowRule> rules = new ArrayList<>();
    FlowRule rule = new FlowRule();
    rule.setResource("createOrder");
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    rule.setCount(1000); // 最终阈值
    rule.setWarmUpPeriodSec(60); // 60秒内从 1/3 阈值逐渐上升
    rules.add(rule);
    FlowRuleManager.loadRules(rules);
}

这样系统启动或流量突增时,不会因为冷启动直接拒绝大量请求,而是平滑过渡。

🤯 真实事故:去年双11压测,我们没开 Warm Up,结果一放量,Sentinel 直接把 70% 请求 block 掉,监控告警响成交响乐。当时真的想砸 Mac(还好忍住了,毕竟 M2 Pro 太贵)。


Seata:分布式事务不是银弹

说到 Seata,很多人一听“AT 模式自动回滚”,眼睛一亮。但生产环境哪有那么简单?

我们订单服务调用库存服务,必须保证 下单成功则扣库存,失败则回滚。Seata AT 模式确实方便,但有两个坑:

  1. 全局锁性能瓶颈:高并发下,undo_log 表争抢严重。
  2. 数据库必须支持 undo log:我们用的是 MySQL 8.0 + InnoDB,没问题;但如果你用 MongoDB 或 PG,就得换 TCC 模式。

我们的解决方案是:核心链路用 Seata AT,非核心异步补偿

比如发优惠券这种操作,即使失败也不影响主流程,就扔到 MQ 里,失败重试三次,还不行就人工介入。既保证了主链路性能,又不失可靠性。

// 订单服务
@GlobalTransactional
public void createOrder(OrderDTO dto) {
    orderMapper.insert(dto);
    stockClient.decreaseStock(dto.getProductId(), dto.getCount()); // 远程调用
    couponClient.issueCouponAsync(dto.getUserId()); // 异步,不参与全局事务
}

🧠 架构思考:不是所有操作都要强一致。CAP 理论告诉我们,AP 或 CP 得根据业务选。电商下单要 CP,但发通知可以 AP。


前端联调:别让微服务变成“微烦恼”

微服务对后端友好,但对前端简直是灾难。以前一个 /api/order,现在变成 /user/info/product/detail/order/create……前端同学每天在 Postman 里切来切去,人都麻了。

我们的解法是:网关聚合 + Mock 机制

  • 所有后端服务走 Spring Cloud Gateway,前端只对接 gateway.example.com
  • 网关根据路径路由到不同服务:
    spring:
      cloud:
        gateway:
          routes:
            - id: user-service
              uri: lb://user-service
              predicates:
                - Path=/api/user/**
            - id: order-service
              uri: lb://order-service
              predicates:
                - Path=/api/order/**
    
  • 开发阶段,前端可通过 Nacos 配置开关,启用 Mock 数据(用 WireMock 实现),不用等后端接口 ready

这样一来,前端再也不用求着后端“先给我个假数据”,自己就能跑通流程。产品经理看了都说“效率提升了”(虽然他根本不知道改了啥)。


生产运维:日志、监控、告警一个不能少

代码写完只是开始,上线才是噩梦的起点。

我们在 SCA 基础上,集成了:

组件 作用 坑点
SkyWalking 链路追踪 Agent 版本必须和 Spring Boot 匹配,否则启动失败
Prometheus + Grafana 指标监控 Sentinel 指标需手动暴露 /actuator/sentinel
ELK 日志收集 Nacos 日志默认输出到 logs/,得挂载 volume

特别提一句:Nacos 的持久化必须做!

默认 Nacos 是内存存储,重启就丢服务列表。生产必须连 MySQL:

-- 执行 nacos/conf/nacos-mysql.sql 初始化表
# application.properties
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://mysql:3306/nacos?charset=utf8mb4
db.user.0=nacos
db.password.0=xxx

😅 社死现场:有一次运维升级 Nacos 忘了配 DB,重启后所有服务“失联”,前端页面全部 502。我一边回滚,一边在 Slack 里打字:“兄弟们,今天午饭我请……”


总结:SCA 不是万能药,但用好了真香

经过两个月折腾,我们的微服务架构终于稳定跑在生产环境。QPS 提升了 3 倍,故障恢复时间从小时级降到分钟级。更重要的是,面试官再问我 SCA 实战经验,我能掏出一堆线上案例,而不是背八股文

几点心得送给大家:

  1. 别迷信“全家桶”:SCA 提供的是工具,不是解决方案。你怎么用,决定了系统稳不稳。
  2. 配置即代码:Nacos 配置要纳入 Git 管理,每次变更可追溯。
  3. 压测必须做:Sentinel 规则、Seata 性能,不压测等于裸奔。
  4. 前端体验很重要:微服务不该增加前端负担,网关和 Mock 是润滑剂。

最后,感谢 GitHub Copilot。没有它,我可能还在手敲那些重复的 @FeignClient@SentinelResource。虽然它偶尔会生成一些离谱代码(比如把 userId 写成 useId),但整体效率提升至少 30%。作为用了快两年的付费用户,我觉得这钱花得值。

哦对了,下周又要和产品经理 battle 新需求了。他说“能不能在下单时顺便预测用户明天早餐吃啥”……我准备直接甩给他一篇《机器学习与 Spring Cloud Alibaba 的边界》。

祝大家代码无 bug,线上不报警,前端不催接口,产品不改需求!

评论 0

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