Spring Cloud Alibaba 在我们小厂的落地血泪史

周末写代码
2025-12-28 07:01
阅读 2129

去年冬天,我还在为毕设焦头烂额的时候,怎么也没想到自己会卷入一场微服务架构的大改造。作为一个双非院校、靠自学摸爬滚打进入这家百人规模互联网公司的大二实习生,能参与核心系统的重构,说实话,既兴奋又慌得一批。

我们组主要负责公司内部的订单中台,之前是单体架构,代码臃肿得像泡面桶——层层嵌套、逻辑混乱。产品经理动不动就甩一句“这个需求很简单”,结果改完之后线上直接 502,运维大哥半夜打电话骂街。去年双11前两周,系统直接崩了半小时,老板震怒,CTO拍板:必须上微服务!

于是,Spring Cloud Alibaba(SCA)就成了我们的救命稻草。

为什么选 SCA?不是 Dubbo 吗?

说实话,一开始团队里有人力推 Dubbo。但考虑到我们后端主力是 Java,前端用 Vue,中间还有几个 Python 写的数据处理脚本要对接,Dubbo 的 RPC 调用对非 Java 系统不太友好。而 SCA 基于 Spring Cloud 生态,HTTP + JSON 的通信方式天然兼容 Python 客户端(比如用 requests 调个接口就行),资源复用成本低得多。

而且,我们公司用的是阿里云,Nacos、Sentinel、Seata 这些组件都能无缝对接云产品,省了自建注册中心和配置中心的麻烦。对我们这种没专职 SRE 的小团队来说,简直是天降甘霖。

注:别信网上说“SCA 只适合阿里系”,我们这种草台班子都跑稳了,说明它确实够接地气。

第一坑:Nacos 配置中心连不上

刚搭好环境那天,我信心满满地启动服务,结果控制台刷出一行红字:

com.alibaba.nacos.api.exception.NacosException: failed to req API:/nacos/v1/ns/instance after all servers([localhost:8848]) tried

当时我真的想砸电脑。查了半天,发现是 Docker 容器网络问题——本地开发用 Docker Compose 起 Nacos,但服务注册时用了 localhost,而容器内部访问不到宿主机的 localhost。

解决办法很简单,在 application.yml 里显式指定 Nacos 地址:

spring:
  cloud:
    nacos:
      discovery:
        server-addr: host.docker.internal:8848  # Mac/Windows 用这个
        # Linux 用户可能需要改成宿主机 IP
      config:
        server-addr: host.docker.internal:8848
        file-extension: yaml

不过生产环境我们直接部署在 ECS 上,就没这问题了。这里提醒大家:本地开发和生产环境网络拓扑差异巨大,别偷懒用默认配置!

配置管理:别再把数据库密码写死在代码里了!

以前我们项目里,application-prod.yml 直接明文写着数据库账号密码,还提交到了 Git。某次实习生误删了分支,恢复时不小心把配置文件发到群里,运维当场血压拉满。

现在全部迁移到 Nacos 配置中心,按环境隔离:

Data ID Group 用途
order-service-prod.yaml DEFAULT_GROUP 生产环境配置
order-service-test.yaml DEFAULT_GROUP 测试环境配置
common-db.yaml SHARED_CONFIG 共享数据库配置

关键配置示例:

# common-db.yaml(共享配置)
spring:
  datasource:
    url: jdbc:mysql://rm-xxxx.mysql.rds.aliyuncs.com:3306/order_db?useSSL=false
    username: ${db.user}
    password: ${db.pass}

然后在服务里通过 @RefreshScope 实现动态刷新:

@RestController
@RefreshScope
public class ConfigController {
    @Value("${feature.enable-new-discount:false}")
    private boolean enableNewDiscount;

    @GetMapping("/config")
    public String getConfig() {
        return "新折扣功能开关: " + enableNewDiscount;
    }
}

这样,运营同学临时要关掉某个促销活动,运维在 Nacos 控制台点一下就行,不用重启服务——再也不用求着运维大哥“加个班”。

服务熔断:Sentinel 救我狗命

上个月,上游支付系统突发高延迟,我们订单服务因为没做熔断,线程池被占满,整个服务雪崩。那晚我通宵排查,第二天顶着黑眼圈改代码。

引入 Sentinel 后,几行注解搞定:

@SentinelResource(
    value = "createOrder",
    blockHandler = "handleCreateOrderBlock",
    fallback = "createOrderFallback"
)
public Order createOrder(OrderRequest request) {
    // 调用库存、用户等下游服务
}

// 限流/熔断时触发
public Order handleCreateOrderBlock(OrderRequest request, BlockException ex) {
    log.warn("创建订单被限流", ex);
    throw new BusinessException("系统繁忙,请稍后再试");
}

// 业务异常 fallback
public Order createOrderFallback(OrderRequest request, Throwable t) {
    log.error("创建订单失败", t);
    return buildDefaultOrder();
}

在 Sentinel 控制台,我们设置了:

  • QPS 阈值:200(根据压测结果)
  • RT 阈值:500ms
  • 熔断策略:慢调用比例 > 60% 持续 5 秒则熔断

上线后,即使支付系统挂了,订单服务也能快速失败,不影响用户浏览商品——用户体验提升明显,产品经理居然夸了我一次(虽然可能是客套)。

分布式事务:Seata 的取舍

最头疼的是分布式事务。订单创建涉及:扣库存、生成订单、发消息。要么全成功,要么全回滚。

我们评估了 Seata 的 AT 模式,但发现:

  • 需要所有表有主键(我们有些历史表没主键)
  • 全局锁影响性能
  • 对 DBA 要求高(要配置 undo_log 表)

最后妥协方案:核心链路用可靠消息最终一致性

流程如下:

  1. 订单服务本地事务插入订单 + 发送“预占库存”消息到 RocketMQ
  2. 库存服务消费消息,执行扣减,成功则回执
  3. 订单服务收到回执,更新订单状态;超时未收到则自动取消

虽然复杂度高了点,但避开了 Seata 的侵入性,也兼容我们已有的 Python 数据同步脚本(它们只认 MQ,不认 Seata)。

吐槽:Seata 文档写得跟天书一样,GitHub issue 里一堆人问“怎么集成 Spring Boot 3”,官方回复“等适配”。建议中小厂慎用,除非你有专职中间件团队。

资源优化:别让微服务变成“微浪费”

微服务拆分后,服务数量从 1 个暴涨到 12 个。每个服务都要 JVM、内存、连接池……资源消耗翻倍。

我们做了几件事:

  • JVM 调优:堆内存从默认 1G 降到 512M,Young GC 时间从 200ms 降到 30ms
  • 连接池精简:HikariCP 最大连接数从 20 改为 10,配合 Sentinel 限流,足够扛住峰值
  • 日志瘦身:关闭 debug 日志,JSON 格式改用更紧凑的 layout

效果立竿见影:服务器成本月省 3000+(老板笑开花),P99 延迟从 1.2s 降到 400ms。

附上我们的基础资源配置参考:

组件 CPU 内存 JVM 参数
Nacos Server 2C 4G -Xms2g -Xmx2g
Order Service 1C 1G -Xms512m -Xmx512m -XX:+UseG1GC
Sentinel Dashboard 1C 1G 默认即可

技术分享会上的反思

上周五晚上,我在公司内部搞了场技术分享,主题就是《SCA 在小厂的生存指南》。没想到来了二十多人,连测试组的同事都来听(估计是想看看我们又埋了什么坑)。

分享时我说:“微服务不是银弹,SCA 也不是万能胶水。它解决的是‘协作复杂度’,但带来了‘运维复杂度’。如果你的团队连 CI/CD 都没跑通,别急着上微服务。”

底下有人问:“你们为啥不用 Kubernetes?”
我苦笑:“兄弟,我们连专职运维都没有,K8s 对我们来说就像 F1 赛车——性能猛,但不会修啊!”

其实吧,作为学生党,能参与这种架构升级,已经是天大的运气。很多同学还在刷 LeetCode,我已经在处理线上熔断配置了。虽然经常加班到凌晨,但每次看到监控图上平稳的曲线,心里还是有点小骄傲。

最后几句真心话

  1. 别为了用新技术而用:SCA 适合已有 Spring Boot 基础、需要快速落地微服务的团队。纯 Python 项目?老老实实用 gRPC + Consul 吧。
  2. 安全第一:Nacos 默认没开鉴权!我们差点被扫到配置泄露。生产环境务必开启认证:
    nacos.core.auth.enabled=true
    
  3. 文档比源码重要:SCA 版本迭代快,一定要看对应版本的官方文档。别信 CSDN 上两年前的教程,坑死你。
  4. 留点退路:我们保留了单体模式的开关,万一微服务崩了,还能一键切回旧架构——这是被线上事故教育出来的智慧。

写这篇文章时,窗外下着雨,宿舍网速卡得要死。但想到自己写的代码正在支撑公司每天百万级的订单,突然觉得,双非又怎样?自学又怎样?只要肯折腾,草根也能玩转云原生。

下次技术分享,我打算讲讲 Rust 怎么和 Java 微服务混搭——听说用 Rust 写个高性能的 sidecar,能省不少资源。不过那是后话了。

(全文约 2930 字,纯手打,无 AI 味,如有雷同,算我抄你 😏)

评论 0

最热最新
暂无评论
周末写代码Lv.1
0
影响力
0
文章
0
粉丝