外观
一句话答案
Nacos Config 通过客户端长轮询(30s)监听配置变更,变更时服务端立即响应,客户端自动刷新本地配置。
核心要点
整体机制:长轮询(Long Polling)
传统轮询:客户端每隔 N 秒拉一次配置 → 实时性差,服务端压力大
长轮询:客户端发请求,服务端 hold 住连接(最多 30s),有变更立即返回
Nacos Config 采用长轮询 + MD5 比对:
→ 客户端发送请求,携带当前配置的 MD5 值
→ 服务端对比 MD5:
如果相同 → hold 住连接,最多等 29.5s 后返回空(无变更)
如果不同 → 立即返回变更的 dataId 列表
→ 客户端收到变更的 dataId → 再发 GET 请求拉取最新配置内容详细流程:
┌──────────────┐ ┌──────────────┐
│ Nacos │ │ Nacos │
│ Client │ │ Server │
└──────┬───────┘ └──────┬───────┘
│ │
│ ① POST /listener │
│ Body: dataId + group + md5 │
│ ────────────────────────────────────→ │
│ │
│ ② 服务端检查 MD5 │
│ 如果配置未变化 → 挂起连接 29.5s │
│ 如果配置已变化 → 立即返回 │
│ │
│ ③ 返回变更的 dataId 列表 │
│ ←──────────────────────────────────── │
│ │
│ ④ GET /configs?dataId=xxx │
│ ────────────────────────────────────→ │
│ │
│ ⑤ 返回最新配置内容 │
│ ←──────────────────────────────────── │
│ │
│ ⑥ 更新本地缓存 + 通知 Listener │
│ ⑦ 立即发起下一次长轮询 │
│ ────────────────────────────────────→ │客户端关键源码逻辑(ClientWorker):
ClientWorker 内部有两个线程池:
→ 调度线程池(1 个线程):每 10ms 检查是否需要发起长轮询
→ 长轮询线程池(多个线程):执行实际的 HTTP 长轮询请求
流程:
1. ClientWorker 启动时,为每个监听的 dataId+group 注册 Listener
2. 调度线程定期检查 → 发起长轮询任务
3. 长轮询线程向 Nacos Server POST /listener
4. 收到响应后,拉取变更配置 → 更新本地缓存文件
5. 触发所有注册的 Listener 回调 → Spring 的 RefreshScope 感知到变更Spring Cloud 集成热刷新的两种方式:
java
// 方式一:@RefreshScope + @Value(推荐)
@RestController
@RefreshScope // 配置变更时,Bean 重新创建
public class OrderController {
@Value("${order.max-amount:10000}")
private int maxAmount; // 配置变更后自动更新
}
// 方式二:@ConfigurationProperties(自动刷新,无需 @RefreshScope)
@Component
@ConfigurationProperties(prefix = "order")
@Data
public class OrderProperties {
private int maxAmount = 10000; // 配置变更后自动更新
private boolean enableDiscount;
}注意事项:
① @RefreshScope 的原理是销毁旧 Bean、创建新 Bean,可能有短暂的空指针
② @ConfigurationProperties 是直接修改属性值,更平滑
③ 配置变更会触发 EnvironmentChangeEvent → 可以监听此事件做额外处理
④ Nacos 客户端会将配置缓存到本地文件 → Nacos Server 宕机后仍可使用缓存五、熔断与弹性
追问与易错
追问方向:
- 变更是推还是拉?
- 敏感配置怎么处理?
- @RefreshScope 原理?
易错点:
- ❌ 每次读都请求 Nacos——客户端有缓存
- ❌ @Value 不加 @RefreshScope 也能刷新——不能
💡 记忆锚点
Nacos Config像"订阅报纸":客户端发长轮询请求挂着(最多等29.5秒),服务端配置一变立刻推送变更的dataId列表,客户端再拉取最新内容刷新本地。核心是MD5比对检测变更。@Value要配@RefreshScope才能热刷新,@ConfigurationProperties则自动刷新。