Nacos 配置中心原理
一、Nacos 概述
Nacos(Dynamic Naming and Configuration Service)是阿里巴巴开源的服务发现与配置管理平台,提供服务注册与发现和动态配置管理两大核心能力。
- 注册中心:服务实例注册、健康检查、服务发现
- 配置中心:配置集中管理、动态下发、变更通知
二、Nacos 配置中心原理
2.1 核心概念 [1.x/2.x/3.x 通用]
以下核心概念在 Nacos 所有版本中保持一致:
| 概念 | 说明 |
|---|---|
| Data ID | 配置的唯一标识,通常格式为 {prefix}-{profile}.{format},如 application-dev.yaml |
| Group | 配置分组,默认 DEFAULT_GROUP,用于隔离同一 Data ID 的不同环境配置 |
| Namespace | 命名空间,用于多环境或多租户隔离,默认 public |
| 配置项 | 一个具体的配置 key-value 对 |
| 配置快照 | 客户端本地缓存的配置副本,用于容灾 |
2.2 客户端获取配置的模式(按版本)
Nacos 获取配置的机制在不同版本中差异显著,从纯 Pull(1.x)演进到Push + Pull 混合(2.x/3.x)。
[1.x 机制] 纯 Pull — HTTP 长轮询
Nacos 1.x 仅通过 HTTP 长轮询(Long Polling) 实现配置监听,没有服务端主动推送能力。
核心流程:
客户端 ──(1) 发起长轮询请求(HTTP)──▶ 服务端
│
├── 有变更?──立即返回变更的 Key 列表
│
└── 无变更?保持连接(最长 30s)
│
├── 30s 内有其他客户端变更配置?
│ → 服务端触发 ConfigChangeEvent
│ → 遍历挂起的请求,返回变更
│
└── 30s 超时 → 返回空响应
↓
客户端再次发起新一轮长轮询
时序细节:
发起长轮询
- HTTP GET 请求,路径
/v1/cs/configs/listener - 携带所有监听配置的 MD5,多个配置合并到同一个请求体中(非每个配置单独一个连接)
- 仅当配置数超过 3000 个时,客户端会按每批 3000 个进行分片,每片启动一个独立的长轮询线程
- 超时时间
LongPollingTimeout=30000(30 秒)
- HTTP GET 请求,路径
服务端挂起请求
- 创建
ClientLongPolling延迟任务 - 比对 MD5 列表与最新配置
- 有变更 → 立即返回
- 无变更 → 放入调度队列等待
- 创建
客户端处理响应
- 收到变更 Key → 调用
/v1/cs/configs重新拉取完整配置 - 收到空响应 → 立即发起新一轮长轮询(无间隔)
- 更新本地快照和内存配置
- 触发 Spring
Environment刷新、@RefreshScope回调
- 收到变更 Key → 调用
⚠️ 1.x 局限: 配置生效最长需等待 30s(一个长轮询周期),无法实现即时推送。
[2.x/3.x 机制] Push + Pull 混合 — gRPC 双向流 + 长轮询降级
Nacos 2.x 引入 gRPC 双向流 作为主要通信方式,实现服务端真正主动推送;HTTP 长轮询保留作为降级方案。3.x 继承该架构。
gRPC Push 机制(主要通道):
客户端 Nacos Server
│ │
│ (1) 建立 gRPC 双向流长连接 │
│══════════════════════════════▶│
│◀══════════════════════════════│
│ │
│ (2) 注册配置监听 │
│──── ConfigWatchRequest ─────▶│
│ │
│ (3) 配置有变更时 │
│ │
│◀─── ConfigChangePush ────────│ ← 服务端主动推送
│ │
│ (4) 收到推送 → 拉取最新配置 │
│──── ConfigQuery ────────────▶│
│◀──── 完整配置内容 ────────────│
gRPC 推送的优势(相比 1.x 长轮询):
| 维度 | 1.x HTTP 长轮询 | 2.x/3.x gRPC 推送 |
|---|---|---|
| 推送延迟 | 最长 30s(需等待轮询周期) | 毫秒级(服务端主动推送) |
| 连接模型 | 每次轮询一个 HTTP 连接 | 多路复用单一长连接 |
| 连接数 | 所有配置合并到一个长轮询请求中,仅超大批量(>3000)时分片,连接数 = ⌈配置数/3000⌉ | 固定,所有操作复用同一 gRPC 连接 |
| 网络开销 | 请求头重复传输 | HTTP/2 头部压缩,开销小 |
HTTP 长轮询降级(备用通道):
当 gRPC 连接建立失败或断开时,2.x/3.x 客户端会自动降级到 1.x 的 HTTP 长轮询机制,确保客户端在不支持 gRPC 的网络环境中仍然可用。
2.3 客户端与服务端通讯协议演进 [全版本对比]
| 版本 | 配置中心协议 | 注册中心协议 | 端口变化 |
|---|---|---|---|
| Nacos 1.x | HTTP 长轮询(Pull) | HTTP 心跳 | 仅 8848 |
| Nacos 2.x | gRPC 双向流(Push)+ HTTP 降级 | gRPC 长连接 + HTTP 降级 | 8848 + 9848 + 7848 |
| Nacos 3.x | gRPC 双向流(增强) | gRPC 长连接 | 同 2.x |
2.4 配置快照与容灾 [1.x/2.x/3.x 通用]
该机制在所有版本中一致:
- 客户端启动时成功拉取配置后,在本地磁盘保存一份配置快照
- 快照位置:
${user.home}/nacos/config/snapshot-{namespace}/{dataId} - 服务端不可用时,客户端从本地快照加载配置,保证应用可启动
2.5 配置变更回调链路(按版本)
[1.x 机制] HTTP 长轮询回调链路
管理员修改配置 → Nacos Server 持久化到 MySQL/Derby
↓
Server 触发 ConfigChangeEvent
↓
遍历挂起的 HTTP 长轮询 → 返回变更 Key 列表
↓
客户端收到列表 → 再次 HTTP GET 拉取完整配置
↓
客户端更新本地缓存 + 本地快照
↓
Spring: 发布 RefreshEvent(Environment 变更)
↓
@RefreshScope 的 Bean 重新创建
↓
配置生效
[2.x/3.x 机制] gRPC Push 回调链路
管理员修改配置 → Nacos Server 持久化到 MySQL/Derby
↓
Server 通过 gRPC 双向流直接推送(ConfigChangePush)
↓
客户端 gRPC 连接收到推送 → 发起 ConfigQuery 拉取完整配置
↓
客户端更新本地缓存 + 本地快照
↓
Spring: 发布 RefreshEvent(Environment 变更)
↓
@RefreshScope 的 Bean 重新创建
↓
配置生效
核心差异: 1.x 需要等待 30s 内长轮询的"被唤醒";2.x/3.x 是服务端通过 gRPC 通道直接推送,延迟从秒级降至毫秒级。
4.2 配置中心的完整交互(按版本)
[1.x 机制] HTTP 长轮询循环
┌───── Client 1.x ────┐ ┌──── Nacos Server 1.x ──┐
│ │ │ │
│ (1) HTTP GET 长轮询 │────────▶│ /v1/cs/configs/listener │
│ MD5 监听列表 │ │ 挂起请求(最长 30s) │
│ │ │ │
│ │◀────────│ (2a) 有变更 → 变更 Key │
│ │ │ (2b) 无变更 → 空响应 │
│ │ │ │
│ (3) 有变更时 │ │ │
│ HTTP GET 拉取完整配置 │────────▶│ /v1/cs/configs │
│ │◀────────│ 返回完整配置内容 │
│ │ │ │
│ (4) 更新本地缓存 │ │ │
│ + 快照 + 回调 │ │ │
│ │ │ │
│ ←── 回到步骤(1) 循环 ──▶ │ │
└───────────────────────┘ └─────────────────────────┘
关键特征: 每个循环周期是一个独立的 HTTP 请求,长轮询超时后立即发起下一个。
[2.x/3.x 机制] gRPC 推送 + HTTP 降级
┌───── Client 2.x/3.x ─┐ ┌── Nacos Server 2.x/3.x ─┐
│ │ │ │
│ (1) 建立 gRPC 长连接 │────────▶│ 端口 9848 │
│ 双向流 │◀────────│ │
│ │ │ │
│ (2) 注册配置监听 │────────▶│ ConfigWatchRequest │
│ │ │ 注册到监听列表 │
│ │ │ │
│ (3) 初次拉取配置 │◀────────│ 返回完整配置 │
│ │ │ │
│ (4) 配置有变更时 │ │ │
│ │◀────────│ gRPC Push(主动推送) │
│ │ │ ConfigChangePush │
│ │ │ │
│ (5) 拉取最新配置 │────────▶│ ConfigQuery │
│ │◀────────│ 返回完整配置内容 │
│ │ │ │
│ (6) 更新本地缓存 │ │ │
│ + 快照 + 回调 │ │ │
│ │ │ │
│ ←── 等待下一次 Push ──▶ │ │
│ (无变更时无网络开销) │ │ │
└────────────────────────┘ └──────────────────────────┘
关键特征: 连接建立后,无变更时零额外网络开销,变更时服务端主动推送,延迟 < 1s。
4.4 通讯端口说明(按版本)
| 端口 | 协议 | 用途 | 引入版本 | 说明 |
|---|---|---|---|---|
| 8848 | HTTP | 管理控制台、HTTP API | 1.x 起 | 所有版本均存在 |
| 9848 | gRPC | 客户端 gRPC 请求端口(8848 + 1000) | 2.x 开始 | 1.x 无此端口 |
| 9849 | gRPC | 集群节点间 gRPC 通信(8848 + 1001) | 2.x 开始 | 1.x 无此端口 |
| 7848 | HTTP | 集群 JRAFT 选举通信 | 2.x 开始 | 1.x 使用 Raft 在 8848 |
版本差异小结:
| 端口数量 | Nacos 1.x | Nacos 2.x | Nacos 3.x |
|---|---|---|---|
| 对外端口 | 1 个(8848) | 2 个(8848 + 9848) | 同 2.x |
| 集群内部端口 | 0 个 | 1 个(7848) | 同 2.x |
配置中心常见问题
Q1:客户端配置变更后多久能生效?**
1.x 机制:最长 30s(一次长轮询周期内),取决于长轮询何时超时或变更触发。
2.x/3.x 机制:毫秒级(gRPC 服务端主动推送),延迟通常 < 1s。
**
Q4:配置长轮询会不会造成连接泄漏?**
1.x 场景:不会,Nacos 客户端内部使用连接池管理 HTTP 连接,长轮询超时后会立即发起下一次请求,每个请求有独立超时和清理机制。且所有配置合并到一个长轮询请求中,不会随配置数增多而线性增加连接数。但 1.x 没有连接复用,配置/心跳/查询各走独立通道。
2.x/3.x 场景:更无此问题,所有操作复用单一 gRPC 长连接。
**