何时需要进行leader选举
- zookeeper服务启动时候
- zookeeper服务器服务期间,无法与leader进行通信。
选举过程中,要求,被选举对象的支持票数必须在一半以上,方能生效,少数服从多数。
选票5个基本概念,
- serverid,sid,机器的server id,在配置文件中
- zxid,ZooKeeper Transaction Id,64位长度,由两部分构成 ,高32位是leader epoch,低32位是计数器
- leader epoch,32位表示,融合在zxid中,每次选举leader时候,会加1,表明一个新的时代 要产生。
- 计数器,32位表示,融合在zxid中,每次对数据的更改,一次请求,加1,leader负责累加操作,影响后续的事物操作结果。
- logicalclock,选举时钟周期,每个机器要进行选举了,就将自己的logicalclock加1
这里的zxid虽然包含了epoch,那是leader确定后,create或者update的数据节点加上时间戳,这个时间戳包含了当前生效的epoch,也即当前生效的logicalclock,直接忽略zxid中的epoch,就理解zxid是事物id,严格递增,每个服务器节点,都能保存最新的自己的zxid。
投票券构成(sid, zxid),之外,还包括当前服务器状态,logicalclock,等信息。
服务器启动过程中的leader选举和运行期间的leader选举原理一致,只是启动过程中,服务器启动有快有慢而已,选举过程简单,当足够的选票到达后能够确定一个leader后,后续启动的机器再沟通后,直接变成follower。
选举过程中机器的状态
- Looking,寻找leader状态
- following,跟随状态,即,follower
- leading,领导者状态,即,leader
- observing,观察者状态,即,observer
选举通道
每台服务 器在启动的过程中,会启动一个QuorumPeerManager,负责各台服务器之间的底层Leader选举过程中的网络通信
消息队列
QuorumPeerManager维护一系列队列和相应的处理线程
- 一个接收队列,接收其他服务器发来的消息,recvQueue,
- 若干个接收器,RecvWorker,集群中其他服务器,每个对应一个RecvWorker,
- 若干个发送队列,按照其他机器sid个数分组形成队列集合,queueSendMap
- 若干个发送器,按照其他机器sid个数分组形成发送器集合,senderWorkerMap
- 上一次发出去的投票信息,lastMessageSent,为每个sid都保留一个上次的发送的投票
建立连接
为了能够投票,zookeeper集群中每两台机器之间都要建立通信,QuorumPeerManager会创建ServerSocket来监听端口(3888),接受Leader选举过程中的通信信息。
为避免两台机器之间重复建立TCP连接,Zookeeper只允许SID大的服务器主动和其他机器建立连接,否则断开连接。在接收到创建连接请求后,服务器通过对比自己和远程服务器的SID值来判断是否接收连接请求,如果当前服务器发现自己的SID更大,那么会断开当前连接,然后自己主动和远程服务器建立连接。一旦连接建立,就会根据远程服务器的SID来创建相应的消息发送器SendWorker和消息接收器RecvWorker。
收发消息
在SendWorker中,一旦Zookeeper发现针对当前服务器的消息发送队列为空,那么此时需要从lastMessageSent中取出一个最近发送过的消息来进行再次发送,这是为了解决接收方在消息接收前或者接收到消息后服务器挂了,导致消息尚未被正确处理。
同时,Zookeeper能够保证接收方在处理消息时,会对重复消息进行正确的处理
接受消息
由消息接收器RecvWorker负责,由于Zookeeper为每个远程服务器都分配一个单独的RecvWorker,因此,每个RecvWorker只需要不断地从这个TCP连接中读取消息,并将其保存到recvQueue队列中
发送消息
由于Zookeeper为每个远程服务器都分配一个单独的SendWorker,因此,每个SendWorker只需要不断地从对应的消息发送队列中获取出一个消息发送即可,同时将这个消息放入sid对应的lastMessageSent中
FastLeaderElection选举算法
在3.4.0后的Zookeeper的版本只保留了TCP版本的FastLeaderElection选举算法。
流程如下,
- 自增选举轮次。Zookeeper规定所有有效的投票都必须在同一轮次中,在开始新一轮投票时,会首先对logicalclock进行自增操作。
- 初始化选票。在开始进行新一轮投票之前,每个服务器都会初始化自身的选票,并且在初始化阶段,每台服务器都会将自己推举为Leader。
- 发送初始化选票。完成选票的初始化后,服务器就会发起第一次投票。Zookeeper会将刚刚初始化好的选票放入sendqueue中,由发送器WorkerSender负责发送出去。
- 接收外部投票。每台服务器会不断地从recvqueue队列中获取外部选票。如果服务器发现无法获取到任何外部投票,那么就会立即确认自己是否和集群中其他服务器保持着有效的连接,如果没有连接,则马上建立连接,如果已经建立了连接,则再次发送自己当前的内部投票。
- 判断选举轮次。在发送完初始化选票之后,接着开始处理外部投票。在处理外部投票时,会根据选举轮次来进行不同的处理。
- 外部投票的选举轮次大于内部投票。若服务器自身的选举轮次落后于该外部投票对应服务器的选举轮次,那么就会立即更新自己的选举轮次(logicalclock),并且清空所有已经收到的投票,然后使用初始化的投票来进行PK以确定是否变更内部投票。最终再将内部投票发送出去。
- 外部投票的选举轮次小于内部投票。若服务器接收的外选票的选举轮次落后于自身的选举轮次,那么Zookeeper就会直接忽略该外部投票,不做任何处理,并返回步骤4。
- 外部投票的选举轮次等于内部投票。此时可以开始进行选票PK。
- 选票PK。在进行选票PK时,符合任意一个条件就需要变更投票。
- 若选举轮次一致,那么就对比两者的ZXID,若外部投票的ZXID大,那么需要变更投票。
- 若两者的ZXID一致,那么就对比两者的SID,若外部投票的SID大,那么就需要变更投票。
- 变更投票。经过PK后,若确定了外部投票优于内部投票,那么就变更投票,即使用外部投票的选票信息来覆盖内部投票,变更完成后,再次将这个变更后的内部投票发送出去。
- 选票归档。无论是否变更了投票,都会将刚刚收到的那份外部投票放入选票集合recvset中进行归档。recvset用于记录当前服务器在本轮次的Leader选举中收到的所有外部投票(按照服务队的SID区别,如{(1, vote1), (2, vote2)...})。
- 统计投票。完成选票归档后,就可以开始统计投票,统计投票是为了统计集群中是否已经有过半的服务器认可了当前的内部投票,如果确定已经有过半服务器认可了该投票,则终止投票。否则返回步骤4。
- 更新服务器状态。若已经确定可以终止投票,那么就开始更新服务器状态,服务器首选判断当前被过半服务器认可的投票所对应的Leader服务器是否是自己,若是自己,则将自己的服务器状态更新为LEADING,若不是,则根据具体情况来确定自己是FOLLOWING或是OBSERVING。
以上10个步骤就是FastLeaderElection的核心,其中步骤4-9会经过几轮循环,直到有Leader选举产生
网友评论