3. etcd 架构总览:gRPC API层、Raft共识模块、WAL日志、MVCC存储引擎、Watch机制

etcd 是一个高度可靠的分布式键值存储系统,其内部架构围绕“强一致性”与“高性能”两大目标进行设计。本章将自上而下剖析 etcd 的核心模块:gRPC API 层、Raft 共识模块、WAL 日志、MVCC 存储引擎以及Watch 机制。理解这五大模块的职责与协作关系,是掌握 etcd 压测与调优的基础。

架构视角:etcd 的架构可以抽象为“共识层 + 存储层 + 监听层”。Raft 模块保证日志的全局有序复制,WAL 提供持久化保障,MVCC 实现并发控制与历史版本查询,Watch 机制则支撑了分布式系统中的事件驱动模式。

3.1 整体架构分层

etcd 的代码模块遵循清晰的层级划分,从用户请求到数据落盘,依次经过以下核心组件:

etcd 核心架构分层图 gRPC API 层 KV / Cluster / Maintenance / Watch / Lease / Auth Raft 共识模块 Leader 选举 · 日志复制 · 安全性 WAL 日志 (Write-Ahead Log) 顺序追加 · 崩溃恢复 · 日志校验 MVCC 存储引擎 多版本并发控制 · 索引树 (B+tree) · 后端 boltdb Watch 机制 事件监听 · 流式推送 · 历史事件回放

3.2 gRPC API 层:统一的通信入口

etcd 的所有客户端交互均通过 gRPC 协议完成。gRPC 基于 HTTP/2,支持双向流、多路复用和高效的 Protobuf 序列化。API 层定义了以下核心服务:

服务名称 主要 RPC 方法 功能描述
KV Range / Put / Delete / Txn / Compact 键值数据的读写、事务与压缩
Watch CreateWatcher / Watch (流式) 监听键或前缀的变化事件
Lease Grant / Revoke / KeepAlive 租约管理,用于 TTL 自动过期
Cluster MemberAdd / MemberRemove / MemberList 集群成员变更与状态查询
Maintenance Alarm / Defragment / Snapshot / Status 节点维护、碎片整理与快照
Auth Authenticate / RoleAdd / UserAdd 身份认证与权限控制
压测关注点:gRPC 的流式 Watch 和 KeepAlive 会占用大量长连接资源。压测时需关注连接数、请求吞吐量以及 Protobuf 序列化/反序列化的 CPU 开销。

3.3 Raft 共识模块:一致性的核心

Raft 模块是 etcd 实现分布式一致性的基石。它将客户端请求转化为日志条目,并通过以下机制确保集群中所有节点达成一致:

  • Leader 选举:当集群启动或 Leader 失联时,Follower 节点通过超时机制发起选举,获得多数票的节点成为新 Leader。
  • 日志复制:Leader 接收客户端写请求,将日志条目追加到本地,并并行发送 AppendEntries RPC 给所有 Follower。当条目被多数节点持久化后,即视为已提交。
  • 安全性:Raft 保证只有拥有最新日志的节点才能成为 Leader,且已提交的条目不会被覆盖。
Raft 日志复制流程 Client Leader Follower 1 Follower 2 Put(key,val) AppendEntries AppendEntries Raft Log (已提交条目) term:1 term:1 term:2 term:3 term:3
压测陷阱:Raft 的日志复制是串行提交的。当写入压力过大时,Leader 的 AppendEntries 广播会成为瓶颈。压测中应关注 etcd_server_proposals_failed_total 和 etcd_server_leader_changes_seen_total 指标,以判断集群是否因过载而发生 Leader 频繁切换。

3.4 WAL 日志:持久化的基石

WAL (Write-Ahead Log) 是 etcd 保证数据不丢失的关键机制。所有 Raft 日志条目在应用到状态机之前,必须先持久化到 WAL 文件中。WAL 的设计遵循以下原则:

  • 顺序追加:WAL 文件只支持追加写入,避免随机 I/O,最大化磁盘吞吐量。
  • 日志分段:WAL 文件按大小分段(默认 64MB),旧段在快照完成后可被清理。
  • 崩溃恢复:节点重启时,通过回放 WAL 中的日志条目重建内存状态机,确保数据不丢失。
  • 校验和:每个日志条目都包含 CRC 校验,用于检测数据损坏。
WAL 文件属性 说明 压测影响
写入模式 顺序追加,fsync 频率由 --sync-interval 控制 高频 fsync 降低吞吐,但提高数据安全性
文件分段 默认 64MB 一个文件,编号递增 大量小文件可能影响文件系统性能
快照关联 快照生成后,之前的 WAL 文件可被删除 快照频率影响磁盘空间回收与恢复时间

3.5 MVCC 存储引擎:多版本并发控制

etcd 的存储引擎基于 boltdb(一个嵌入式 Go 数据库),并在此基础上实现了 MVCC (Multi-Version Concurrency Control)。MVCC 的核心思想是:每次写操作不直接覆盖旧值,而是生成一个新版本。每个版本由 revision(全局递增的主版本号)唯一标识。

  • 索引树:etcd 在内存中维护一棵 B+tree(treeIndex),用于快速根据 key 查找对应的 revision 列表。
  • 后端存储:实际的 key-value 数据以 (revision, key) -> value 的形式存储在 boltdb 中。
  • 压缩 (Compaction):定期删除历史版本,回收存储空间。压缩操作会删除小于指定 revision 的所有旧版本数据。
压测关注点:MVCC 的版本链长度直接影响读性能。如果未及时执行压缩,历史版本堆积会导致 B+tree 查询变慢,并增加磁盘空间占用。压测时应监控 etcd_mvcc_db_total_size_in_bytes 和 etcd_mvcc_hash_revision。

3.6 Watch 机制:事件驱动的桥梁

Watch 机制是 etcd 实现服务发现、配置变更通知的核心。客户端可以通过 Watch 监听一个 key 或一个前缀,当被监听的 key 发生变化时,etcd 会实时推送事件。

  • 流式推送:Watch 基于 gRPC 双向流,服务端持续推送事件,客户端无需轮询。
  • 事件类型:PUT(创建/更新)和 DELETE(删除)。
  • 历史回放:客户端可以指定一个起始 revision,etcd 会从该 revision 开始回放历史事件,确保客户端不会错过任何变更。
  • Watch 存储:etcd 在内存中维护一个 Watch 树,用于高效匹配 key 与监听者。
Watch 事件推送流程 Client etcd Server Watch(key) Event: PUT/DELETE 事件队列 rev:1001 PUT /a rev:1002 PUT /b rev:1003 DEL /c
压测陷阱:大量 Watch 连接会消耗 etcd 的内存和 CPU。每个 Watch 流都需要在服务端维护一个 goroutine 和缓冲区。如果客户端消费速度跟不上事件产生速度,可能导致缓冲区溢出或内存压力。压测时应监控 etcd_watch_events_total 和 etcd_watch_dropped_total。

3.7 模块协作流程总结

一个完整的 etcd 写请求(例如 Put(key, value))在内部经过以下流水线:

  1. gRPC API 层 接收客户端请求,解析 Protobuf,调用 KV 服务。
  2. Raft 共识模块 将请求封装为日志条目,通过 Leader 广播给 Follower。
  3. WAL 日志 将日志条目顺序追加到磁盘文件,并执行 fsync 确保持久化。
  4. 当日志条目被多数节点确认后,Raft 模块将其标记为已提交。
  5. MVCC 存储引擎 将已提交的日志应用到 boltdb,生成新版本数据。
  6. Watch 机制 检测到 key 变更,将事件推送给所有监听该 key 的客户端。
压测启示:上述流程中,WAL 的 fsync 和 Raft 的多数派确认是主要的延迟瓶颈。压测时可通过调整 --snapshot-count、--sync-interval 以及磁盘类型(SSD vs HDD)来观察对吞吐和延迟的影响。