在选举过程中,主要涉及到以下这些类。
先分析下这些类的关系,其中 FastLeaderElection 持有 QuorumCnxManager 通过该类管理服务器之间端到端的连接;
FastLeaderElection 还持有 Messenger ,通过该类中的 WorkerSender、WorkerReceiver 来收发选举过程中产生的消息。
连接建立
通过 Listener 绑定选举端口,之后调用 FastLeaderElection.lookForLeader 进行选举。
其中,服务器之间的连接只由 myid 大的主动发起连接到 myid 小的,这样减少了重复连接。如果 myid 小的服务器建立连接到 myid 大的服务器,那么将只发送自身的地址之后就断开连接。myid 大的服务器收到该地址消息后,主动发起请求,建立连接到 myid 小的服务器的通信。
发送选票
在 FastLeaderElection.lookForLeader 方法中,通过 sendNotifications 方法将选票放入队列 sendqueue 中;
该队列中的消息通过 FastLeaderElection 中的 Messager WorkerSender 进行处理,将该消息交付给 QuorumCnxManager 维护的对端的发送队列 。对端发送队列的映射关系如下:
// <sid,queue>
final ConcurrentHashMap<Long, BlockingQueue<ByteBuffer>> queueSendMap;
如果消息包交付之后,对端的连接还未建立则进行连接的建立。其中对端连接的接收、发送处理线程为 SendWorker、RecvWorker;通过以下结构维护:
// <sid,SendWorker> 其中 SendWorker 通过一个字段关联了 RecvWorker
final ConcurrentHashMap<Long, SendWorker> senderWorkerMap;
之后消息,就由 SendWorker 发送给对端了。
接收选票
服务器之间的选举消息由 RecvWorker 接收,将接收到的数据封装为 org.apache.zookeeper.server.quorum.QuorumCnxManager.Message 之后,投递到 QuorumCnxManager 的队列 recvQueue 中;
WorkerReceiver 从 QuorumCnxManager 的队列 recvQueue 中获取收到的消息,而 recvQueue 中的消息由对端连接的接收线程 RecvWorker 放入。消息被封装为 org.apache.zookeeper.server.quorum.QuorumCnxManager.Message。
WorkerReceiver 将收到的 Message 消息,处理封装为 org.apache.zookeeper.server.quorum.FastLeaderElection.Notification,并放入 FastLeaderElection 的队列 recvqueue 中;
在 FastLeaderElection.lookForLeader 方法中,如果节点的状态为 LOOKING 则证明选举未完毕。将不断轮训队列 recvqueue 获取 Notification 消息。将收到的消息进行比对,计算该选票的结果。选票处理规则如下:
protected boolean totalOrderPredicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch) {
LOG.debug(
"id: {}, proposed id: {}, zxid: 0x{}, proposed zxid: 0x{}",
newId,
curId,
Long.toHexString(newZxid),
Long.toHexString(curZxid));
if (self.getQuorumVerifier().getWeight(newId) == 0) {
return false;
}
// 选举周期较大胜出
// zxid 较大胜出
// myId 较大胜出
/*
* We return true if one of the following three cases hold:
* 1- New epoch is higher
* 2- New epoch is the same as current epoch, but new zxid is higher
* 3- New epoch is the same as current epoch, new zxid is the same
* as current zxid, but server id is higher.
*/
return ((newEpoch > curEpoch)
|| ((newEpoch == curEpoch)
&& ((newZxid > curZxid)
|| ((newZxid == curZxid)
&& (newId > curId)))));
}