为什么我们需要共识?
在分布式系统中,最难的问题不是“如何计算”,而是“如何达成一致”。
当你有 3 台服务器(节点),其中一台说 x=1,另一台说 x=2,谁是对的?
如果没有共识算法,Kubernetes 的大脑(Etcd)就会精神分裂,整个集群就会崩溃。
Paxos 曾是唯一的真理,但它太难懂了(连作者自己都承认)。 Raft 的出现,就是为了 可理解性 (Understandability)。
一、Raft 的三个子问题
Raft 将共识问题分解为三个独立的子问题:
- Leader Election(领导者选举):谁说了算?
- Log Replication(日志复制):如何把指令同步给小弟?
- Safety(安全性):如何保证已提交的数据不丢失?
1.1 领导者选举 (Leader Election)
Raft 节点有三种状态:Follower(跟随者)、Candidate(候选人)、Leader(领导者)。
- 心跳机制:Leader 必须不断向 Follower 发送心跳(AppendEntries RPC,哪怕是空的)。
- 选举超时 (Election Timeout):如果 Follower 在一段时间内(比如 150-300ms 随机)没收到心跳,它就认为 Leader 挂了。
- 发起投票:
- Follower 变成 Candidate。
- Term(任期)加 1。
- 给自己投一票。
- 向其他节点广播 RequestVote RPC。
谁能当选? 获得 大多数 (Majority) 选票的节点当选。 如果有两个 Candidate 同时发起,票数瓜分(Split Vote),谁都不过半? Raft 利用 随机超时时间 巧妙解决了这个问题。下次选举,总有一个跑得快。
1.2 日志复制 (Log Replication)
一旦 Leader 确立,客户端的所有写请求都发给 Leader。
- Leader 把指令(如
SET x=1)追加到自己的 Log。 - Leader 并行发送 AppendEntries RPC 给所有 Follower。
- Follower 收到并写入本地 Log,返回 Success。
- 提交 (Commit):当 Leader 收到 大多数 Follower 的成功响应,它就将该日志条目标记为 Committed,并应用到状态机(State Machine)。
- Leader 告诉客户端:写入成功。
关键点:只要日志被 Committed,它就永远不会丢失(除非大多数节点永久损坏)。
二、安全性与脑裂 (Brain Split)
如果网络分区了怎么办?
比如 5 个节点(A, B, C, D, E)。
网络把它们切成了 [A, B] 和 [C, D, E]。
- 旧 Leader A 在小分区。它收不到大多数的心跳响应,无法提交新日志。
- 大分区
[C, D, E]会选出新 Leader C。 - 客户端连到 C,写入成功(因为 C 有 3 票,过半)。
- 客户端连到 A,写入会一直阻塞或失败。
当网络恢复时: A 发现 C 的 Term 更大,A 自动退位变成 Follower。 A 会从 C 那里同步最新的日志,自己未提交的旧日志会被覆盖。 结论:Raft 永远保证强一致性 (Strong Consistency)。
三、Etcd 实战优化
Etcd 使用了 Raft,但做了很多工程优化:
- 预投票 (Pre-Vote):Candidate 在发起正式选举前,先问问大家“你们觉得我能行吗?”防止网络抖动的节点频繁打断集群。
- 批处理 (Batching):把多个写请求合并成一个网络包发送。
- Pipeline:Leader 不等待上一个 RPC 返回就发送下一个。
总结
Raft 不是魔法,它是严格的逻辑推演。 理解了 Raft,你就理解了为什么 K8s 需要奇数个 Master 节点,为什么 Etcd 对磁盘延迟那么敏感(写日志要 fsync)。 在这个不确定的世界里,Raft 给了我们确定的答案。