分布式专题|面试官常问的zookeeper选举、消息广播、崩溃恢复原理,你都知道了么?

分布式专题|面试官常问的zookeeper选举、消息广播、崩溃恢复原理,你都知道了么?

zookeeper选举过程

我们先介绍几个主要的参数:

  • zxid:当前节点最新的事务ID,ID值越大,则说明数据越新
  • serverId:对应每个节点的myid,myid越大在选举过程中的权重就越大。
  • epoch:代表选举的次数,ZXID高位的编号和其值相等
  • 选举状态:
    • LOOKING,竞选状态。

    • FOLLOWING,随从状态,同步leader状态,参与投票。

    • OBSERVING,观察状态,同步leader状态,不参与投票。

    • LEADING,领导者状态。

有了上面的基本概念之后,我们现在来开始说选举的过程吧:
选举的过程发生在以下两个场景下:

  • 节点初始化启动的时候
  • 服务器运行时期,leader崩溃后的重新选举

节点初始化时的选举过程(以三台机器为例)

  1. 每个节点先给自己投票,加入现在有两台机器上线,分别为A(myid:1,zxid:0),B(myid:2,zxid:0),那么这两个节点在投票给自己之后,会将投票结果发到集群,如:A服务器发出的投票则为(myid:1,zxid:0),B为(myid:2,zxid:0);

  2. 每个节点接收到来自集群中其他节点的投票,首先判断该投票的有效性(判断epoch参数是否低于当前节点的epoch)

  3. 处理投票:

    • 先检查ZXID,ZXID较大的服务器优先作为leader
    • 如果zxid相同,则检查myid,myid较大的作为leader
  4. 统计投票
    每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于A、B而言,都统计出集群中已经有两台机器接受了(myid:2,zxid:0)的投票信息,此时便认为已经选出了Leader;

  5. 改变选举状态
    在选举开始前,会将每个节点的状态修改为Looking状态,选举完毕后,如果当选为leader,则节点状态为LEADING,follow节点为FOLLOWING状态,OBSERVE为OBSERVING状态,当服务器为LOOKING状态时,整个集群停止对外提供服务。

服务器运行期间,Leader崩溃,重新选举过程

  1. 变更followering状态为 LOOKING状态,OBSERVING不参与投票,不需要变更;
  2. 每个follower节点发起投票,这一步也都是投自己;
  3. 每个follow接受集群中的其他节点的投票,然后处理投票,处理投票的规则和初始状态过程处理规则一样,先比较zxid,如果zxid一致,则比较myid,大者胜出,成为每个节点最终投票的节点;
  4. 统计投票,选出投票数最多的节点作为leader;
  5. 变更节点状态;

数据同步(消息广播)

  1. Leader收到消息后,或者是follow收到写消息,转发给Leader后,将会赋予消息一个全局64位自增zxid;
  2. leader位每个follow节点准备一个FIFO队列,并将消息作为一个提案并带上zxid发送给follow节点‘
  3. 当follow节点收到leader发过来的提案之后,会先把消息写到磁盘中,然后给leader回复一个ack消息,代表我已经收到了消息,并保存了下来;
  4. 当leader收到过半数的ack之后(这个和二段提交有点不同,二段提交要求收到所有的ack),就会像所有的follow发送commit消息,并本地执行该消息,并提交
  5. follow节点收到commit消息后,会比较commit携带的zxid是否是历史队列中最小的,如果是则执行,否则一致等待,从而保证了顺序执行。

崩溃恢复

崩溃恢复需要处理的两个主要问题,这在网上也有很多文章说过,但是说的都不是很全面,我在这里用自己的理解和大家说一遍,如果说的不对的欢迎指正:

已经被提交的提案不能被丢弃

假设有这种场景,如果在leader发出了commit之后,各个follow收到commit之前,leader挂掉了,导致follow并没有执行已经提交的提案。这个时候,这个消息是不能丢失的;

  • 解决方案
    leader失效后,重新选举出来的leader肯定具备最大的zxid(不考虑这个zxid有没有被提交),只要zxid最大,那么就会被选为leader(myid也得考虑,这里不是重点),zxid最大说明这个节点肯定包括了所有的最新的提案,当这个节点当选为leader之后,新的leader会检查自身有没有未被提交的提案,如果有的,则会向集群中发送请求,询问其他follow节点是否存在其提案,如果超过半数回复ok,则执行提交操作,之后进行数据同步操作,这样就保证了已经被提交的提案不会被丢失。
没有被提交的提案应该被丢弃

假设有这种场景,如果在leader生成提案后,广播之前,leader崩溃了,这个时候的提案是应该被丢弃了,这个ZAB协议时如何解决的呢?

  • 解决方案:
    Zab 通过巧妙的设计 zxid 来实现这一目的

    zxid占据64位,高32位存储epoch编号,这个编号是每选举出一次leader之后都会加一,有种朝代的感觉哈,低32位从0开始,当有新的请求或出现新的提案时,就会加1,但是重新选择leader之后,就会进行清零;
    那么zab时如何借助zxid来解决没有被提交的提案应该丢弃的问题呢?

    在旧的leader重启后,因为已经经过一次新的选举了,旧的leader所处的朝代已经落后了,新的leader会要求旧的leader将 它所处的朝代没有被提交 的提案清除,重新同步最新的提案,这就保证了未被提交的提案进行丢弃;

微信搜一搜【乐哉开讲】关注帅气的我,回复【干货领取】,将会有大量面试资料和架构师必看书籍等你挑选,包括java基础、java并发、微服务、中间件等更多资料等你来取哦。

猜你喜欢

转载自blog.csdn.net/weixin_34311210/article/details/109566936