zookeeper原理(二)集群选主和同步

一、基本概念
zookeeper的核心是原子广播,这个机制保证了各个server之间的同步,实现这个机制的协议叫做zab协议。zab协议有两种模式: 分别是恢复模式和广播模式。

  • 恢复模式,zookeeper配置为集群模式时,系统启动或者是当前leader崩溃或者是当前leader丢失大多数的follower,zk进入恢复模式,恢复模式需要重新选举出一个新的leader,当领导者被选举出来,且大多数Server的完成了和leader的状态同步以后,恢复模式就结束了。
  • 广播模式,状态同步保证了Leader和所有Server都具有相同的系统状态。这时候当Server加入Zookeeper集群后,会先在恢复模式下启动该Server,发现Leader后,并和Leader进行状态同步,待到同步结束,它也参与消息广播,即进入广播状态。Zookeeper服务一直维持在Broadcast状态,直到Leader崩溃了或者Leader失去了大部分的Followers支持,才会进入恢复模式,从新选举Leader。

二、选主流程
在Zookeeper-3.4.0之后推荐使用的选举算法只有:FastLeaderElection,以下是zookeeper选主过程:
通俗一点讲,这个选主过程其实就是一个选举的过程,选举出一个领袖来领导大家(其他节点),领导大家做什么呢?可以简单理解为增删改的操作其他节点都会发给leader节点来做,然后由leader节点同步给其他节点。
这个选举过程涉及到三个主要动作:

  • 发送信息给其他节点
  • 处理其他节点发送过来的信息
  • 接收其他节点发送的信息

zookeeper涉及的角色:

zookeeper原理(二)集群选主和同步_第1张图片

为了提高吞吐量通常我们只要增加服务器到Zookeeper集群中。但是当服务器增加到一定程度,会导致投票的压力增大从而使得吞吐量降低。因此我们引出了一个角色:Observer。随着 ZooKeeper 集群变大,投票操作的吞吐量会下降。所以需要增加客户节点数量的期望和我们希望保持较好吞吐性能的期望间进行权衡。要打破这一耦合关系,引入了不参与投票的服务器,称为 Observers。 Observers 可以接受客户端的连接,将写请求转发给领导节点。但是,领导节点不会要求 Observers 参加投票。相反,Observers 不参与投票过程,仅仅和其他服务节点一起得到投票结果。

Learner的消息类型:

  • PING消息是指Learner的心跳信息;
  • REQUEST消息是Follower发送的提议信息,包括写请求及同步请求;
  • ACK消息是Follower的对提议的回复,超过半数的Follower通过,则commit该提议;
  • REVALIDATE消息是用来延长SESSION有效时间;

Follower的工作流程:

  • 向Leader发送请求信息(ping消息,request消息,ack消息,revalidate消息)
  • 接收leader消息进行处理
  • 接收client请求信息,如果为写请求,发送给Leader进行投票
  • 返回client的结果

Leader的消息类型:

  • PING消息: 心跳消息;
  • PROPOSAL消息:Leader发起的提案,要求Follower投票;
  • COMMIT消息:服务器端最新一次提案的信息;
  • UPTODATE消息:表明同步完成;
  • REVALIDATE消息:根据Leader的REVALIDATE结果,关闭待revalidate的session还是允许其接受消息;
  • SYNC消息:返回SYNC结果到客户端,这个消息最初由客户端发起,用来强制得到最新的更新。

Leader的主要工作:

  • 恢复数据
  • 维持leader与learner的心跳,接收learner的请求并判定lerner的请求消息类型
  • learner的消息类型主要有ping消息,Request消息,ack消息,revalidate消息,根据不同的消息类型进行不同的处理。

每个节点都应该对应一个状态,所有可能的状态:

  • LOOKING,节点启动的时候都是该状态,正在选举
  • FOLLOWING,如果节点参与选举,但不是leader,则处于此状态
  • LEADING, leader的状态
  • OBSERVING,如果不参与选举,则为此状态

这里的信息包括以下几种类型:

  • 想要选举为leader的节点id
  • 当前节点的id
  • 当前节点的zxid,标识当前节点数据的版本,值越大说明版本越新
  • 当前节点本轮选举的标识epoch(逻辑时钟),标识每一轮选举,值越大标识本次选举离现在越近

选举时要保证半数以上的server的票数才能当选为leader,所以zookeeper集群中server数目一般为奇数个,例如:3,5,7个等,下面分两种场景来说明选举的过程:

  1. 集群第一次启动

    • 此时每个zookeeper服务器都没有数据,即每个服务的zxid都是相等的
    • 每个zk节点选择自己作为本轮要推举的leader节点,然后把上述的信息广播给所有节点(包括自己,理论上需要N*N各连接才能广播给集群中所有N个节点,这里zk做了一个优化,当要发送的节点是自己时,则不创建网络连接,直接修改自己的信息即可)。这个广播过程是由一个队列来完成的,在向每个节点发送消息之前先比较要发送的目标节点的id和自己id的大小,如果大于目标节点的id则发送消息,否则不发送消息。
    • 当节点接收到来自其他节点的消息时,比较发送消息的节点的id和当前节点的id的大小,如果大于消息节点的id则断开此连接,并尝试发送消息给这个id对应的消息节点。否则,查看消息id对应的节点的身份,如果是投票成员,查看其状态是否是LOOKING状态,如果是,则比较当前节点和消息节点的逻辑时钟,有以下几种情况:

      • 如果当前节点逻辑时钟> 消息节点逻辑时钟,说明消息节点还处在一个比较老的选举周期里,则只需将当前节点的信息发送给消息节点

      • 如果当前节点逻辑时钟<= 消息节点逻辑时钟,说明当前节点还处在一个比较老的选举周期里,这是一次更新的选举周期,则当前节点需要做以下操作:

          • 更新逻辑时钟,同时将之前手机来的其他服务器的选举信息清空
          • 比较当前节点和消息里的信息,节点id和zxid,首先对比数据id(zxid),比较大的作为当前节点推举的leader候选人;然后在比较节点id,比较大的作为当前节点的leader候选人,最后把信息广播给其他节点。
    • 最后每个节点都会判断是否收到了所有节点的选举消息,如果是,则根据选举结果设置自己的状态:

      • 如果选举的leader是自己,则把自己节点的状态设置为LEADING,否则设置成FOLLOWING。
      • 即使不是所有的节点都进行了投票,也可以判断当前是否有超过半数的节点投票给了某节点,如果超过半数的节点投票给了某节点,尝试在一段时间内等待接收新的数据,如果在此期间没有新的数据到来,说明大家已经默认了这个结果,则此节点为leader,并退出选举。

      假定server id A<\B<\C, C当选的可能性很大,也就是说C当选的概率不是百分之百的,这一点可以从上述的选举过程中看出来,整个选举如下图所示:
      zookeeper原理(二)集群选主和同步_第2张图片

    • 至于集群启动一段时间后的选举,相比集群第一次启动的不同在于每个节点的zxid和epoch有可能不同。当一个新启动的节点加入集群时,它对集群内其他节点发出投票请求,而其他节点已不处于LOOKING状态,此时其他节点回应选举结果,该节点收集这些结果到outofelection中,最终在收到合法LEADER消息且这些选票也构成选举结束条件时,该节点就结束自己的选举行为。

    三、同步流程

    • 当通过选主流程选出leader之后,一定是集群中数据最完整最新的节点。因为所有znode的变更都需要通过leader,leader会为所有follower和observer创建learnerhandler线程用于接收同步数据请求,当follower和abserver收到leader发过来的信息,比较zxid的大小,如果zxid小于leader的zxid,则把节点最大的zxid发送给leader,leader会将大于该zxid的所有数据同步到该follower,完成同步后,通知改follower进入uptodate状态,follower接收到uptodate消息后,就可以接收client的请求了。
    • 写操作的流程如下:

      zookeeper原理(二)集群选主和同步_第3张图片

    • 读流程比较简单,无论客户端连接的节点是不是leader,只要节点能够接受客户端的读请求,节点服务器可以直接返回数据给客户端而无需经过leader。

你可能感兴趣的:(web)