你以为做了优雅下线就万无一失?Spring Cloud LoadBalancer 的默认缓存,才是发布期报错的真凶!
我们用 K8s+Nacos 做了一套 “完美” 的滚动发布流程:
结果:每次发版都有数十秒的 RPC 超时 / 连接失败,报错峰值持续 30 + 秒,Nacos 控制台已经看不到旧实例,但流量还在往已销毁的 Pod 上打。
spring:
cloud:
loadbalancer:
cache:
enabled:true # 默认开启!全公司没人改从 Spring Cloud LoadBalancer 源码可以直接看到,缓存 TTL 被硬编码为35 秒:

注释:Time To Live - 从写入开始计算,35 秒后缓存过期。
Spring Cloud LoadBalancer 会缓存服务实例列表,35 秒内不会主动刷新:
1. 每次 RPC 负载均衡,优先读本地缓存
2. Nacos 2.x 虽然用 gRPC 实时推送实例变更,但清不掉这层缓存
3. 旧实例下线后,调用方还要拿着 35 秒前的旧列表继续发流量
早期 Eureka Client 是HTTP 定时轮询拉取服务列表(默认 30 秒一次),网络开销大。LoadBalancer 缓存是为了:
Nacos 2.x 改用gRPC 长连接 + 实时推送:
但 Spring Cloud 为了兼容所有注册中心,不能为 Nacos 单独去掉缓存,于是这个 “历史包袱” 就成了 Nacos 用户的大坑。
spring:
cloud:
loadbalancer:
cache:
enabled:falseNacos Client 本身已经在内存里维护了最新实例,调用getInstances是纯内存操作,没有网络 IO,关闭缓存不会增加注册中心压力。
如果暂时不能关闭缓存,可以缩短 TTL,但治标不治本:
spring:
cloud:
loadbalancer:
cache:
enabled:true
ttl: 5s # 缩短到5秒,减少报错窗口很多线上长期疑难问题,都不是业务 Bug,而是框架为了兼容历史组件留下的 “设计债务”。只会复制粘贴配置的 CRUD 工程师,永远发现不了这种藏了 3 年的坑;只有吃透底层通信原理,才能真正做到零故障发布。