emmm建议大家边看源码边看此文章啊哈,有点高度总结,没有上源码
1到7为一些属性的说明,8的lookForLeader就是核心算法
源码内部的变量有些没有按照驼峰命名,好吧我看着有点难受,不过也无所谓了
FastLeaderElection.java
- 1
finalizeWait = 200,决定一个选举过程需要等待的选举时间,一经到达,它将结束leader选举;实际是等待其他server发送通知的超时时间;可以延长,依次*2,但不会超过maxNotificationInterval,提高选举效率
- 2
maxNotificationInterval = 60000;finalizeWait的最大值,超过的话会开始新一轮选举
- 3
四种状态:ServerState枚举
- 4
LearnerType枚举有PARTICIPANT、OBSERVER
- 5
static public class Notification:通知,包含发送者的信息(状态等)和其推荐的人的信息
- 6
组合了QuorumPeer:管理“法定人数投票”协议,状态为leader election(对应looking)、leader、follower
- 7
组合了QuorumCnxManager:TCP实现的用于Leader选举的连接管理器;使 用基于双方IP地址的中断连接机制来确保每对服务器正确地操作并且可以与整个网络进行通信的连接有且只有一个
;内部使用一个map,key为其他server,value为发送消息失败的队列(图)
8
lookForLeader():开启新一轮的Leader选举,只要QuorumPeer的状态变为了LOOKING,此方法将被调用,发送notifications给所有其他的同级服务器;
每开启一轮,即调用此方法,都会调用logicalclock.incrementAndGet()
递增“年号”
8.1
创建选举对象,进行初始化:当前时间(Time.currentElapsedTime
)、其他server的本轮投票信息recvset(hashmap)
、退出选举的票outofelection(hashmap)
、收到通知的超时时间notTimeout
8.2
将自己作为新的Leader投出去:修改当前server的推荐信息,为自己;调用sendNotifications
,此方法并没有真正发送,而是将推荐信息构成的数据结构notmsg
添加到一个队列sendqueue
(LinkedBlockingQueue);只会发送给getVotingView()
的server,即不发给observer
8.3
验证当前自己的选票与大家的选票谁更适合做Leader:依次从recvqueue
中取出通知进行判断,其本质是LinkedBlockingQueue
选出来后更新proposedLeader、proposedZxid、proposedEpoch
,然后调用sendNotifications()
;leader挂了之后,有可能部分server知道了部分不知道,导致知道的部分server的logicalClock递增了,然后发给部分不知道的,此时部分不知道的依然没有收到leader的挂了的消息,当前逻辑时钟小于部分知道的server传过来的epoch,因此就有了n.electionEpoch > logicalclock.get()
这种情况(epoch在本地就叫做logicalClock,传出去了就叫epoch)
haveDelivered()
中可以看到,只要有一个队列为空,说明当前Server与集群没有失联(queueSendMap
,key为serveID,value为向对应的server发送失败的消息副本的队列);
如果有一个server失联了,即队列全不为空,那么会调用manager.connectAll()
连接所有其他server,但是并不需要重发,因为当前server若与集群失联,则其他server一定不可能收到此server发送的通知,所以其他server取不到此server的通知,即会判断n == null
为true,执行if
代码,重新发送通知,当前server就会收到其他server所发送的通知;
当然此前提是leader选举没结束,如果收到的票中能选出来leader,那么也就结束了,其他server就不会重发了,当前server不用重连,直接认主即可:(设计技巧)
if(n == null){
//n为server收到的通知
if(manager.