基于版本3.4.13
QuorumCnxManager主要负责和其他节点数据传输 sendqueue:选票发送队列,用于保存待发送的选票。 recvqueue:选票接收队列,用于保存接收到的外部投票。 WorkerReceiver:选票接收器。其会不断地从QuorumCnxManager中获取其他服务器发来的选举消息,并将其转换成一个选票,然后保存到recvqueue中,在选票接收过程中,如果发现该外部选票的选举轮次小于当前服务器的,那么忽略该外部投票,同时立即发送自己的内部投票。 WorkerSender:选票发送器,不断地从sendqueue中获取待发送的选票,并将其传递到底层QuorumCnxManager中。 recvQueue:当前节点接受信息的列队 senderWorkerMap:每台节点对应的senderworker,<Long, SendWorker>,Long为服务器myid,SendWorker用于从queueSendMap存的Queue取数据发送给其他节点的,是个线程
注意图里的recvqueue(FastLeaderElection类中)队列和recvQueue(QuorumCnxManager类中)是不一样的。
QuorumPeerMain.main —> initializeAndRun方法—>runFromConfig方法里 quorumPeer.start() 启动QuorumPeer这个线程 QuorumPeer类:
@Override
public synchronized void start() {
// 加载数据
loadDataBase();
// 开启读取数据线程,取客户端数据
cnxnFactory.start();
// 进行领导者选举
startLeaderElection();
super.start();
}代码位于QuorumPeer的run方法 服务器有四种状态,分别是LOOKING、FOLLOWING、LEADING、OBSERVING。
每个投票中包含了两个最基本的信息,所推举服务器的SID和ZXID,投票(Vote)在Zookeeper中包含字段如下
在3.4.0后的Zookeeper的版本只保留了FastLeaderElection选举算法。
上图展示了FastLeaderElection模块是如何与底层网络I/O进行交互的。Leader选举的基本流程如下
public Vote lookForLeader() throws InterruptedException {
...
try {
//投票箱<Long, Vote> Long:其他节点sid Vote:其他节点投的票
HashMap<Long, Vote> recvset = new HashMap<Long, Vote>();
HashMap<Long, Vote> outofelection = new HashMap<Long, Vote>();
int notTimeout = finalizeWait;
synchronized(this){
// 时钟+1
logicalclock.incrementAndGet();
// 更新提议(投票),包含(myid,LastZxid, epoch), 投自己
updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch());
}
// 把投票发送出去,写到sendqueue
sendNotifications();
...
}以上10个步骤就是FastLeaderElection的核心,其中步骤4-9会经过几轮循环,直到有Leader选举产生。