从大厂裸辞后,我用 Spring Cloud Alibaba 重写了前东家的微服务骨架
上周五深夜,我又一次打开 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 持久化规则,终于实现了“运维同学后台点几下就能限流”。
持久化实现的核心是重写 RulePublisher 和 RuleReceiver。这里贴一段简化版:
// 将规则推送到 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