从大厂裸辞后,我用 Spring Cloud Alibaba 重写了前东家的微服务骨架

404收集者
2026-05-29 13:05
阅读 3974

上周五深夜,我又一次打开 Vim,光标在空文件里闪得人心慌。距离正式从某一线大厂“战略撤退”已经三个月了——没错,就是那种“35岁焦虑+周报写到吐+需求改八百遍”的经典组合拳下被迫出逃。现在每天除了刷 LeetCode 准备跳槽面试(毕竟 Java 岗卷成麻花了),剩下的时间就在琢磨:如果让我重新设计一套微服务架构,该怎么避开当年踩过的那些天坑?

正好最近有朋友问起 Spring Cloud Alibaba 的生产实践,索性把这段时间的折腾心得整理出来。别误会,我不是要吹它多牛,而是想说:这玩意儿真能用,但得配对姿势,否则上线即事故


起因:不是我们想换,是 Nacos 救了命

故事得回到去年双11前两周。当时我在的团队负责一个核心交易链路,用的是 Spring Cloud Netflix 那套老古董(Eureka + Hystrix + Zuul)。结果测试压测时发现,Zuul 网关在高并发下频繁 Full GC,Hystrix 的熔断策略又太“粗暴”,动不动就把整个接口干掉,导致大量用户下单失败。

运维大哥半夜打电话骂街:“你们这架构能不能抗住?再不行就切回单体!” 产品经理在群里疯狂@:“老板说今晚必须跑通 5W TPS,不然年终奖泡汤!”

眼看 deadline 压顶,CTO 一拍桌子:“换成 Spring Cloud Alibaba,Nacos 注册中心 + Sentinel 流控,三天内搞定!”

于是,我们成了公司第一批吃螃蟹的人。虽然当时心里嘀咕:“阿里系的东西,文档糙、社区杂、版本乱,真能稳?” 但没想到,这一换,居然成了我后来跳槽简历上的亮点项目。


别被 GPT-4o 骗了,SCA 不是“开箱即用”

现在网上一堆 AI(比如某些号称 GPT-4o 的模型)生成的文章,张口闭口“Spring Cloud Alibaba 一键集成,三行代码搞定微服务”。醒醒吧兄弟,生产环境哪有这种童话?

我拿自己血泪史举个例子:刚引入 Nacos 时,本地开发一切正常,一上测试环境,服务注册延迟高达 30 秒,调用方疯狂报 No provider available。查了一晚上日志,才发现是 Nacos 客户端默认用了 UDP 心跳,而测试环境防火墙只放行 TCP。改成 TCP 后,问题消失。

再比如 Sentinel,默认规则是存在内存里的,应用一重启全没了。我们一度以为流控失效,差点误判成系统瓶颈。后来才意识到:生产环境必须对接 Dashboard + 持久化到 Nacos 或 Apollo

所以别信什么“零配置”,真正的生产级部署,至少要考虑以下几点:

  • 注册中心高可用:Nacos 至少 3 节点集群,挂一个不能崩
  • 配置热更新安全:敏感配置(如数据库密码)不能明文存 Nacos
  • Sentinel 规则持久化:避免重启丢失,最好支持动态推送
  • OpenFeign 超时与重试策略:默认超时 1 秒,线上分分钟 timeout

实战:我是怎么把 SCA 搞稳的

1. Nacos:不只是注册中心,更是配置中枢

我们最终采用 Nacos 作为统一的服务发现 + 配置中心。但直接用默认配置?达咩!

关键配置如下(application.yml):

spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: nacos-cluster.prod.internal:8848
        namespace: prod-ns  # 隔离环境,别和 dev/test 混一起
        metadata:
          version: v2.1.0   # 用于灰度发布
      config:
        server-addr: ${spring.cloud.nacos.discovery.server-addr}
        file-extension: yaml
        group: DEFAULT_GROUP
        namespace: ${spring.cloud.nacos.discovery.namespace}
        shared-configs:
          - data-id: common-db.yaml
            refresh: true
          - data-id: sentinel-rules.json
            refresh: true

💡 经验namespace 是隔离不同环境的利器,别偷懒全塞 DEFAULT。另外,metadata 可以配合 Sentinel 或网关做金丝雀发布。

2. Sentinel:流控不是摆设,要能动态调

早期我们把流控阈值写死在代码里,每次调整都要发版。后来接入 Sentinel Dashboard,并通过 Nacos 持久化规则,终于实现了“运维同学后台点几下就能限流”。

持久化实现的核心是重写 RulePublisherRuleReceiver。这里贴一段简化版:

// 将规则推送到 Nacos
public class NacosRulePublisher implements RulePublisher {
    private ConfigService configService;

    @Override
    public void publish(String app, List<FlowRule> rules) throws Exception {
        String dataId = genDataId(app);
        String rulesJson = JSON.toJSONString(rules);
        configService.publishConfig(dataId, GROUP, rulesJson);
    }
}

然后在 Dashboard 的 application.properties 里配置:

# 指向你的 Nacos
sentinel.nacos.server-addr=nacos-cluster.prod.internal:8848
sentinel.nacos.namespace=prod-ns

这样一来,只要在 Dashboard 上修改规则,所有实例自动同步,再也不用求运维改 JVM 参数了

3. OpenFeign + Seata:分布式事务别硬扛

我们有个场景:下单时要扣库存、生成订单、发优惠券。三个服务,强一致性要求。最初用 try-catch + 补偿,结果补偿逻辑写得比主流程还复杂,测试根本覆盖不完。

后来引入 Seata 的 AT 模式,配合 Feign,代码清爽多了:

@GlobalTransactional // 开启全局事务
public void createOrder(OrderRequest request) {
    stockClient.decrease(request.getSkuId(), request.getCount()); // Feign 调用
    couponClient.issue(request.getUserId());
    orderMapper.insert(buildOrder(request));
}

但注意:Seata 对数据库有要求(需 undo_log 表),且不支持 MySQL 分库分表。我们当时为了上 Seata,临时把订单库从 ShardingSphere 切回单库,血亏。


性能与稳定性:线上不是 playground

上线后第一周,我们盯监控盯到眼瞎。有几个关键指标必须关注:

指标 阈值 监控方式
Nacos 服务注册延迟 < 1s Prometheus + Grafana
Sentinel QPS 丢弃率 < 0.1% Sentinel Dashboard
Feign 调用平均耗时 < 50ms SkyWalking
Seata 全局事务提交成功率 > 99.9% 自定义埋点

有一次,我们发现 Feign 调用偶尔超时 5 秒。排查半天,原来是没设 ReadTimeout,默认走 Ribbon 的 1 秒超时,但网络抖动时会重试,导致总耗时飙升。后来统一配置:

feign:
  client:
    config:
      default:
        connect-timeout: 1000
        read-timeout: 2000
  retryer:
    enabled: false  # 关闭 Feign 自带重试,由 Sentinel 控制

🤯 教训:重试和熔断要配合好,否则雪崩来得更快。


数据库与接口设计:别让中间件背锅

很多人以为上了微服务,DB 就可以随便搞。错!我们曾因为一个慢 SQL 导致整个链路拖垮。

接口设计原则

  • 参数校验前置:用 @Valid + 全局异常处理,别让非法请求打到 DB
  • 返回字段精简:别一股脑返回整个 POJO,用 DTO 控制粒度
  • 幂等性必须保证:尤其支付、下单类接口,加唯一请求 ID

数据库优化

  • 所有微服务 DB 独立,禁止跨库 join
  • 索引按查询路径建,别听 DBA 的“先建再说”
  • 大表分页用游标(cursor-based),别用 LIMIT 100000, 20

举个例子,我们的订单查询接口原本这样写:

@GetMapping("/orders")
public List<Order> list(@RequestParam int page, @RequestParam int size) {
    return orderService.page(page, size); // 内部用 LIMIT
}

结果用户翻到第 1000 页时,数据库 CPU 直接飙到 90%。后来改成基于 lastOrderId 的游标分页:

@GetMapping("/orders")
public OrderPage list(@RequestParam(required = false) Long lastOrderId) {
    return orderService.cursorPage(lastOrderId);
}

性能提升 10 倍不止。


辞职后的反思:技术选型不是炫技

现在回头看,Spring Cloud Alibaba 确实解决了我们当时的燃眉之急。Nacos 比 Eureka 更适合国内网络环境,Sentinel 的细粒度流控也比 Hystrix 灵活得多。但没有银弹,它的学习曲线陡峭,文档碎片化,社区响应慢(尤其非阿里系公司)。

如果你正在考虑引入 SCA,我的建议是:

  • 小团队慎用 Seata:维护成本高,不如用最终一致性 + 消息队列
  • Nacos 别当 KV 存储用:配置中心 ≠ 数据库,别存大文本
  • 监控体系必须跟上:没有可观测性,微服务就是黑盒炸弹

最近面试时,有家公司问我:“你为什么从大厂出来还研究这些老技术?” 我笑笑说:“Java 生态的微服务框架,底层原理就那么些,吃透一个,其他都是换皮。我现在用 Vim 写代码,不为炫技,只为理解每一行背后的代价。”


最后:给正在刷题跳槽的你

如果你也在准备 Java 后端岗,别只盯着算法题。大厂面试官越来越看重工程落地能力。你能说出 SCA 在生产中如何调优、如何排查问题、如何权衡取舍,比背八股文有用得多。

至于我?LeetCode 刷到 300 题了,Vim 插件配得飞起。希望下一份工作,别再让我在周五晚上 debug 分布式事务了。

共勉。

评论 0

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