Spring Cloud Alibaba 在县城远程办公的实战踩坑记
去年十月,我还在北京一家中型互联网公司做后端开发,每天挤地铁通勤一小时,耳机里循环着《码农翻身》播客。后来因为疫情反复,加上公司推行“混合办公”政策,我干脆搬回了老家——一个三线小县城,靠着 500M 光纤和一堆 VSCode 插件,继续写我的 Java 代码。
说来好笑,小镇做题家的标签贴得我死死的:大学靠刷 LeetCode 进大厂,工作后靠啃《分布式系统原理与范式》撑场面。但现实是,线上系统崩了,没人管你算法多牛,只看你能不能快速 fix。
上周五晚上十点,我正蹲在村口小卖部蹭 WiFi(家里光猫又抽风了),手机突然弹出钉钉告警:订单服务大量超时,熔断器触发。心里一紧——这可是双11预热期间,搞砸了年终奖就泡汤了。
而我们的微服务架构,正是基于 Spring Cloud Alibaba(SCA) 搭建的。今天这篇,就聊聊我在县城远程办公这一年,用 SCA 做生产项目的真实体验,包括那些让我半夜惊醒的坑,以及最终怎么“苟”下来的。
为什么选 Spring Cloud Alibaba?
我们团队原本用的是 Spring Cloud Netflix,但 Eureka、Hystrix 这些组件早在 2018 年就停止维护了。领导拍板:“换!必须国产化,拥抱阿里生态。”
于是,我们把注册中心换成 Nacos,熔断限流用 Sentinel,配置中心也迁到 Nacos,消息队列则用 RocketMQ(虽然严格来说不算 SCA 核心,但阿里全家桶嘛,顺手就用了)。
说实话,一开始我是抗拒的。毕竟在县城,网络延迟比北京高那么几毫秒,Nacos 集群同步会不会卡?Sentinel 规则动态更新靠不靠谱?但 deadline 不等人,只能硬着头皮上。
项目背景:一个“高并发”的订单系统(其实也就几千 QPS)
我们的核心业务是电商下单,典型的 Java 微服务架构:
user-service:用户信息product-service:商品库存order-service:订单创建payment-service:支付回调
每个服务都用 Spring Boot 2.7 + Spring Cloud 2021.0.5 + Spring Cloud Alibaba 2021.0.5.0。
关键需求:
- 服务注册发现要快(用户不能等)
- 下单链路必须限流熔断(防止雪崩)
- 配置要能动态刷新(产品经理天天改文案)
坑1:Nacos 注册中心的心跳机制把我整懵了
刚上线那会儿,测试环境一切正常。结果一上生产,服务频繁掉线!日志里全是:
com.alibaba.nacos.api.exception.NacosException: Client not connected, current status:UNHEALTHY
我第一反应是网络问题——毕竟我在县城,公司服务器在北京。但运维查了防火墙、带宽都没问题。
后来翻 Nacos 源码才发现:默认心跳间隔是 5 秒,超时时间是 15 秒。而我们某些服务启动慢(比如加载了大模型算法做推荐),超过 15 秒没发心跳,就被踢出服务列表了!
解决方案很简单,在 application.yml 里调大超时:
spring:
cloud:
nacos:
discovery:
heart-beat-interval: 5000
heart-beat-timeout: 30000 # 默认15秒,改成30秒
ip-delete-timeout: 60000
但更优雅的做法是:让服务启动完成后再注册。我们在 order-service 里加了个 ApplicationRunner,确保库存预热、缓存加载完才向 Nacos 注册。
坑2:Sentinel 的 QPS 限流“不准”?
产品经理要求:下单接口最多 1000 QPS,超了就返回“系统繁忙”。
我用 Sentinel 控制台配了流控规则,阈值 1000,模式 QPS。结果压测时发现,实际放行了 1200+ 请求!
当时真的想砸电脑。后来研究文档才知道:Sentinel 默认是“单机均摊”模式,如果你有 3 个 order-service 实例,总阈值其实是 3000!
正确的做法是选 “集群流控”,但集群流控需要额外部署 Sentinel Token Server,还得考虑高可用。对于我们这种小团队,实在不想再维护一个组件。
折中方案:按实例数手动分摊阈值。比如 3 个实例,每个配 333 QPS。虽然不完美,但够用。
配置示例(通过 Nacos 动态推送):
[
{
"resource": "POST:/api/order/create",
"controlBehavior": 0,
"count": 333,
"grade": 1,
"limitApp": "default",
"strategy": 0
}
]
注:
grade: 1表示 QPS 模式
坑3:配置中心动态刷新失效
我们把数据库连接池参数、Redis 超时时间都放到 Nacos 配置中心,希望不用重启就能生效。
但改了 datasource.max-pool-size 后,服务根本没反应!
原因在于:@RefreshScope 只对 Spring Bean 生效,而 DataSource 是由 HikariCP 管理的,不是普通 Bean。
解决方案有两个:
- 手动监听配置变更,然后调用
HikariDataSource.set...()方法 - 用 Spring Cloud Alibaba 提供的
@NacosConfigListener
我们选了第二种,代码如下:
@Component
public class DataSourceConfigRefresher {
@Autowired
private HikariDataSource dataSource;
@NacosConfigListener(dataId = "order-service-db.yaml", groupId = "DEFAULT_GROUP")
public void onDataSourceChange(String config) {
Yaml yaml = new Yaml();
Map<String, Object> props = yaml.load(config);
Integer maxPoolSize = (Integer) ((Map) props.get("spring")).get("datasource").get("hikari").get("maximum-pool-size");
// 动态调整
dataSource.setMaximumPoolSize(maxPoolSize);
log.info("DB pool size updated to: {}", maxPoolSize);
}
}
虽然有点 hack,但线上稳了。
性能调优:从算法到架构
说到性能,很多人以为就是堆机器、调 JVM。但我觉得,合理的架构设计比炫技的算法更重要。
比如我们的订单 ID 生成,原本用数据库自增,结果主键冲突、分库分表后更乱。后来改用 雪花算法(Snowflake),但标准雪花算法在分布式环境下有重复风险(时钟回拨)。
于是我们基于 百度的 UidGenerator 改了一版,用 Redis 做 workerId 分配,保证全局唯一。核心代码不到 100 行,但解决了大问题。
另外,接口设计上做了“读写分离”:
- 创建订单走
order-service - 查询订单走
order-query-service(只读从库)
这样即使写库压力大,用户查订单也不受影响。这是我在刷《数据密集型应用系统设计》时学到的,没想到真用上了。
生产环境运维经验
在县城远程办公,最怕半夜被叫起来救火。所以我们在 SCA 基础上加了几层保障:
| 组件 | 作用 | 我们的配置 |
|---|---|---|
| Nacos 集群 | 服务注册/配置 | 3 节点,同城双活 |
| Sentinel Dashboard | 流控监控 | 持久化到 MySQL,避免规则丢失 |
| Arthas | 线上诊断 | 所有服务预装,支持远程 attach |
| Prometheus + Grafana | 指标监控 | 采集 JVM、Sentinel QPS、Nacos 注册数 |
特别提一句:Sentinel 规则一定要持久化!默认是存在内存里的,服务重启就没了。我们写了个脚本,每天凌晨自动备份规则到 Git。
效果如何?
自从用上 SCA 并填完这些坑,系统稳定性提升明显:
- 服务注册成功率从 92% → 99.98%
- 下单接口 P99 延迟从 800ms → 220ms
- 双11当天零重大故障(虽然被产品经理骂了三次 UI)
最重要的是,我现在可以在老家阳台上,一边晒太阳一边用 VSCode 远程调试代码,再也不用挤早高峰地铁了。
当然,偶尔也会怀念北京的加班餐和同事的段子。但在县城,至少我能花更多时间陪父母,顺便研究下分布式事务(Seata 还没敢上生产,下次再聊)。
最后几句真心话
Spring Cloud Alibaba 不是银弹,但它确实降低了我们中小团队玩微服务的门槛。Nacos 比 Eureka 好用,Sentinel 比 Hystrix 灵活,这些都是实打实的进步。
但技术再新,也抵不过一个朴素的道理:线上无小事,敬畏每一行代码。
毕竟,你在县城写的 bug,可能正在北京某用户的手机上爆炸。
好了,村口小卖部要关门了,我得回家了。代码已提交,今晚睡个好觉。

评论 0