【etcd】单 Raft 组与服务器角色:Leader、Follower、Learner 与 request 路由
内容提要
etcd集群采用单一Raft组管理所有键值数据,与TiKV的Multi-Raft形成对比。EtcdServer负责状态机,raftNode适配Raft库。Leader处理写请求,Follower转发,Learner不参与投票。写路径经Raft提交,读路径可本地或ReadIndex,Watch和Lease各有独立机制。
延伸解读
单 Raft 组的工程取舍
etcd 所有键值数据由单一 Raft 组管理,带来全局 Revision 全序和简单运维模型,但写吞吐不随节点数扩展。文章指出这是刻意设计,而非未实现分片。当 quota 与单写者 bbolt 同时触顶时,应评估迁移 Multi-Raft 系统或拆分集群,而非增加 Follower 期望提升写性能。
EtcdServer 与 Raft 库的职责边界
EtcdServer 是状态机宿主,raftNode 是适配层,Raft 库只处理日志复制,不感知 MVCC。排障时需区分问题层级:gRPC/API 层、共识层、持久化层、状态机层或成员层。committedIndex 由 Raft 推进,appliedIndex 由 apply 协程推进,二者分叉是 apply 轴与 WAL 轴的分界。
Learner 的用途与排障注意
Learner 是 non-voting member,接收日志复制但不参与 quorum 和选举,用于新节点 catch-up 或跨地域观测副本,避免成员变更时的临时双 majority 问题。排障时注意:Learner 的 Raft Applied Index lag 可能较高,但不影响集群健康,将其当作 Follower 计算 quorum 会误判。
请求路由的差异:写、读、Watch、Lease
写请求必须经 Leader 和 quorum 提交;Follower 收到写请求会转发 Leader,多一跳 RTT。读请求默认不走 Raft,Serializable 读可能读到 stale 状态,Linearizable 读走 ReadIndex。Watch 流建立不经过 Raft,但依赖 MVCC 版本保留;Lease KeepAlive 在 Leader 本地续期,不经 Raft,Leader 切换时 TTL 语义会变化。
Q&A
etcd集群中所有键值数据由多少个Raft组管理?
etcd集群中所有键值数据由同一个Raft组管理,即单Raft组架构。
etcd中EtcdServer和raft.Node的职责边界是什么?
EtcdServer负责状态机宿主,包括MVCC、Lease等,而raft.Node负责共识算法,如选举、日志复制等。EtcdServer通过raftNode适配层与raft.Node交互。
etcd中Follower收到写请求后如何处理?
Follower收到写请求后会转发给Leader,由Leader负责propose并提交,客户端可能感知到额外RTT,但不影响线性一致性。
etcd中Learner节点为什么不参与quorum计数?
Learner是non-voting member,不参与quorum计数和Leader选举投票,目的是在不扰动quorum的情况下让新节点追赶日志,之后可提升为voting member。
etcd中读请求是否总是经过Raft?
不是。默认的Serializable读直接读本地MVCC状态,不经过Raft;而Linearizable读需要走ReadIndex流程,由Leader协调。
etcd中Watch和Lease的机制是否依赖Raft?
Watch建立gRPC流后由watchableStore在apply之后推送事件,不经过Raft;Lease的Grant/Revoke走Raft,但KeepAlive在Leader本地续期,不经Raft提交。
etcd单Raft组架构的写吞吐为什么有硬顶?
因为所有写请求都必须经过同一个Raft组复制,无法按key分片并行复制,导致写吞吐不随节点数线性扩展。