本页目录
共识在实际系统中的实践
etcd、Consul、ZooKeeper——三个实际系统中同一个共识问题的三种工程解法。Raft+boltdb、Raft+Gossip、ZAB,共识协议是核心但不是全部:线性一致性读、租约、watch 机制和成员管理各有一套工程取舍。
etcd
etcd 是最广泛使用的 Raft 实现(Go),Kubernetes 的核心依赖——所有 K8s 对象(Pods, Services, ConfigMaps)都存在 etcd 中。
架构
每个写请求 → Raft propose → commit → apply to boltdb → response。读请求有两种模式:
- Serializable Read: 从本地 boltdb 直接读(不经过 Raft)→ 可能返回旧数据(stale)
- Linearizable Read (默认): ReadIndex 机制——1) leader 记录当前 commitIndex; 2) 发心跳给多数 follower 确认自己仍是 leader; 3) 等状态机 apply 到 ≥ 该 commitIndex; 4) 读。这保证了读不会看到已被更替的 leader 的数据。
Watch
etcd 支持 long-poll watch: client 指定 key prefix → server 返回 change events。Kubernetes 的 controller pattern (Informer) 基于 etcd watch 实现。
Consul
Consul 用 Raft 做服务目录(service catalog)的一致性复制,用 Gossip (SWIM) 做成员发现和健康检查传播——分离了"需要一致性的数据"和"可以最终一致的数据"。
设计原则:服务注册(谁在提供什么服务)需要强一致 → Raft。成员存活检测(谁还活着)可用最终一致 → Gossip。
ZooKeeper (ZAB 协议)
ZooKeeper 的 ZAB (ZooKeeper Atomic Broadcast) 协议比 Raft 早 8 年,设计目标不同:
- Raft: 所有 server 的 log 完全相同
- ZAB: leader 的 log 是权威的,followers 可能 leader 多一些 entries(但可以被 overwrite)
ZAB 的"顺序保证"不是全局 linearizability order 像 Raft,而是 FIFO client order——client 的多个请求保持发送顺序,但不同 client 之间没有顺序保证。
为什么 ZooKeeper 还活着
ZooKeeper 的先发优势和 Java 生态导致大量遗留系统依赖它(Kafka 旧版本、Hadoop、HBase),但新系统越来越倾向 etcd(更简、Go-native、K8s 依赖)。
共识的应用场景
共识不是万能的——它解决的是"多个节点对单个值达成一致":
该用共识: 不该用共识:
- Leader election - 大容量数据复制 (用 gossip)
- 分布式锁 - 非 leader-based 的复制
- 配置中心 - 健康检查
- 元数据管理 - 日志聚合
大多数系统不需要共识——它们需要的是最终一致性的复制(Gossip, async replication)或者单 master + WAL(大多数 SQL DB 的主从复制)。
参考
- etcd: github.com/etcd-io/etcd, raft.github.io
- Consul: consul.io/docs/architecture
- ZooKeeper: "ZooKeeper: Wait-free coordination for Internet-scale systems" (2010)
Keywords: etcd, Raft, boltdb, Consul, Gossip, ZooKeeper, ZAB, linearizable read, ReadIndex