zookeeper FastLeaderElection 选举算法分析

本文深入解析了ZooKeeper中的ZAB协议,详细介绍了其工作原理、角色分工及选举算法FastLeaderElection。ZAB协议确保了客户端请求按序执行,通过ZXID标识事物并维护一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

zab协议

zk集群中有一个leader负责处理所有客户端的请求, 通过广播的的形式将事物传播到各个follower节点形成副本,每当follower接到事物后会以日志的形式持久化到本地,并向leader发送ack;
当leader接收到过半的follower回复ack后,leader会再次广播commit消息进行事物提交。
zab保证按照客户端事物请求的顺序执行事物提交

从client的角度看,client可以向集群中的任意一台机器发送请求,如果是写请求,就会转发到leader节点;任何节点都可以直接处理client的读请求;

在zab中,所有更新事物都会由一个64位的事物编号zxid表示, zxid用高32位和低32位代表不同的含义

high : 代表leader的epoch , 新产生一个leader后,会从该服务器上找到最大的事物Proposal的zxid, 高32位加1, 低32位清零;
low : 递增的计数器,leader接收到客户端的一个请求,在提交事物的时候会进行加1;

 

三个角色 leader , follower, observe

leader : 负责读,写,当有写请求时会先把消息广播给所有follower, 等待follower的ack ,如果收到一半以上的ack, leader就commit广播给所有follower

follower : 负责读, 有写请求会转发给leader , 收到leader的广播会做出ack回应

observe : 只负责读 , 引入observe的目的是 : 当集群规模大的时候,leader接收写请求后会广播给所有的其他节点, 会有很大的消耗,

observe 没有发言权, 只负责数据同步并提供读服务 , 既提供了集群的读,又不会给写带来性能消耗

 

选举算法 FastLeaderElection

1. 在看选举算法之前,先看几个变量和辅助方法

/***投票意向的几个变量**/
  long proposedLeader;  //本机投的zk id, zk配置文件中配置的每个实例一个id
  long proposedZxid;    //目标zk的 事物id或者叫数据id , 此id越大,证明数据越新,越有可能被选成leader
  long proposedEpoch;   //目标zk 事物id的高32位, 代表的leader的朝代,每换一个leader,朝代加1 
  long logicalclock;    //本机的投票计数器, 每投一次票,该数加1, 大的表示新一轮投票,小的表示老一轮投票不参考 ,
  						  至于为什么会出现不一致的现象是因为 网络传输可能会造成延迟等不确定因素,如果超时了,不能总等着对方
  HashMap<Long, Vote> recvset = new HashMap<Long, Vote>();  //本机统计的投票信息
  
  updateProposal(long leader, long zxid, long epoch);  //更新本地的投票意向,每进行一轮投票,本地的投票意向可能会改变

 sendNotifications(); //向集群中的每个zk发送自己的投票意向 , 把自己的投票意向和投票计数器封装成对象放进队列里, 单用一个线程异步发送 , tcp

 

2. 选举loop

 只有本机为looking状态即选举状态时进入选举loop,其他状态表示已经出结果了

while(self is looking && running){

  	Notification n = recvqueue.poll(notTimeout,TimeUnit.MILLISECONDS); //拿到一份别人的投票

  	如果对方是也是looking,执行下面操作
  	n is looking :

  		if(n.clock > self.clock) { // 自己的这轮选举已经落后了
  			1. recvset.clear();  //本地统计的投票过期了
  			2. 参考别人的信息,更新自己的投票意愿
  			   更新条件 :  如果别人的 朝代 或者 数据id 比自己大, 表示他的数据更新, 就把对方的投票信息设置自己的投票意愿 , 否则还不如自己的数据新呢, 干脆不更新了
  			3.更新完自己的意愿,本地留了一份,然后在通知出去  sendNotifications();
  		
  		} else if(n.clock < self.clock){  //对方的选举是过期的
  			//啥也不用干
  		}else {  //这种情况就是两者的选举属于同一轮,都是有效的, 接下来就要看谁的朝代更大或数据更新

  			if(对方数据更新){
  				//更新一下本地投票意向,把自己的投票意愿通知出去
  			}

  			recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch)); // 把对方的投票意愿缓存起来,用于最终的统计
  			
  			if(少数服从多数,可以终止投票){
  				1. 在做一次最终的投票校验,看是否还有变化,若没有进行下面操作,否则重头开始选

  				2.没有变化即 别人也不在发送自己的投票信息了接下来开始判断选出来的是谁了,然后更新自己的状态

  				3.如果选出来的是自己,把自己设为leading状态,
  				  否则设为following或observe (而follow和observe的区别是 一个参与投票一个不参与投票,引入observe的目的是在增大集群规模的情况下不影响 写操作时消息ack 和 commit带来的开销)

  				4.然后把leader的信息保存到自己本地,可以时刻知道leader是谁

  			}


  		}

  	如果所接收服务器不在选举状态,也就是在FOLLOWING或者LEADING状态
		做以下两个判断:
		a) 如果逻辑时钟相同,将该数据保存到recvset,如果所接收服务器宣称自己是leader,那么将判断是不是有半数以上的服务器选举它,如果是则设置选举状态退出选举过程
		b) 否则这是一条与当前逻辑时钟不符合的消息,那么说明在另一个选举过程中已经有了选举结果,于是将该选举结果加入到outofelection集合中,再根据outofelection来判断是否可以结束选举,如果可以也是保存逻辑时钟,设置选举状态,退出选举过程

  	}

 

下面是终止选举的判断方法

 

   /**
  	 * 第一个参数是本地缓存的其他人的投票信息,第二个是自己的投票信息,
     * 因为经过一轮交换意见,本地的投票信息已经是最新的朝代或数据id了
  	 * 
     * 只要判断缓存中的其他人的信息跟自己一致,则可统计出跟自己意见一致的人数,如果是少数服从多数则可终止
  	 *
  	 */
  	protected boolean termPredicate(HashMap<Long, Vote> votes, Vote vote) {  //每经历一轮投票都要判断是否能尽快终止投票,以便尽早提供服务

        HashSet<Long> set = new HashSet<Long>();

        /*
         * First make the views consistent. Sometimes peers will have
         * different zxids for a server depending on timing.
         */
        for (Map.Entry<Long,Vote> entry : votes.entrySet()) {
            if (vote.equals(entry.getValue())){
                set.add(entry.getKey());
            }
        }

        return self.getQuorumVerifier().containsQuorum(set);
    }

 

转载于:https://my.oschina.net/bihu/blog/1154115

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值