Nacos AP 一致性规范深度解析:Distro 同步、Config Notify 与通用 HTTP Client 目标契约
【免费下载链接】nacosan easy-to-use dynamic service discovery, configuration and service management platform for building AI cloud native applications.项目地址: https://gitcode.com/GitHub_Trending/na/nacos
Nacos 将 CAP 理论中的 AP 与 CP 选择显式地划分为两条一致性路径:AP 路径优先保证可用性与分区容忍,服务于可以最终收敛的运行时状态;CP 路径则用于持久元数据等强一致场景。本文以仓库中的 基础 AP 一致性规范 为骨架,结合core、naming、config等模块的源码实现,系统讲解 AP 资源规则、Distro 共享同步框架、Naming Distro 契约、Config Notify 变更传播路径、失败语义与边界规则,并深入解读通用 HTTP Connection-based Client 的目标契约。读完本文,你将能够理解 Nacos AP 路径的组件构成与生命周期、各配置参数的实际含义,以及如何通过 Distro 与 Config Notify 完成临时状态的集群收敛。
1. AP 一致性在 Nacos 中的定位
AP 和 CP 是 CAP 理论中的一致性选择。在 Nacos 中,AP 路径优先保证可用性和分区容忍,用于可以最终收敛的状态。它本身不提供全局强顺序写日志、线性一致读,或持久管理面归属。也就是说,AP 路径解决的是"节点间最终一致地共享运行时状态"问题,而不是"每次读写都线性一致"的问题。
当前 AP 风格实现包括两类:
| 实现 | 主要归属 | 用途 |
|---|---|---|
| Distro | Naming 运行时状态 | 在服务端节点之间同步临时、客户端拥有的服务实例状态。 |
| Config Notify | Config 缓存与 listener 可见性 | 通知 peer 节点某个 Config 资源发生变化,使本地 dump 缓存和 listener 刷新。 |
需要特别说明的历史背景是:consistency模块中曾存在APProtocol接口,但当前活跃的 AP 实现是 Distro 基础能力和 Config Notify 路径。新规范直接描述 AP 语义,而不假设所有 AP 行为都必须实现APProtocol接口。这一点在阅读core模块的distro包(core/src/main/java/com/alibaba/nacos/core/distributed/distro)时可以印证:整个 Distro 框架围绕DistroProtocol、组件注册表与任务引擎组织,而非依赖旧的一致性抽象。
从规范定位上看,基础能力规范 是 AP 一致性部分的上级文档,本文是其在 AP 维度上的展开;持久化与强一致相关内容请参见 CP 一致性规范 与 持久化与 Dump 规范。
2. AP 资源规则:什么状态适合走 AP
并非所有数据都适合走 AP 路径。规范给出了明确的判断标准——AP 状态适用于具备以下特征的资源:
- 由存活客户端或本地观测拥有的运行时状态;
- 更新频率高,全局串行化代价过高;
- 可以重建、刷新或丢弃;
- 正确性允许最终收敛和重试;
- 失败处理可以通过 verify、snapshot、reload 或重新查询完成。
相反,以下资源不适合AP 路径:持久管理元数据、运维覆盖、schema 状态、长期权限,以及要求单一全局提交顺序的资源。这些资源应使用 CP 一致性规范或持久化与 Dump 规范。权限、namespace 元数据、插件状态、数据库 schema 状态被明确列入 AP 路径的禁区(见第 9 节边界规则)。
此外,AP 使用方必须显式定义以下契约要素:
- 资源身份和 resource type;
- 生产状态的 owner 或责任规则;
- 数据操作集合和幂等预期;
- 收敛窗口和重试策略;
- verify 与修复行为;
- 启动加载和 snapshot 行为;
- 本地 apply 后发布的事件;
- 删除数据是否需要 tombstone、过期或替换语义。
AP 路径使用到的 delayed task、execute task 和本地事件规则,由 任务执行规范 与 事件分发与 NotifyCenter 规范 定义。
3. Distro 模型:核心组件与数据流
Distro 是core.distributed.distro下的共享 AP 同步框架,位于 core/src/main/java/com/alibaba/nacos/core/distributed/distro。其数据流模型如下:
DistroKey(resourceKey, resourceType, targetServer) -> DistroData(type, content) -> DistroDelayTask / DistroExecuteTask -> DistroTransportAgent -> DistroDataRequest -> DistroDataProcessor -> local state and events3.1 组件职责
| 组件 | 职责 |
|---|---|
DistroKey | 通过 resource key、resource type 和可选 target server 标识一个 AP datum。 |
DistroData | 承载序列化后的 datum 内容和DataOperation。 |
DistroDataStorage | 产出单个数据、verify data 和完整 snapshot。 |
DistroDataProcessor | apply 接收到的数据、verify data 和 snapshot data。 |
DistroTransportAgent | 向 peer 节点发送 sync、verify、query 和 snapshot 请求。 |
DistroFailedTaskHandler | 将失败的 sync 或 verify 操作转换为重试任务。 |
DistroTaskEngineHolder | 管理 sync、verify、load 和 retry 的延迟任务与执行任务。 |
3.2 实体类源码印证
DistroKey是一个由三个字段组成的普通实体:DistroKey.java 定义了resourceKey、resourceType和可选的targetServer,并实现了基于三者的equals/hashCode,这意味着同一个 datum 发往不同 target server 时会被视为不同的延迟任务,天然支持"按目标节点拆分任务"。
DistroData则组合了DistroKey、DataOperation type与序列化后的byte[] content:DistroData.java。其中DataOperation来自com.alibaba.nacos.consistency模块,是 Distro 与 CP 路径共用的操作枚举。
3.3 DataOperation 操作集
Distro 操作使用ADD、CHANGE、DELETE、VERIFY、SNAPSHOT、QUERY等DataOperation。领域 processor 必须定义自身 resource type 支持哪些操作。例如 Naming 的 ClientData processor 就在 DistroClientDataProcessor.java 中显式声明了 resource type 与支持的事件类型。
3.4 协议入口:DistroProtocol
DistroProtocol是整个 Distro 框架的编排入口(DistroProtocol.java),核心行为包括:
- 启动任务:构造时调用
startDistroTask()。若处于单机模式(EnvUtil.getStandaloneMode()),直接置isInitialized = true并返回,不启动任何远端任务;否则启动 verify 定时任务与 load 任务。 - sync / syncToTarget:
sync()遍历memberManager.allMembersWithoutSelf()向所有 peer 广播;syncToTarget()则把DistroKey补上 targetServer 后封装为DistroDelayTask提交给延迟任务引擎,实现按 key 延迟执行。 - onReceive / onVerify:根据
DistroData携带的 resourceType 从DistroComponentHolder查找对应DistroDataProcessor,分别调用processData与processVerifyData。 - onQuery / onSnapshot:通过
DistroDataStorage获取单条 datum 或全量 snapshot。
DistroComponentHolder负责把 data storage、processor、transport agent、failed-task handler 按 resourceType 注册起来,形成"类型到组件"的路由表;DistroTaskEngineHolder则管理延迟/执行两类任务引擎。
3.5 Distro 动态配置项
DistroConfig(DistroConfig.java)继承AbstractDynamicConfig,支持从环境变量动态读取,所有配置键与默认值定义在 DistroConstants.java 中:
| 配置键 | 默认值 | 含义 |
|---|---|---|
nacos.core.protocol.distro.data.sync.delayMs | 1000 | sync 延迟任务的基础延迟 |
nacos.core.protocol.distro.data.sync.timeoutMs | 3000 | sync 请求超时 |
nacos.core.protocol.distro.data.sync.retryDelayMs | 3000 | sync 失败重试延迟 |
nacos.core.protocol.distro.data.verify.intervalMs | 5000 | verify 定时任务周期 |
nacos.core.protocol.distro.data.verify.timeoutMs | 3000 | verify 请求超时 |
nacos.core.protocol.distro.data.load.retryDelayMs | 30000 | load 失败重试延迟 |
nacos.core.protocol.distro.data.load.timeoutMs | 30000 | load 请求超时 |
这些参数可以通过application.properties中的对应键覆盖,是调优集群收敛速度与网络压力平衡点的主要抓手。
4. Distro 生命周期:加载、验证与修复
在非单机模式下,Distro 会启动 load 和 verify 任务。生命周期规则如下:
- 启动加载:从 peer 节点获取 snapshot,并交给领域 processor apply;
- 初始化门槛:snapshot apply 成功后,对应 data storage 才能标记为已初始化;
- verify 时序:verify 任务不应在对应 data storage 初始化完成前运行;
- verify 内容:verify data 比较 revision、checksum 等紧凑状态,并在目标节点发现不一致时触发修复;
- 变更合并:change 和 delete 任务按 key 延迟执行,并在 task engine 支持时进行合并;
- 失败处理:sync 或 verify 失败必须由领域 failed-task handler 重试,或带诊断信息地显式丢弃。
单机模式不得伪造远端 AP 收敛。它应将 AP 运行时状态标记为本地初始化,并跳过远端同步——这正是DistroProtocol.startDistroTask()中EnvUtil.getStandaloneMode()分支所体现的逻辑。
对应的任务类均位于 core/src/main/java/com/alibaba/nacos/core/distributed/distro/task:
load/DistroLoadDataTask:启动时从 peer 拉取 snapshot;verify/DistroVerifyTimedTask、DistroVerifyExecuteTask:周期性与按 key 执行 verify;delay/DistroDelayTask与DistroDelayTaskProcessor:延迟调度 sync/delete;execute/DistroSyncChangeTask、DistroSyncDeleteTask:实际执行变更同步;DistroTaskEngineHolder:统一持有各任务引擎与执行 worker。
5. Naming Distro 契约
Naming 使用 Distro 同步临时 client state。规则要点:
- 只有当前节点负责的临时 client 会通过 Distro 同步;
- 持久 client 和元数据必须使用 CP 或持久化路径;
- client change 产生 Distro
CHANGE,disconnect 产生DELETE,verify 失败可以触发定向ADD; - Distro sync data 包含 client id、attributes、已发布服务、实例发布信息和批量实例数据;
- apply Distro data 必须更新服务端 Client state,并发布 Naming 事件,使派生索引和推送视图可以重建;
- verify 使用 client id 和 revision,并可以从源节点调度修复;
- snapshot 包含当前临时 client sync data 集合。
从源码看,Naming 的 Distro 集成点在 naming/src/main/java/com/alibaba/nacos/naming/consistency/ephemeral/distro/v2:
DistroClientComponentRegistry把DistroClientDataProcessor注册为 data storage、data processor、transport agent 和 failed-task handler(DistroClientComponentRegistry.java);DistroClientDataProcessor.TYPE的取值为"Nacos:Naming:v2:ClientData"(DistroClientDataProcessor.java);- 收到的 payload 会通过
Serializer反序列化为ClientSyncData并调用handlerClientSyncData更新本地 Client state; - 本地
ClientChangedEvent会触发向 peer 的 Distro 同步(事件类型在interest()中声明)。
Naming Distro 传输通过DistroDataRequest/DistroDataResponse承载,并遵循 内部 RPC 与集群请求规范;接收入口是 DistroDataRequestHandler.java。
6. 通用 HTTP Connection-based Client 目标契约
本节定义由多个 HTTP 请求共同维护的临时 Naming Client。Agent Endpoint、普通 Naming Instance 和未来其他运行时 Endpoint 在进入 Client 前分别完成领域适配;Client manager 和 Distro 只处理标准InstancePublishInfo或BatchInstancePublishInfo。
目标流程为:
HTTP request -> module-owned Distro Filter routes by internal client id -> mutate HttpConnectionBasedClient -> existing Naming ClientData CHANGE or DELETE -> peer Client state -> Naming indexes and runtime projections规范同时明确:在对应 Client manager 完成并由上层 API 声明能力前,不表示该 Client 类型已经实现或可用。这属于目标契约(target contract),读者不应把它误读为已经上线可用的能力。
6.1 资源身份与路由
| 项目 | 目标语义要求 |
|---|---|
| 外部身份 | 调用方提供的 opaqueexternalClientId。 |
| 内部 Client id | HTTP_CLIENT@@<externalClientId>。 |
| Distro resource type | 复用Nacos:Naming:v2:ClientData。 |
resourceKey和responsibleId | 完整内部 Client id。 |
| Client manager | HttpConnectionBasedClientManager,与ConnectionBasedClientManager同级并由ClientManagerDelegate路由。 |
| 责任归属 | Distro 根据稳定内部 Client id 选择唯一责任节点。 |
| 模块复用 | AI、Naming 或其他模块使用相同 external id 时共享同一个 Client、publisher/subscriber 容器和生命周期。 |
| 远端入口 | 每个模块使用自己的 HTTP Distro Filter 将有状态请求转发到责任节点;AI 不扩展 Naming 模块现有的 Distro Filter。 |
首次创建有状态 Client 时绑定一个鉴权主体和一个namespaceId。状态只保存稳定主体标识,不保存 credential 或 access token。后续有状态请求必须使用相同主体和 namespace;不匹配时拒绝请求且不刷新任何活性时间。Client id 只是路由和状态归属标识,不是鉴权凭据。
6.2 ClientData 与操作
通用 HTTP Client 直接复用 Naming 的ClientSyncData、DistroClientDataProcessor及其ADD/CHANGE/DELETE/VERIFY/SNAPSHOT/QUERY语义,不注册新的 Distro resource type 或 processor。同步数据包含标准 Client identity、attributes、全部 publication 和 revision;HTTP Client attributes 额外保存 namespace、鉴权主体标识、Client 活性和 Publisher 活性。
Publisher 数据仍使用现有 Client 的完整 service publication:
- 单实例使用
InstancePublishInfo; - 完整批次使用
BatchInstancePublishInfo; - Agent 或其他领域 Adapter 不把领域 DTO 放入 Distro payload;
- Subscriber 与现有 connection-based Client 一致,只保留在实际承载订阅的一侧,不进入 ClientData publication snapshot。
Publication 变化或语义健康状态变化推进 Client revision 并产生现有ClientChangedEvent。普通 Client 续约和 Publisher heartbeat不因时间戳本身变化而广播 ClientData;peer 通过已有 verify、snapshot 和 repair 收敛。
6.3 Client 与 Publisher 分层活性
HTTP Client 分别维护两层活性:
| 活性 | 刷新来源 | 影响 |
|---|---|---|
| Client 活性 | 合法查询、订阅变更、publication 写入和显式 Publisher heartbeat。 | 决定 Client 及 subscriber state 是否仍存在。 |
| Publisher 活性 | publication 写入和显式 Publisher heartbeat。 | 决定该 Client 拥有的 publication 是否健康和保留。 |
查询只续约已存在的 Client,不创建空 Client,不修改 publisher payload、revision 或健康状态。因此频繁查询可以维持 Client 或 subscriber state,但不能使已超时的 publication 恢复健康,也不能阻止 publisher expiry。显式 Publisher heartbeat 同时续约 Client 和该 Client 的全部 publication。
Publisher timeout 满足如下不等式:
heartbeatIntervalMillis < unhealthyTimeoutMillis < expireTimeoutMillis超过unhealthyTimeoutMillis时 publication 保留但转为 unhealthy;恢复 Publisher 活性时恢复为 active,并仅在公开健康投影变化时产生CHANGE。超过expireTimeoutMillis时删除该 Client 的全部 publication,但如果 Client 仍有 subscriber state 则保留 Client。Client 自身过期时释放其全部 publisher 和 subscriber state。
只有责任节点执行 native Client 和 Publisher 超时调度。ClientData replica 通过现有 snapshot、verify 和 repair 流程收敛。Replica 最近一次成功 verify 的时间参与其后成为责任节点时的本地超时计算,因此 Client 不需要维护第二个 ownership 标记或单独同步 failover 状态。普通 heartbeat 不在每个间隔广播。
6.4 Apply 事件与可见性
本地 publication 变更、远端ADD/CHANGE、repair 或 snapshot apply 必须沿用 Naming Client/service 事件,重建 publisher index、service storage 和 push view。DELETE使用现有 Client release 路径清理派生状态。VERIFY本身不产生 discovery 变化。
上层 Agent/RAD 投影只消费 NamingServiceStorage结果;HTTP Client manager 不维护第二份 Agent Endpoint projection,也不直接依赖 Agent 定义或 AI Resource 状态。相关的 RAD 协议与 Agent 存储细节可参考 RAD 协议规范 与 Agent 存储规范。
7. Config Notify 契约
Config Notify 是 AP 风格的变更传播路径。它不是持久存储协议,也不承载权威配置内容。
模型如下:
Config write or delete -> ConfigDataChangeEvent -> local DumpService refresh -> AsyncNotifyService fan-out to peers -> ConfigChangeClusterSyncRequest -> peer DumpService refresh -> LocalDataChangeEvent -> client listener push规则要点:
- 权威数据源仍是 Config 持久化层;
- notify request 只携带配置身份、
lastModified、gray name 和兼容字段,不携带完整权威内容; - 接收节点必须按照 Config 规则从持久化层刷新本地 dump/cache;
- 本地缓存变化后,通过
LocalDataChangeEvent触发 listener 和 watch 通知; - 不健康目标 member 应延迟重试,而不是阻塞写路径;
- callback 失败或超时必须按受控 backoff 调度重试;
- peer 已移除时,待处理 notify task 可以成为 no-op。
源码印证:AsyncNotifyService(config/src/main/java/com/alibaba/nacos/config/server/service/notify/AsyncNotifyService.java)从ConfigDataChangeEvent中取出 tenant、grayName 与lastModifiedTs构造ConfigChangeClusterSyncRequest并扇出(fan-out)到 peer;接收侧由 ConfigChangeClusterSyncRequestHandler.java 处理,远端据此刷新本地 dump 缓存并触发LocalDataChangeEvent,最终推动 client listener 与 watch 通知。
对于 Config,AP notify 成功表示 peer 节点已被通知刷新服务状态。它不替代持久化成功,也不使推送 payload 成为权威内容。
8. 失败语义
AP 使用方必须处理部分成功。核心规则:
- 本地写成功时,可能尚未被所有 peer 观测;
- retry 可能导致重复操作,因此 apply 逻辑必须幂等,或由 revision、timestamp、operation type、当前状态保护;
- 超时不能证明远端操作一定没有发生;
- 远端旧状态必须通过 verify、snapshot、重新查询或领域特定 reload 修复;
- AP 恢复过程必须可以通过日志、指标、trace 或诊断观察;
- AP 失败不得静默地把运行时状态转化为持久元数据。
这解释了为什么 Distro 的DistroDataProcessor.processData返回 boolean 表示处理是否成功,以及为什么DistroFailedTaskHandler会把失败操作转换回延迟重试任务——部分成功是 AP 路径的常态,而不是异常。
9. 边界规则
- AP 一致性是最终收敛,不是强一致。
- 本地
NotifyCenter事件本身不是 AP 一致性;只有领域定义了远端传播和修复行为时,它才成为 AP 行为的一部分。 - Distro 是运行时数据的正式共享 AP 框架。Naming 使用它同步包括通用 HTTP connection-based Client 在内的临时 Client state。Config Notify 是 Config 特定的 cache/listener 可见性 AP 通知路径。
- AP 路径不得用于权限、namespace 元数据、持久服务元数据、插件状态或数据库 schema 状态。
- 除非接口规范显式暴露,AP payload 是内部集群契约。
- AP 传输必须遵循内部 RPC 的鉴权、来源、payload 和重试规则(参见 内部 RPC 与集群请求规范)。
10. 相关规范
- 基础能力规范
- 内部 RPC 与集群请求规范
- 远程连接生命周期规范
- 集群成员规范
- CP 一致性规范
- 持久化与 Dump 规范
- 任务执行规范
- 事件分发与 NotifyCenter 规范
- Config 规范
- Config 监听与订阅规范
- Naming 一致性与客户端状态规范
- Agent 存储规范
- RAD 协议规范
- gRPC API 规范
小结
AP 路径是 Nacos 面向高更新频率临时运行时状态的核心一致性选择。通过本文可以看到,Distro 框架在core模块中以DistroProtocol为入口、以组件注册表与任务引擎为骨架,把 sync/verify/load/retry 全生命周期托付给可配置的延迟与执行任务;Naming 通过Nacos:Naming:v2:ClientDataresource type 复用该框架同步临时 Client state;Config 则通过ConfigChangeClusterSyncRequest走轻量 notify 路径维持缓存与 listener 可见性。理解这套 AP 契约,是深入阅读 Nacos 集群一致性与后续 CP、持久化规范的直接前提。
【免费下载链接】nacosan easy-to-use dynamic service discovery, configuration and service management platform for building AI cloud native applications.项目地址: https://gitcode.com/GitHub_Trending/na/nacos
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考