本文档介绍了 etcd 如何实现 Raft 共识协议来在分布式系统中维护一致的状态。Raft 是 etcd 提供所有数据操作的强一致性保证的核心机制。实现涵盖了领导者选举、日志复制、成员变更和状态机复制。
有关 etcd 服务器整体实现的更多信息,请参阅 核心服务器。有关数据如何存储和管理的信息,请参阅 存储引擎。
来源:server/etcdserver/raft.go server/etcdserver/server.go
etcd 采用了分层架构来实现 Raft,将共识协议与状态机应用分开。主要组件包括:
raftNode:包装 Raft 实现的核心结构。它管理通信通道并与 etcd 的其余部分进行接口。
raft.Node:来自 go.etcd.io/raft/v3 库的实际 Raft 状态机实现。它执行共识算法操作。
rafthttp.Transport:处理 Raft 节点之间的网络通信。
WAL (Write-Ahead Log):在应用 Raft 日志条目之前将其持久化到磁盘,以确保持久性。
snap.Snapshotter:管理系统状态快照的创建和应用,以防止日志无限增长。
EtcdServer 结构包含一个 raftNode 实例(r raftNode),作为共识层和应用之间的集成点。
来源:server/etcdserver/raft.go80-104 server/etcdserver/raft.go106-120 server/etcdserver/raft.go69-78 server/etcdserver/raft.go756-764
实现 etcd 中 Raft 共识的核心结构包括:
来源:server/etcdserver/v3_server.go144-151 server/etcdserver/raft.go171-340
当客户端向 etcd 发送写请求时:
EtcdServer 将其转换为 InternalRaftRequest 并调用 raftRequest()。Node.Propose() 提议给 Raft 状态机。raftNode.start() 方法处理已提交的条目,并通过 applyc 通道将其传递给 EtcdServer。此过程确保所有写操作在被视为成功之前已在集群中一致复制。
来源:server/etcdserver/v3_server.go104-144
etcd 支持两种类型的读操作:
串行化读:这些读操作直接从本地状态机读取,无需咨询 Raft,性能更好,但可能读取到陈旧数据。
可线性化读:这些读操作会通过 Raft 协议,以确保节点在读取之前拥有最新数据。这是通过 ReadIndex 算法实现的,该算法会咨询领导者以确定本地状态是否最新。
处理范围(读)请求的代码展示了这一点。
etcd Raft 实现的核心是 raftNode.start() 中的事件循环 server/etcdserver/raft.go171-340,它:
Ready 结构,其中包含:Raft 使用心跳机制来维持领导地位。领导者会定期向所有跟随者发送心跳,以防止它们启动新的选举。
在 etcd 中,这由 raftNode.start() 方法中的 ticker.C 生成的 tick 驱动。如果在某个时期(选举超时)内,跟随者未收到心跳,它将转为候选人状态并开始新的选举。
当发生领导者变更时,etcd 会更新指标并通知依赖于领导者状态的组件。
来源:server/etcdserver/server.go977-996 server/etcdserver/raft.go242-270
etcd 使用两种机制来确保持久性和管理日志增长:
预写日志 (WAL):所有 Raft 条目在应用前都会写入 WAL,以确保重启后的持久性。
快照:etcd 会定期创建当前状态的快照,允许它截断 WAL 并防止无限增长。
快照过程由 EtcdServer 中的 snapshot() 方法管理 server/etcdserver/server.go977-996,它:
此过程基于配置的 SnapshotCount(默认值:10000 个条目)自动发生。
etcd 使用 Raft 的联合共识算法来安全地更改集群成员。添加或移除成员时:
ConfChange 提案通过 Raft 传递。实现位于 applyConfChange() server/etcdserver/server.go,并处理各种类型的配置更改,包括添加成员、移除成员以及将学习者提升为投票成员。
etcd 暴露了多个与 Raft 相关的指标用于监控:
| 指标 | 描述 |
|---|---|
etcd_server_leader_changes_seen_total | 领导者变更的总数 |
etcd_server_proposals_committed_total | 已提交提案的数量 |
etcd_server_proposals_applied_total | 已应用提案的数量 |
etcd_server_proposals_failed_total | 失败提案的数量 |
etcd_server_proposals_pending | 待处理提案的数量 |
etcd_server_is_leader | 该成员是领导者(1)还是跟随者(0) |
来源:tests/e2e/metrics_test.go142-210
这些指标有助于监控 Raft 共识的健康和性能,并可能指示网络问题、磁盘性能问题或领导者频繁切换等问题。
etcd 的 Raft 实现是其作为分布式键值存储的可靠性基础。通过共识提供强一致性保证,etcd 确保集群中的所有节点在任何已提交的索引处对数据具有相同的视图。
理解 Raft 在 etcd 中的工作方式对于有效运行 etcd 集群和诊断运行中可能出现的问题至关重要。