Nacos CP 一致性基础规范深度解析:JRaft 协议模型、读写语义与传输鉴权
【免费下载链接】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 官方 Foundation CP Consistency Spec 的展开式解读,系统梳理 Nacos 在 CAP 理论中选择 CP 路径时的整体设计:从CPProtocol/RequestProcessor4CP的抽象契约,到JRaftProtocol多 Raft group 运行时,再到读写语义、Snapshot 恢复、JRaft 传输鉴权与边界规则。读者读完可掌握 Nacos 内置 CP 实现的分层结构、nacos_config、naming_persistent_service_v2、plugin_state等核心 Raft group 的职责划分,以及如何在自研领域内正确接入 CP 基础层。
1. 定位:Nacos 中的 CP 路径
AP 与 CP 是 CAP 理论中的两种一致性取舍。在 Nacos 中,CP 路径在分区容忍(partition tolerance)前提下,优先保证强顺序提交(strongly ordered committed state):当 quorum(法定多数)、leader 或协议 group 不可用时,CP 操作可以选择失败或暂时不可用,而绝不会接受分叉写入(divergent writes)。这一原则在 foundation-cp-consistency-spec.md 中被明确为第一条定位。
当前仓库内置的 CP 实现是通过JRaftProtocol接入的 JRaft(JRaftProtocol.java)。Nacos 允许通过 SPI 加载其他CPProtocol实现,但仓库内支持的 CP 语义由 Raft/JRaft 定义,替换实现时必须保持既有抽象语义。
从源码结构看,一致性模块(consistency)与核心实现模块(core)做了清晰分层:
consistency模块只定义抽象契约:ConsistencyProtocol、CPProtocol、RequestProcessor4CP、ProtocolMetaData、SnapshotOperation;core模块承载 JRaft 具体实现:JRaftProtocol、JRaftServer、NacosStateMachine、ProtocolManager等。
2. CP 资源规则:什么样的状态适合走 CP
规范从正反两面给出了 CP 状态的适用边界。
适合使用 CP 状态的资源特征:
- 由管理面(management)或服务端控制路径拥有的持久状态;
- 必须在客户端消失、服务端重启后继续存在的状态;
- 代表运维人员或开发人员意图、且需要覆盖运行时注册的状态;
- 在一个逻辑 group 内要求**单一提交顺序(single commit order)**的写入;
- 可以从snapshot 和已提交日志恢复的状态。
不适合使用 CP 的状态:高频可丢弃的运行时状态,只要 AP 最终收敛即可满足,应使用 AP 一致性规范。
CP 使用方在接入前必须定义清楚 7 项内容:
- Raft group name 与归属;
- write request 的形态与 operation 值;
- read request 的形态与读取可见性;
- 确定性的
onApply行为; - snapshot 保存/加载的形态与兼容性;
- 用户可见操作对 leader 或 readiness 的要求;
- group 无 leader、无 processor 或无 quorum 时的错误行为。
这 7 项定义正是后文 processor 契约、读写语义和 snapshot 规则的源头。
3. 协议模型:从 ProtocolManager 到 StateMachine
共享一致性接口是ConsistencyProtocol,CP 通过CPProtocol对其特化。从源码看:
- ConsistencyProtocol.java 定义了一致性协议的完整生命周期:
init(Config)初始化、addRequestProcessors()注册处理器、protocolMetaData()返回元数据、getData()/aGetData()读、write()/writeAsync()写、memberChange()成员变更、isReady()就绪判断、shutdown()关闭; - CPProtocol.java 仅在父接口之上增加了一个
isLeader(String group)方法,用于判断本节点是否为某个 group 的 leader。
规范给出的 CP 分层模型如下:
ProtocolManager -> CPProtocol(JRaftProtocol) -> RequestProcessor4CP per group -> JRaftServer multi-raft group -> NacosStateMachine -> processor.onApply / processor.onRequest -> snapshot operations各层职责在仓库中均有对应实现:
ProtocolManager(ProtocolManager.java)以ip:raftPort形式注入 CP member,并异步将 member 变化事件传播给 CP 协议;JRaftProtocol内部持有JRaftServer(多 Raft group 运行时)与JRaftMaintainService(运维命令),addRequestProcessors()直接调用raftServer.createMultiRaftGroup(processors);NacosStateMachine(NacosStateMachine.java)负责把已提交日志 apply 到领域层,最终回调processor.onApply / onRequest。
JRaftProtocol.init()中还注册了RaftEvent订阅者(JRaftProtocol.java):每次 Raft 事件(leader 变化、term、group 成员、错误信息)到来时,都会更新ProtocolMetaData并注入本节点成员信息(raftMetaData),同时刷新 Raft group 监控指标——这就是规范中"protocolMetaData()记录 leader、term、group members 和错误"的落地实现。
4. Request Processor 契约
每个 CP 领域通过注册一个RequestProcessor4CP接入基础层。RequestProcessor4CP.java 是抽象类,仅默认返回空的loadSnapshotOperate()列表,其余契约由规范约束:
group()必须返回稳定且唯一的 group name;- 一个 group 在同一个 server runtime 中应只有一个权威 processor;
onApply(WriteRequest)必须是确定性的,不得依赖慢速远端 IO(例如不得在 apply 路径上做 RPC 调用);onRequest(ReadRequest)必须按照该 group 的读规则返回响应;- group 需要 snapshot 恢复时,
loadSnapshotOperate()必须声明 snapshot operation; - processor 必须将未知 operation 作为显式失败处理;
- processor 只能在committed apply 更新本地状态之后,按 事件分发与 NotifyCenter 规范发布领域事件。
规范刻意强调了职责边界:processor 拥有领域语义(domain semantics),CP 基础层拥有 group 路由、leader 转发、日志提交、read-index 处理、metadata 与 snapshot 集成。领域代码只需要关心"状态如何被 apply",而不用关心"日志如何被复制"。
5. JRaft 运行时规则
JRaft 被用作多 group 的 CP 运行时,核心规则包括:
- 每个
RequestProcessor4CP创建一个 Raft group(见JRaftServer.createMultiRaftGroup); - 写请求提交到 group leader,或被转发到当前 leader;
- 已提交日志通过
NacosStateMachineapply; - follower replay 会 apply 已提交写入,但忽略没有 closure 的 follower-local read entry;
- 读路径优先尝试 Raft read-index,失败时可回退到 leader read;
protocolMetaData()记录 leader、term、group members 与错误;isReady()表示协议已启动;strict mode 下还要求 group 已存在 leader;- member 移除通过 Raft peer-change command 处理,新增节点启动时自行注册到集群(
ProtocolManager通过memberChange(Set<String>)以ip:raftPort形式下发成员列表,JRaftProtocol.memberChange()最多重试 5 次 peer 变更)。
一个容易被误用的点是:实现中的超时是运维默认值,不是公开 API 保证。例如JRaftProtocol.write()同步等待 10 秒、getData()等待 5 秒,这些只是实现层默认值;除非领域 API 显式声明,领域规范不得把 JRaft 超时值暴露为用户可见的正确性契约。
5.1 JRaft 传输鉴权(JRaft Transport Authentication)
JRaft 原生 gRPC 是服务端之间的内部传输(inner transport)。新版 JRaft client 始终通过 gRPCCallCredentials携带配置的 Nacos server identity,服务端始终在分发给 JRaft processor 之前通过ServerInterceptor校验。
滚动升级采用临时两态迁移:
COMPATIBLE -> ENFORCEDCOMPATIBLE(兼容)态:缺失或错误 credential 会被限频记录但请求继续执行,因为旧版本 member 无法携带 credential;- 每个新 member 发布临时能力
supportJraftAuth=true; - 当完整 member 视图中的所有成员都上报该能力后,本机自动且不可逆地进入
ENFORCED(强制)态; ENFORCED态下,缺失或错误 credential 会在 processor 执行前以 gRPCUNAUTHENTICATED拒绝。
仓库实现对应 JRaftAuthUpgradeCoordinator.java,其中定义了状态文件名jraft-auth-enforced.state与state=ENFORCED标记。规范的补充约束:
- 状态迁移是**单向(monotonic)**的:member 新增、删除或元数据更新不得使已强制的进程回到兼容态;
- 运行态必须先**锁存(latch)**强制鉴权,再写入
{nacos.home}/data/jraft-auth-enforced.state;状态文件写入失败不得延迟强制鉴权,后续定时检查必须持续重试; - 服务端重启发现该文件时必须立即强制鉴权;
- 状态文件不包含 server identity 或 member 数据,也不是运维回退开关;
- 能力发现、兼容放行、状态迁移与状态文件处理由一个独立兼容组件拥有;该组件从引入起即标记废弃(Javadoc 明确在Nacos 4.0.0删除),且不得拥有永久 credential 解析或校验逻辑。
进入强制状态后的降级代价是明确警告的:向不携带 JRaft credential 的旧版本做混合滚动降级不保证无损或可用。旧 client 无法调用已强制的新 server,leader 选举、复制、ReadIndex、leader 转发、snapshot 与 CLI 操作都可能因 leader 与 quorum 分布而失败。
6. 当前 CP 使用方
规范以表格形式列出了仓库内当前的 CP group:
| Group | 归属 | 用途 |
|---|---|---|
nacos_config | Persistence 与 Config 内置存储 | 复制内置存储操作,并使 Config dump 等待可读的 committed state |
naming_persistent_service_v2 | Naming | 复制持久实例注册、更新和注销操作 |
naming_service_metadata | Naming | 复制 service metadata 和 cluster metadata |
naming_instance_metadata | Naming | 复制运维态 instance metadata |
naming_persistent_service | Naming 运维 | 复制遗留 naming switch/domain 状态 |
plugin_state | Core plugin | 选择内置 Raft synchronizer 时,复制插件状态和运行时配置变化 |
以plugin_state为例,代码中的 group 定义可在 PluginStateProcessor.java(GROUP = "plugin_state")与 RaftPluginStateSynchronizer.java(PLUGIN_STATE_GROUP = "plugin_state")中找到对应。新增 CP 使用方必须把 group 加入相关领域规范,并定义该 group 提交的资源语义。
7. 读写语义
7.1 写规则
- write request 必须包含group、operation 和序列化数据;
- 成功表示写入已按该 group processor 契约被接受并 apply;
- 失败必须向调用方暴露,不能静默重试后仍作为用户可见成功返回;
- 当调用层可能重复提交时,领域 apply 逻辑必须幂等或受保护;
- 写入不得在 committed apply 之前发布事件。
plugin_state是一个值得注意的特殊领域:Core 插件领域把它作为内置 Raft synchronizer 的可选运行时资源,而不是 Spring context 构造的前置条件。standalone 模式以及显式选择自定义PluginStateSynchronizerProvider的集群都不注册该 group。约束包括:
- processor 不得在 bean 构造器中初始化 CP 或注册 group;
- Core 接受本地插件状态和配置后,再异步初始化默认 Raft synchronizer 和 group;
- CP 初始化或 group 注册失败只记录日志并将集群插件写能力标记为不可用,不得导致 Nacos 启动失败;
- 依赖该 group 的调用在其不可用期间必须返回明确的服务端错误,不得静默按 standalone 写入或切换到其他 synchronizer。
7.2 读规则
- read request 必须包含目标 group 和序列化查询数据;
- group processor 定义是否支持读取以及读取哪类状态;
- read-index 失败时可以回退到 leader read;
- 如果领域绕过 CP 从本地 cache 读取,必须在领域规范中记录stale-read 容忍度(即允许读到多旧的数据、可接受多久的滞后)。
8. Snapshot 与恢复
Snapshot 规则:
- 具有可恢复状态的 group 应提供
SnapshotOperation(接口定义见 SnapshotOperation.java); - snapshot 格式与兼容性由领域 processor 拥有;
- snapshot save/load 必须保留重启后重建服务状态所需的数据;
- 没有 snapshot operation 的 group 依赖日志 replay 或独立持久化;
- 领域规范必须定义 snapshot 恢复如何与本地 cache 和派生索引交互。
一个关键实践:对 Config 内置存储而言,启动阶段会等待 CP metadata 表明数据可读后,才继续 dump 恢复。这是建立在 CP 基础能力与持久化与 Dump 规范之上的 Config 持久化规则——它保证了 dump 不会在 committed state 尚未就绪时读到不完整数据。
9. 边界规则
最后,规范以边界规则收束,防止 CP 能力被误用:
- CP 一致性是在 group 内的强顺序提交,不保证每个接口都通过 CP 读取;
- CP group 拥有committed 领域状态,不拥有传输重试、SDK redo 或 AP 运行时状态;
- 除非领域规范明确要求 CP 语义,不应使用 JRaft group 串行化高频临时状态;
- CP processor 不得重新定义公开 API 字段、资源身份或鉴权规则;
- CP metadata 是运维协议状态,可通过授权的 ops API 暴露,但不是领域资源身份;
- 替换 CP 实现时,必须保持本文定义的
CPProtocol、RequestProcessor4CP、group、snapshot、metadata 与错误语义; - 只有领域规范已定义本地读取视图和明确写入不可用语义时,才允许把可选领域的 group 注册失败与服务端启动隔离;该例外不降低内置 Config 存储等关键 CP 领域的 readiness 要求。
10. 延伸阅读
CP 一致性是 Nacos 基础能力规范体系的一部分,建议结合以下文档整体阅读:
- 基础能力规范
- AP 一致性规范
- 持久化与 Dump 规范
- 事件分发与 NotifyCenter 规范
- 集群成员规范
- Config 规范
- Config 持久化、Dump 与历史规范
- Naming 一致性与客户端状态规范
- 鉴权与权限规范
对应的核心源码入口:抽象契约见 consistency/src/main/java/com/alibaba/nacos/consistency,JRaft 实现见 core/src/main/java/com/alibaba/nacos/core/distributed/raft,传输鉴权升级协调器见 JRaftAuthUpgradeCoordinator.java。
【免费下载链接】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),仅供参考