「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」
ZK服务端启动代码涉及很广,本文就集群下的zookeeper是怎么选举leader的进析。
当然我们要先知道什么是ZAB协议,所谓的ZAB协议也就是原子消息广播协议,也是zookeeper作为其数据一致性的核心算法,基于协议,Zookeeper实现了一种主备模式的系统架构来保持集群中各副本之间的数据一致性。具体的是Zookeeper集群中只会有一个进程(Leader)来处理客户端的所有事务请求,并将Leader的事务变更记录广播到所有的副本上去。
ZAB基本模式可分为消息广播和崩溃恢复,进一步又可以分为三个阶段
1.发现(Discovery):其实就是Leader选举的过程
2.同步(Synchronization):选举Leader结束之后,Leader要将自己的事务数据同步到其他副本
3.Broadcast(广播阶段):数据同步结束后Leader接收客户端的事务请求,并且将事务请求广播给所有的副本
一.服务器的状态
/**
*状态enum解读
*LOOKING:选举态,服务器这种状态时,会发起投票选举Leader
*FOLLOWING: 跟随者状态,也就是Leader的副本
*LEADING: 领导者状态,Leader,ZAB协议中只允许存在一个Leader
*OBSERVING:观察者状态
*/
public enum ServerState {
LOOKING, FOLLOWING, LEADING, OBSERVING;
}
二.选举方式
Zookeeper提供了三种选举方式,但是在3.4.0版本后只提供一种即FastLeaderElection,类图如下
主要内部类Notification的代码
public static class Notification {
//版本号
int version;
//建议的leader sid
long leader;
//被推举的leader的事务Id
long zxid;
//选举时钟
long electionEpoch;
//被推举leader的状态
QuorumPeer.ServerState state;
//发送次条数据的sid
long sid;
//被推举leader的时钟
long peerEpoch;
}
主要Messenger类,由类图可以看到Messenger主要包装了WorkerSender和WorkerReceiver,WorkerReceiver选票接收线程直接看run方法这个类做了什么
结合流程图梳理下大致过程
1.线程循环从QuorumCnxManager.recvQueue()获取mesage,判断message中的机器是否是观察者,是的话就发送当前服务器的投票
2.非观察者的话解析message封装为Notification,判断当前服务器是否是Looking状态
3.是Looking状态将Notification加入recvqueue中,并且判断投票的服务器是否是Looking并且选举的轮次小于当前服务器的轮次,发送当前机器的投票否则不作处理
4.是Looking状态将Notification加入recvqueue中,并且判断投票的服务器是否是Looking并且选举的轮次小于当前服务器的轮次,发送当前机器的投票否则不作处理
5.非Looking状态,判断发送方是否是Looking状态,是的话就发送自己的选票(此时选票一定是leader),否的话就不做处理
三.WorkerSender选票发送线程
class WorkerSender extends ZooKeeperThread {
volatile boolean stop;
QuorumCnxManager manager;
WorkerSender(QuorumCnxManager manager){
super("WorkerSender");
this.stop = false;
this.manager = manager;
}
public void run() {
while (!stop) {
try {
//从队列取数据
ToSend m = sendqueue.poll(3000, TimeUnit.MILLISECONDS);
if(m == null) continue;
//处理数据
process(m);
} catch (InterruptedException e) {
break;
}
}
LOG.info("WorkerSender is down");
}
void process(ToSend m) {
ByteBuffer requestBuffer = buildMsg(m.state.ordinal(),
m.leader,
m.zxid,
m.electionEpoch,
m.peerEpoch);
manager.toSend(m.sid, requestBuffer);
}
}