Skip to content
进阶

一句话答案

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则自动刷新。