本章定位:Leader 选举是 ZooKeeper 高可用的核心保障。理解 FastLeaderElection 的投票规则和选举流程,才能诊断集群不可用的根因。
定义与作用
当 ZooKeeper 集群启动或 Leader 失效时,所有参与节点通过**FastLeaderElection(FLE)**算法选出新的 Leader。Leader 是集群中唯一处理写请求的节点,选举的快速和正确直接决定集群的可用性。
选举解决的核心问题:
| 问题 | 选举如何解决 |
|---|---|
| 避免脑裂(Split Brain) | Quorum 机制:必须过半数节点同意 |
| 选出最优 Leader | ZXID 最大者优先(拥有最新数据) |
| 处理并发选举 | 两阶段投票 + 选举轮次(epoch) |
| 快速收敛 | 直接通信(P2P),无需协调者 |
核心原理
选举投票交互
投票规则:比较 ZXID(越大越新)→ myid(越大越优先)。Server 3 因 ZXID=101 最大而当选 Leader。
投票比较规则
节点状态转换
| 状态 | 含义 | 何时进入 |
|---|---|---|
| LOOKING | 正在寻找 Leader | 启动时、Leader 失效时 |
| LEADING | 当前是 Leader | 获得多数票后 |
| FOLLOWING | 当前是 Follower | 确认 Leader 后 |
| OBSERVING | 当前是 Observer | 确认 Leader 后(不参与投票) |
完整示例
示例一:观察 Leader 选举日志
场景说明:启动 3 节点集群,观察选举日志输出。
# 启动 Server 1
bin/zkServer.sh start conf/zoo-1.cfg
# 日志关键输出:
# LOOKING - 进入选举
# Notification: myid=1, zxid=0x0, state=LOOKING
# Notification: myid=1, zxid=0x0, state=LOOKING(给自己投票)
# 启动 Server 2
bin/zkServer.sh start conf/zoo-2.cfg
# LOOKING - 进入选举
# Notification: myid=2, zxid=0x0, state=LOOKING
# 收到 Server 1 的投票: myid=1, zxid=0x0
# 比较:myid 2 > 1 → 维持 vote=2
# Server 1 收到 Server 2 投票:myid 2 > 1 → 更新 vote=2
# 启动 Server 3
bin/zkServer.sh start conf/zoo-3.cfg
# Server 2 收到第三票 → 确认 LEADING
# LEADING - LEADER ELECTION TOOK - 200 MS
# Server 1、3 变为 FOLLOWING
操作前后对比:
| 阶段 | Server 1 (myid=1) | Server 2 (myid=2) | Server 3 (myid=3) |
|---|---|---|---|
| 启动 S1 | LOOKING(投自己) | — | — |
| 启动 S2 | 改投 myid=2 | LOOKING(投自己) | — |
| 启动 S3 | 维持投 myid=2 | LEADING | FOLLOWING |
示例二:Leader 宕机后的重新选举
场景说明:模拟 Leader 宕机,观察新 Leader 的选举过程。
# 查看当前状态
bin/zkServer.sh status conf/zoo-1.cfg # Mode: follower
bin/zkServer.sh status conf/zoo-2.cfg # Mode: leader
bin/zkServer.sh status conf/zoo-3.cfg # Mode: follower
# 向 Leader 写入数据
zkCli.sh -server 127.0.0.1:2182
[zkshell: 0] create /election-test/node1 "data"
Created /election-test/node1
[zkshell: 1] create /election-test/node2 "data"
Created /election-test/node2
# 停掉 Leader (Server 2)
bin/zkServer.sh stop conf/zoo-2.cfg
# 查看状态 —— Server 1 成为新 Leader
bin/zkServer.sh status conf/zoo-1.cfg # Mode: leader
# Server 1 的日志:
# Notification: myid=1, zxid=0x100000003, state=LOOKING
# Notification: myid=3, zxid=0x100000003, state=LOOKING
# 比较:zxid 相同,myid 1 < 3?
Server 1 和 Server 3 的 zxid 在大多数情况下相同(因为 Leader 宕机前已同步),此时 myid 较大的 Server 3 获胜。但时序因素可能使结果不绝对。
操作前后对比:
| 时间点 | Leader | Follower | 说明 |
|---|---|---|---|
| 宕机前 | Server 2 | Server 1, 3 | 正常运行 |
| 宕机后 | Server 1 或 3 | 另一个 | 仅需 ~200ms 选举 |
示例三:Java API 监听 Leader 选举事件
场景说明:通过 Watcher 感知集群 Leader 变化。
public class LeaderElectionWatcher implements Watcher {
@Override
public void process(WatchedEvent event) {
if (event.getState() ==
Event.KeeperState.Disconnected) {
System.out.println("Disconnected from Server");
} else if (event.getState() ==
Event.KeeperState.SyncConnected) {
System.out.println("Reconnected to Server");
// 此时 Leader 可能已变更
}
}
}
// 客户端自动重连到新 Leader
ZooKeeper zk = new ZooKeeper(
"127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183",
15000,
new LeaderElectionWatcher()
);
执行输出(Leader 宕机期间):
Disconnected from Server
... <~200ms 选举 + 同步 ...
Reconnected to Server
操作前后对比:
| 事件 | 客户端状态 | 说明 |
|---|---|---|
| Leader 正常 | SyncConnected | 正常读写 |
| Leader 宕机 | Disconnected | 连接断开 |
| 新 Leader 就绪 | SyncConnected | 自动重连,Session 未过期 |
易错场景与面试考点
易错场景
1. 两节点集群的选举陷阱
2 节点集群:一个节点宕机后,剩下 1 个节点无法达到 Quorum(需要过半数,即 ≥2),集群停止服务。2 节点集群的可用性不如 3 节点。
2. 选举期间的写请求
选举期间客户端写请求会失败(ConnectionLossException 或 SessionExpiredException)。客户端应实现重试逻辑(指数退避),而不能假设写操作一定成功。
3. Observer 的误区
Observer 不参与投票,Leader 选举时 Observer 被忽略。如果集群包含 2 个 Follower + 3 个 Observer,宕机 1 个 Follower 仍然只剩 1 票,达不到 Quorum。
面试高频题
Q:FastLeaderElection 为什么叫 Fast?
A:相对于原始的 LeaderElection 算法(基于 Zab 协议的简化版),FLE 有两个加速:
- P2P 直接通信:节点直接向所有其他节点发送投票,无需选出一个协调者(Coordinator)
- 快速收敛:收到更高优先级的投票后立即转投,减少选举轮次
Q:选举过程中如何避免脑裂?
A:
- Quorum 机制:必须过半数节点选举同一 Leader
- epoch 机制:每次选举 epoch 递增,旧 epoch 的投票自动失效
- 过半确认:Leader 激活前需要过半数 Follower 确认(FLE 阶段结束后的同步阶段)
Q:如果两个节点同时认为自己应该是 Leader 怎么办?
A:不会发生。FLE 算法中每个节点根据相同的规则(先比 ZXID,再比 myid)做决策,最终所有节点会收敛到同一个最优节点。最坏情况是多轮投票,但结果唯一。
Q:ZXID 相同的情况下谁当选?
A:myid 大者当选。ZXID 相同说明所有节点拥有相同数据,此时 myid 作为仲裁(Tiebreaker),保证选举的确定性。
小结
| 要点 | 说明 |
|---|---|
| 投票规则 | ZXID 最大者优先,相同则 myid 大者优先 |
| 选举算法 | FastLeaderElection(P2P 直接通信) |
| 状态转换 | LOOKING → LEADING / FOLLOWING / OBSERVING |
| Quorum | 必须过半数节点(ceil(N/2) + 1) |
| Observer | 不参与选举,不计入 Quorum |
| 收敛时间 | 通常在 200ms 以内 |
Leader 选举保障了集群的自动故障转移。下一章进入 ZooKeeper 的最经典应用——分布式锁。