云原生时代的裁判
第三位选手 etcd,名字来自 /etc 加 distributed——「分布式版的配置目录」。它是 CoreOS 团队 2013 年起的强一致键值存储,gRPC 接口,MVCC 多版本数据,背后由 Raft 协议背书。它的江湖地位一句话定位:Kubernetes 的全部集群状态——Pod、Service、ConfigMap、调度结果——都存在 etcd 里,它挂了整个 K8s 就瘫痪。云原生十年,etcd 把 ZooKeeper 的很多地盘(选主、配置、服务发现、锁)用更现代的方式重做了一遍。本篇先打地基:Raft 入门三件套。
三件套之一:任期——逻辑时钟
Raft 把集群时间切成一段一段的任期(Term),每一段至多一位 Leader。正常态:Leader 周期性发心跳(空 AppendEntries)维系权威;Leader 失联,Follower 等过随机超时就发起选举,Term 加一,进入新任期。Term 就是第 9 篇 ZAB 里 epoch 的对应物——朝代号思想的工业化表达:所有消息都带 Term,收到 Term 更小的消息直接拒绝,旧主复活自动退位,脑裂的账本新旧立判。用逻辑时钟替代物理时钟,正是 Martin 批评 RedLock「时钟不可信」时点出的正解。
三件套之二:选举——随机超时与投票纪律
多个 Follower 同时超时怎么办?Raft 的答案朴素得漂亮:选举超时时间随机化(150-300 毫秒区间内各节点随机),谁先超时谁先喊「选我」,抢跑优势让选票瓜分的概率大幅下降——比 ZAB 那套「比 epoch 比 zxid 比 myid」的连环比较简单直接。真正的安全性藏在两条投票纪律里:其一,一个节点一个任期内只投一票(先到先得),保证多数派只可能选出一个 Leader;其二,候选人的日志不比自己新,就拒绝投票——这条纪律直接锁死了「数据落后者当选」的可能,是「已提交数据绝不丢」的第一道闸。对比 ZAB:Raft 的选举纪律与日志安全性是合在一起的,逻辑上更紧凑。
三件套之三:日志复制——过半才提交
Leader 处理写请求的流程,ZAB 的读者一眼即熟:
客户端写请求 → Leader 追加日志(未提交)
→ 并行发 AppendEntries 给全体 Follower
Follower 追加日志 → 回 ACK
Leader 收到过半 ACK → 日志标记 committed → apply 到状态机
→ 回复客户端成功 → 后续心跳携带 commitIndex 通知全员 apply过半确认才提交——quorum 门闩与 ZAB 一脉相承。Raft 有一条更严的纪律叫 log matching:日志必须连续,AppendEntries 带上前一条日志的索引与 Term 做一致性校验,对不上就回退重传——所有节点的日志在相同位置必然完全相同,杜绝分叉。旧 Leader 复活时,它任期里未过半的日志会被新 Leader 的日志直接覆盖——与 ZAB「旧朝提案作废」殊途同归,只是实现更简单粗暴。
| 概念 | Raft | ZAB 对应物 |
|---|---|---|
| 逻辑时钟 | Term(任期) | epoch(朝代号) |
| 事务序号 | 日志索引(每任期重算) | zxid 低 32 位 |
| 选举防瓜分 | 随机超时抢跑 | 三元组连环比较 |
| 日志一致性 | log matching,强制连续 | 按 zxid 顺序落盘 |
| 提交门闩 | 过半 ACK | 过半 ACK |
etcd 的两个现代化
协议同源之外,etcd 相对 ZK 有两处「代际差」。其一,MVCC 多版本数据:每次修改分配全局单调递增的 revision,历史版本可查——锁场景里这个 revision 就是天然的 fencing token 原料(第 16 篇发力),ZK 的 zxid 藏在元数据里,etcd 把版本做成了一等公民。其二,Watch 是流式的:客户端从指定 revision 起持续订阅变更,不断流,不像 ZK 的一次性 Watch 要反复续订——实现锁的排队唤醒时,代码量直降一个数量级。下一篇把 Raft 的安全性抠到细节:提交规则的两条红线、日志新旧的精确判定、以及「已提交为什么数学上不可能丢」。
评论 (0)