【ZooKeeper】ZooKeeper ZAB 协议深入详解
一、ZAB 协议核心概念
1.1 ZAB 协议概述
ZooKeeper Atomic Broadcast (ZAB) 协议是 ZooKeeper 的核心共识算法,专为 ZooKeeper 设计,用于在分布式系统中实现原子广播和崩溃恢复。ZAB 协议的核心目标是:
- 消息原子性:所有节点要么都接受消息,要么都不接受
- 全局有序性:所有节点以相同的顺序接收消息
- 崩溃恢复:Leader 故障后能快速恢复系统一致性
1.2 关键术语定义
// ZAB 协议核心状态定义
public enum ZabState {
ELECTION, // 选举阶段
DISCOVERY, // 发现阶段
SYNCHRONIZATION, // 同步阶段
BROADCAST // 广播阶段
}
// ZXID 数据结构
public class ZXID implements Comparable<ZXID> {
private final long epoch; // 选举周期
private final long counter; // 事务计数器
// ZXID 比较规则:先比较 epoch,再比较 counter
public int compareTo(ZXID other) {
if (this.epoch != other.epoch) {
return Long.compare(this.epoch, other.epoch);
}
return Long.compare(this.counter, other.counter);
}
}
二、ZAB 协议工作流程
2.1 协议阶段总览
2.2 选举阶段(Fast Leader Election)
public class FastLeaderElection {
// 选票数据结构
static class Vote {
long id; // 被选举服务器ID
long zxid; // 被选举服务器最新ZXID
long electionEpoch; // 选举周期
}
// 选举算法核心逻辑
public Vote lookForLeader() throws InterruptedException {
// 1. 自荐为Leader
Vote currentVote = new Vote(myId, lastZxid, logicalClock);
// 2. 广播选举通知
sendNotifications(currentVote);
// 3. 收集其他服务器投票
while (!haveQuorum) {
Notification n = recvQueue.poll(timeout, MILLISECONDS);
// 4. 验证投票有效性
if (n.electionEpoch > logicalClock) {
logicalClock = n.electionEpoch;
currentVote = new Vote(n.sid, n.zxid, n.electionEpoch);
sendNotifications(currentVote);
}
// 5. 更新投票统计
voteTracker.addVote(currentVote);
// 6. 检查是否获得多数票
if (voteTracker.hasQuorum()) {
return currentVote;
}
}
}
}
选举算法关键点:
- 比较优先级:先比较 ZXID,ZXID 大的优先;ZXID 相同则比较服务器 ID
- 投票更新:收到更高优先级的投票时更新自己的投票
- 快速收敛:通常 1-2 轮投票即可选出 Leader
2.3 发现阶段(Discovery)
发现阶段的目标是确保新 Leader 包含所有已提交的事务:
public class Leader {
private void lead() throws IOException {
// 1. 收集 Follower 的 lastZxid
Map<Long, Long> followerLastZxids = collectFollowerLastZxids();
// 2. 确定同步点
long newEpoch = currentEpoch + 1;
long syncPointZxid = determineSyncPoint(followerLastZxids);
// 3. 广播 NEWLEADER 消息
broadcastNewLeader(newEpoch, syncPointZxid);
// 4. 等待多数 Follower 确认
waitForAck(newEpoch);
}
}
2.4 同步阶段(Synchronization)
同步阶段确保所有节点数据一致:
public class Follower {
public void followLeader() throws IOException {
// 1. 接收 Leader 的同步点
NewLeader newLeader = recvNewLeader();
// 2. 检查本地数据状态
if (lastZxid > newLeader.syncPointZxid) {
// 需要截断多余事务
truncateLog(newLeader.syncPointZxid);
} else if (lastZxid < newLeader.syncPointZxid) {
// 需要同步缺失事务
syncMissingTransactions(newLeader.syncPointZxid);
}
// 3. 发送同步完成确认
sendSyncComplete();
}
}
2.5 广播阶段(Broadcast)
广播阶段处理客户端请求:
public class Leader {
private void broadcastProposal(Request request) {
// 1. 分配全局唯一 ZXID
long zxid = generateNextZXID();
request.setZxid(zxid);
// 2. 创建提案
Proposal p = new Proposal();
p.request = request;
p.zxid = zxid;
// 3. 广播提案给所有 Follower
for (LearnerHandler f : followers) {
f.queuePacket(p);
}
// 4. 等待 ACK
outstandingProposals.put(zxid, p);
}
private void processAck(long sid, long zxid) {
Proposal p = outstandingProposals.get(zxid);
if (p != null) {
p.ackSet.add(sid);
// 5. 收到多数 ACK 后提交
if (isQuorum(p.ackSet.size())) {
commitProposal(p);
outstandingProposals.remove(zxid);
}
}
}
}
三、ZAB 协议核心机制
3.1 事务处理流程
3.2 崩溃恢复机制
3.2.1 Leader 故障处理
public class Follower {
public void run() {
while (running) {
try {
// 处理 Leader 消息
processLeaderMessage();
} catch (LeaderDisconnectedException e) {
// 检测到 Leader 断开连接
reenterElection();
}
}
}
private void reenterElection() {
// 1. 重置连接状态
resetConnection();
// 2. 重新加入选举
election.lookForLeader();
}
}
3.2.2 数据一致性保证
ZAB 协议通过以下机制保证数据一致性:
- 事务 ID 排序:ZXID 全局有序
- 提案原子性:只有获得多数 ACK 的提案才会提交
- 历史事务同步:新 Leader 确保包含所有已提交事务
- 未提交事务丢弃:新 Leader 会丢弃未获得多数 ACK 的事务
3.3 消息顺序保证
ZAB 协议严格保证消息顺序:
- 全局有序:所有节点以相同顺序接收消息
- 因果有序:如果消息 A 在消息 B 之前被广播,则所有节点都会在 B 之前接收 A
- FIFO 顺序:来自同一客户端的消息按发送顺序处理
四、ZAB 协议源码分析
4.1 选举算法实现
// FastLeaderElection 选举逻辑
protected boolean totalOrderPredicate(long newId, long newZxid, long newEpoch,
long curId, long curZxid, long curEpoch) {
// 1. 比较选举周期
if (newEpoch > curEpoch) {
return true;
}
if (newEpoch < curEpoch) {
return false;
}
// 2. 比较 ZXID
if (newZxid > curZxid) {
return true;
}
if (newZxid < curZxid) {
return false;
}
// 3. 比较服务器 ID
return newId > curId;
}
4.2 提案处理流程
// Leader 提案处理
public void propose(Request request) throws IOException {
// 分配 ZXID
long zxid = lastProposedZxid + 1;
request.setZxid(zxid);
lastProposedZxid = zxid;
// 创建提案
Proposal p = new Proposal();
p.request = request;
p.zxid = zxid;
// 添加到待提交队列
outstandingProposals.put(zxid, p);
// 发送给 Followers
sendPacket(Leader.PROPOSAL, p);
}
// Follower 提案处理
public void processProposal(Proposal p) {
// 验证 ZXID 连续性
if (p.zxid != lastZxid + 1) {
throw new IllegalStateException("Unexpected ZXID sequence");
}
// 写入事务日志
log.append(p);
// 更新内存数据
zk.processTxn(p);
// 发送 ACK
sendAck(p.zxid);
}
4.3 提交处理流程
// Leader 提交处理
private void commitProposal(Proposal p) {
// 1. 更新提交点
lastCommittedZxid = p.zxid;
// 2. 发送 COMMIT 消息
sendPacket(Leader.COMMIT, p);
// 3. 应用事务到状态机
zk.commit(p);
}
// Follower 提交处理
public void processCommit(Proposal p) {
// 1. 验证提案存在
if (!pendingProposals.contains(p.zxid)) {
throw new IllegalStateException("Commit for unknown proposal");
}
// 2. 应用事务到状态机
zk.commit(p);
// 3. 从待处理队列移除
pendingProposals.remove(p.zxid);
}
五、ZAB 协议优化策略
5.1 性能优化技术
5.1.1 批量提交
public class Leader {
private final BatchProcessor batchProcessor = new BatchProcessor();
public void processRequest(Request request) {
// 添加到批处理队列
batchProcessor.addRequest(request);
}
private class BatchProcessor extends Thread {
public void run() {
while (running) {
// 收集一批请求
List<Request> batch = collectBatch();
// 创建批量提案
Proposal batchProposal = createBatchProposal(batch);
// 广播批量提案
broadcastProposal(batchProposal);
}
}
}
}
5.1.2 流水线处理
public class LearnerHandler extends Thread {
private final Pipeline pipeline = new Pipeline();
public void run() {
while (running) {
// 接收消息
QuorumPacket p = recvPacket();
// 添加到流水线
pipeline.process(p);
}
}
private class Pipeline {
private final Map<Long, Proposal> inProgress = new ConcurrentHashMap<>();
public void process(QuorumPacket p) {
switch (p.getType()) {
case Leader.PROPOSAL:
// 处理提案并发送 ACK
processProposal(p);
sendAck(p.getZxid());
break;
case Leader.COMMIT:
// 处理提交
processCommit(p);
break;
}
}
}
}
5.2 故障恢复优化
5.2.1 快速故障检测
public class Leader {
private final HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor();
private class HeartbeatMonitor extends Thread {
private final Map<Long, Long> lastHeartbeat = new ConcurrentHashMap<>();
public void run() {
while (running) {
// 检查心跳超时
checkTimeouts();
Thread.sleep(heartbeatInterval);
}
}
private void checkTimeouts() {
long now = System.currentTimeMillis();
for (Map.Entry<Long, Long> entry : lastHeartbeat.entrySet()) {
if (now - entry.getValue() > sessionTimeout) {
// 处理超时 Follower
handleFollowerTimeout(entry.getKey());
}
}
}
}
}
5.2.2 增量同步优化
public class Follower {
public void syncWithLeader(long syncPointZxid) throws IOException {
// 1. 获取本地最后 ZXID
long lastZxid = getLastZxid();
// 2. 确定同步范围
if (lastZxid == syncPointZxid) {
return; // 已同步
}
// 3. 请求差异事务
if (lastZxid < syncPointZxid) {
requestMissingTransactions(lastZxid, syncPointZxid);
}
// 4. 截断多余事务
else {
truncateLog(syncPointZxid);
}
}
}
六、ZAB 协议与其他协议对比
6.1 ZAB vs Paxos
| 特性 | ZAB | Paxos |
|---|---|---|
| 设计目标 | 原子广播 | 分布式共识 |
| 角色 | Leader/Follower | Proposer/Acceptor/Learner |
| 顺序保证 | 严格全局有序 | 无全局有序保证 |
| 性能优化 | 针对写操作优化 | 通用算法 |
| 实现复杂度 | 相对简单 | 理论复杂 |
| 崩溃恢复 | 内置恢复机制 | 需要额外实现 |
6.2 ZAB vs Raft
| 特性 | ZAB | Raft |
|---|---|---|
| 术语 | Leader/Follower | Leader/Follower/Candidate |
| 选举机制 | Fast Leader Election | Leader Election |
| 日志复制 | 两阶段提交 | 日志追加 |
| 成员变更 | 需要重启 | 动态配置 |
| 快照机制 | 定期快照 | 日志压缩 |
| 客户端交互 | 通过 Leader | 直接与 Leader 交互 |
七、ZAB 协议实践应用
7.1 在 ZooKeeper 中的应用
ZAB 协议在 ZooKeeper 中的关键应用:
- 领导者选举:实现分布式锁服务
- 配置管理:保证配置变更的原子广播
- 命名服务:确保节点创建顺序一致
- 分布式队列:保证消息顺序处理
7.2 性能调优参数
# zoo.cfg 关键 ZAB 参数
tickTime=2000
initLimit=10
syncLimit=5
electionAlg=3
minSessionTimeout=4000
maxSessionTimeout=40000
leaderServes=yes
syncEnabled=true
参数说明:
tickTime:基础时间单位(毫秒)initLimit:Follower 连接 Leader 的超时时间syncLimit:Follower 与 Leader 同步的超时时间electionAlg:选举算法(3 表示 FastLeaderElection)min/maxSessionTimeout:会话超时范围leaderServes:Leader 是否处理客户端请求syncEnabled:是否启用事务日志同步
八、ZAB 协议总结
8.1 核心优势
- 强一致性保证:严格的事务顺序和原子性
- 高吞吐量:优化的广播机制支持高并发
- 快速故障恢复:秒级 Leader 切换
- 简单高效:相比通用共识算法更易实现
8.2 适用场景
- 协调服务:分布式锁、配置管理
- 命名服务:统一资源定位
- 状态同步:集群状态一致性维护
- Leader 选举:分布式系统主节点选举
8.3 局限性
- 写性能瓶颈:所有写操作必须通过 Leader
- 脑裂问题:网络分区时可能出现双 Leader
- 配置变更复杂:集群扩容缩容需要重启
- 内存限制:全量数据存储在内存中
ZAB 协议作为 ZooKeeper 的核心引擎,通过其独特的设计在分布式协调领域展现出卓越的性能和可靠性。理解 ZAB 协议的工作原理对于构建和优化分布式系统至关重要。
本文来自博客园,作者:NeoLshu,转载请注明原文链接:https://www.cnblogs.com/neolshu/p/19513675

浙公网安备 33010602011771号