Nacos 如何实现 Raft 协议?
Nacos 自身并没有从零开始实现 Raft 协议。它采用了更成熟的做法:
-
集成成熟的 Raft 库: Nacos (特别是 Nacos 2.x 版本) 集成了一个成熟、经过生产验证的 Raft 协议实现库。最核心的是 JRaft(一个基于 Java 的 Raft 算法实现,源自蚂蚁集团/Alipay)。通过集成 JRaft,Nacos 直接利用其提供的 Raft 能力,处理复杂的 Raft 细节,如日志复制、领导者选举、成员变更等。
-
应用范围: Nacos 主要在 集群模式 下使用 Raft 协议,并为 CP (Consistency Protocol) 提供了良好的支持。Raft 在 Nacos 中主要用于:
- Nacos 服务器节点间的领导者选举 (Leader Election): 在 Nacos 集群中,需要有一个 Leader 节点来负责处理写操作或协调任务,以保证数据的一致性。Raft 协议被用来在集群节点中自动选举出Leader。
- 元数据和关键数据的一致性: 对于需要强一致性的数据(例如服务的元数据、持久化的配置信息、集群节点信息等),Nacos 会利用 Raft 的日志复制机制,确保这些数据在集群中的多数派节点上达成一致。当 Leader 收到写请求时,它会将操作记录为一条日志条目,通过 Raft 协议将这条日志复制给 Follower 节点,只有当日志被多数派节点确认后,操作才会被提交并应用到状态机(即 Nacos 的业务数据存储)。
- 集群成员管理: Raft 也参与管理 Nacos 集群自身的成员变更(节点的加入和离开)。
-
与 Nacos 业务逻辑的结合:
- Nacos 将需要通过 Raft 保证一致性的操作封装成 Raft 的日志条目 (Log Entry)。
- Nacos 将其业务数据存储(如内存中的服务注册表、配置信息等)作为 Raft 的状态机 (State Machine)。
- 当 Raft 协议确定一条日志条目已经被提交 (Committed) 后,Nacos 会调用 JRaft 提供的回调接口,将这条日志条目中的操作应用 (Apply) 到自己的状态机上,从而更新业务数据。
重要区分:
- 并非所有数据都走 Raft: Nacos 为了性能和可用性,区分了 AP (Availability Priority) 和 CP (Consistency Priority) 数据。对于服务实例的注册与发现这类要求高可用和高性能的数据(AP 数据),Nacos 2.x 主要使用其自研的 Distro 协议(一种类 Gossip 的协议,优化了AP场景下的数据同步)进行节点间的数据同步,而不是 Raft。只有那些明确标记为需要强一致性(CP)的数据或操作(如持久化服务、部分配置管理操作、集群管理)才会走 Raft 协议。
Raft 协议的关键组件和流程
Raft 是一种比 Paxos 更易于理解和实现的分布式一致性算法。它的核心思想是将一致性问题分解为三个相对独立的子问题:
-
领导者选举 (Leader Election):
- 组件:
- 节点角色:
- Leader (领导者): 集群中同一时间只能有一个 Leader。它负责处理所有客户端的请求(写操作),管理日志复制,并向 Follower 发送心跳。
- Follower (跟随者): 被动角色。响应来自 Leader 和 Candidate 的请求。如果一段时间内没有收到 Leader 的心跳,就会转变为 Candidate并发起选举。
- Candidate (候选人): 用于选举新 Leader 的临时角色。Follower 超时后转变为 Candidate,向其他节点请求投票。
- 任期 (Term): 如果 Candidate 赢得选举,它在该任期内担任 Leader。如果选举失败(没有获得多数票),则退回 Follower 状态,任期用于检测过期的信息。
- 节点角色:
- 流程:
- 初始状态或 Leader 失联后,Follower 在等待 Leader 心跳超时后,增加自己的当前任期号,转变为 Candidate。
- Candidate 向集群中的其他所有节点发送
RequestVote
RPC(远程过程调用),请求投票。 - 其他节点收到
RequestVote
请求后,如果在当前任期内尚未投票给其他 Candidate,并且 Candidate 的日志至少和自己一样 (Raft 的安全性保证),则投票给该 Candidate,并重置自己的选举计时器。 - Candidate 如果收到了来自集群多数派节点的投票,则赢得选举,成为新的 Leader。
- 成为 Leader 后,立即向所有 Follower 发送心跳(空的
AppendEntries
RPC)以宣告自己的领导地位,并阻止新的选举发生。 - 如果在选举期间,Candidate 收到了来自具有更高或相等任期的新 Leader 的
AppendEntries
RPC,则承认该 Leader,并立即转变为 Follower。 - 如果选举超时(例如发生网络分区导致选票分裂),没有 Candidate 获得多数票,则当前任期结束,Candidate 增加任期号,开始新一轮选举(通过随机化的选举减少冲突)。
- 组件:
-
日志复制 (Log Replication):
- 组件:
- 复制日志 (Replicated Log): 每个节点都维护一个包含一系列命令(操作)的日志。日志条目按顺序编号(索引),并包含对应的任期号。
- 状态机 (State Machine): 实际存储业务数据的地方。所有节点的状态机初始状态相同。
- 提交索引 (Commit Index): Leader 维护的所有日志条目的索引。
- 流程:
- 客户端的写请求首先发送给 Leader。
- Leader 将该操作作为一个新的日志条目追加到自己的日志末尾。
- Leader 通过
AppendEntries
RPC 并行的将新的日志条目发送给所有 Follower。 - Follower 收到
AppendEntries
请求后,进行一致性检查(检查前一个日志条目的索引和任期是否匹配),如果通过,则将新的日志条目追加到自己的日志中,并向 Leader 发送成功响应。 - 当 Leader 收到多数派 Follower 对某个日志条目的成功响应后,Leader 就认为该日志条目是已提交 (Committed) 的。Leader 会更新自己的
Commit Index
。 - Leader 在后续的
AppendEntries
RPC(包括心跳)中,会告知 Follower 当前的Commit Index
。 - Follower 收到 Leader 的
Commit Index
后,会知道哪些日志条目是已提交的。 - 节点(包括 Leader 和 Follower)按顺序将已提交的日志条目应用(Apply)到自己的状态机中,更新业务数据。Raft 保证所有节点最终会以相同的顺序应用相同的日志条目。
- 组件:
-
安全性 (Safety):
- Raft 包含多项机制来确保系统的一致性和正确性(即使在发生网络分区、节点崩溃等故障):
- 选举安全 (Election Safety): 每个任期内只有一个 Leader 会被选举出来。
- 领导者只追加 (Leader Append-Only): Leader 不会覆盖或删除自己的日志条目,只会追加。
- 日志匹配 (Log Matching Property): 如果两个不同日志中的条目拥有相同的索引和任期号,那么它们存储的命令是相同的,并且它们之前的所有日志条目也都完全相同。这是通过
AppendEntries
中的一致性检查实现的。 - 领导者完整性 (Leader Completeness Property): 如果某个日志条目在某个任期被提交了,那么它一定会出现在所有更高任期的 Leader 的日志中。
- 状态机安全 (State Machine Safety Property): 如果一个节点已经将某个索引的日志条目应用到其状态机,那么其他任何节点在相同索引位置应用的一定是相同的日志条目。这是因为只有已提交的日志才能被应用。
- Raft 包含多项机制来确保系统的一致性和正确性(即使在发生网络分区、节点崩溃等故障):
总结: Nacos 利用 JRaft 库实现了 Raft 协议,主要用于 Nacos 集群自身的 Leader 选举和 CP 数据的强一致性同步。Raft 协议通过领导者选举、日志复制和一系列安全性机制,确保了分布式环境下数据的一致性。