Nacos 一致性协议
注册中心的实例数据分布在多个节点上,节点之间如何保证一致,是理解 Nacos 行为的关键。Nacos 按实例类型自动选择协议:临时实例走 AP,持久实例走 CP。
两种实例,两套协议
第 2 章提过 ephemeral 参数,它决定的不只是"要不要落库",更决定了用哪套一致性协议:
| 实例类型 | 默认值 | 一致性 | 协议 | 典型场景 |
|---|---|---|---|---|
| 临时实例 | ephemeral=true | AP | 类 Distro 协议(节点间异步复制) | 微服务常规注册 |
| 持久实例 | ephemeral=false | CP | 基于 Raft 的副本协议(2.x 自研实现 + JRaft) | DNS、Service Mesh、需长期稳定的服务 |
临时实例:AP 的取舍
临时实例量大、生命周期短,Nacos 为它实现了无单点的 AP 方案,要点是:
- 每个节点都能独立读写,没有强主节点,挂一个不影响整体
- 实例数据在节点间异步复制,写操作先本地生效再同步给别人
- 牺牲"强一致"换取"高可用":某个节点可能短暂读到另一节点还没同步到的数据
对注册中心来说这通常可以接受:实例列表短暂旧一点,远好过"写不进去导致服务起不来"。
持久实例:CP 的取舍
持久实例需要可靠保存、多副本一致,Nacos 让这类数据走基于 Raft 的副本协议:
- 集群先选举出 leader,写请求由 leader 处理
- 多数派节点确认后才算提交成功(多数派写)
- 少数派节点或 leader 故障时,从副本中重新选主,保证已提交数据不丢
代价是写入链路更重,且选主期间可能短暂不可写——所以持久实例适合数量不多但必须可靠的服务。
AP 与 CP 对比
| 维度 | AP(临时实例) | CP(持久实例) |
|---|---|---|
| 一致性 | 最终一致,允许短暂读到旧数据 | 强一致,多数派确认才成功 |
| 可用性 | 节点故障时读写基本不受影响 | 选举期间短暂不可写,已提交数据可读 |
| 数据落库 | 不落库,靠心跳维护 | 持久化存储 |
| 适合规模 | 成千上万实例的动态微服务 | 数量少、要求稳定的场景 |
为什么注册中心常用 AP
注册信息本质是"短暂状态":实例活着才有意义,心跳停了自然会被摘除。调用方偶尔拿到一个刚下线的实例,最多是本次调用失败重试一次;但如果为了强一致让注册写入经常失败或阻塞,整个微服务集群都无法启动。所以日常微服务都用默认的临时实例(AP)。
配置管理则相反:配置内容必须人人一致,错的配置会影响所有节点,因此配置数据依赖持久化存储(单机 Derby 或集群 MySQL)保证一致,详见第 11 章。
Nacos 如何自动选择
不需要手动指定协议:注册时带不带 ephemeral 参数即可。OpenAPI 注册时显式传 ephemeral 字段:
# 持久实例:ephemeral=false 走 CP
curl -X POST 'http://localhost:8848/nacos/v1/ns/instance' \
-d 'serviceName=gateway-dns&ip=192.168.1.10&port=8080&ephemeral=false'
# 输出:ok
不传该参数时默认 ephemeral=true,即临时实例走 AP。
故障场景行为
- AP 集群挂一个节点:剩余节点照常注册、发现,数据随后异步补齐
- CP(Raft 副本)leader 故障:触发重新选主,选举窗口内写请求短暂失败或等待,已提交数据仍可读;多数派节点存活才能选出新 leader
小结
一句话记牢:临时实例 AP 保可用、持久实例 CP 保一致,Nacos 用 ephemeral 字段自动切换。日常微服务用默认的 AP 临时实例即可;需要"注册信息必须可靠保存"的场景(如 DNS、Service Mesh 的数据面)才考虑持久实例。