ZooKeeper内部原理

一、ZooKeeper半数机制

上文中我们说ZooKeeper集群一般采用奇数台服务器,这是为什么呢,答案就是文章开头那一句,ZooKeeper集群在一半以上服务器数量可用时集群可用,举个栗子,当服务器数量是三台时,最多允许一台服务器挂掉,当服务器数量是四台时,也最多允许一台服务器挂掉,也就是说,三台服务器和四台服务器的容灾情况是一样的,为了节省成本,我们一般使用奇数。

二、ZooKeeper的选举机制

我们知道ZooKeeper中只包含一个Leader节点,但是这个Leader节点是怎么产生的呢,答案是大家一起选举出来的,ZooKeeper的选举机制是ZooKeeper中一个比较重要的内容。在讲ZooKeeper选举机制之前,要先讲清楚几个概念。
myid:被推举的Leader的编号。
zxid:被推举的Leader事务ID,全局有序,也就是说zxid越大的服务器上信息越新。
electionEpoch:逻辑时钟,用来判断多个投票是否在同一轮选举周期中,该值在服务端是一个自增序列,每次进入新一轮的投票后,都会对该值进行加1操作。
peerEpoch:被推举的Leader的epoch。
state:当前服务器的状态。有如下值:
LOOKING:寻找Leader状态。当服务器处于该状态时,它会认为当前集群中没有Leader,因此需要进入Leader选举状态。
FOLLOWING:跟随者状态。表明当前服务器角色是Follower。
LEADING:领导者状态。表明当前服务器角色是Leader。
OBSERVING:观察者状态。表明当前服务器角色是Observer。

ZooKeeper的选举机制就是选举出集群的Leader,但是什么时候需要在集群中选取一个Leader呢,应该是有两种情况
1、集群刚刚启动,还没有Leader
2、集群Leader挂掉了,需要重新选举Leader
这两种情况十分类似,但是还是有一点区别,这里分开描述。
(1)、集群启动时选举
下面以三台服务器为例,myid分别为:1、2、3

  1. Server1启动,并选举自己为Leader,但是因为集群中其他机器未启动,所以在等待状态
  2. Server2启动,并选举自己为Leader,此时集群中已有两台机器,可以互相通信,于是将自己的投票信息以(myid,zxid)的形式发送给其他机器。
  3. 接收投票。每台服务接收到一个投票信息后,会确定这个投票信息的有效性,如判断这次投票是不是来自本轮,投票信息是不是来自LOOKING状态的服务器。
  4. 处理投票,也就是将自己的投票信息与接收到的投票信息进行PK,PK的规则如下:
    首先检查zxid,zxid大的胜出
    zxid相等的情况下检查myid,myid大的胜出
    对于本文中的例子,Server1发出的投票信息是(1,0),Server2发出的投票信息是(2,0),在Server1中的PK,zxid相等,但是Server2的myid较大,所以在Server1中,将投票信息改为(2,0),Server2中不作改变。本轮投票结束。
  5. 统计投票,每次投票结束后,服务器都会统计本轮投票的票数,如果有一台机器的得票数大于半数,则选举为Leader,否则进行下一轮投票,一直到有一台机器得票数大于半数。本文中的例子中,经过第一轮投票,Server2已经有了两票,大于半数,选举为Leader
  6. 状态更改,当集群中有一台机器被选举为Leader之后,选举结束,同时每台服务器更改自己的状态,Follower的机器改为FOLLOWING,Leader的机器改为LEADING。

(2)、集群重新选举

  1. 当服务器的Leader挂掉之后,所有的非OBSERVING状态的服务器将自己的状态改为LOOKING。
  2. 当状态变更完毕之后开始投票选举,值得一提的是,因为在运行过程中在运行期间每台服务器的zxid可能不同。
    其余过程和上面完全一样。

三、监听器原理

在这里插入图片描述

1、首先创建Main线程
2、然后创建一个ZooKeeper客户端,此时会创建两个线程,一个connect负责通信,一个Listener负责监听
3、connect线程将监听事件发送给注册的监听器,注册的监听器将注册的监听事件写入列表
4、当节点发生变化之后通过Listener线程通知Client
5、Client线程通过调用Process方法执行后续操作

四、ZAB协议

ZAB协议的全称是ZooKeeper Atomic Broadcast(ZooKeeper原子广播)。ZooKeeper是通过ZAB协议来保证分布式事务的最终一致性。
当我们创建一个ZooKeeper客户端的时候会随机连接到一个可用的ZooKeeper节点,如果是读请求,就直接从当前节点读取数据,如果是写请求,那么节点就会向Leader节点提交事务,Leader节点收到事务提交会广播该事务,当集群中半数以上的节点写入成功,该事务就会被提交。
由上我们可用发现,ZAB协议需要满足两个条件:
1、ZAB协议需要确保那些已经在Leader提交的事务最终被所有服务器提交。
2、ZAB协议需要确保丢弃那些只在Leader上被提出而没有被提交的事务。

ZAB协议原理

ZAB协议要求每个Leader都要经历三个阶段:

  • 发现:要求ZooKeeper集群必须选举出一个Leader进程,同时Leader会维护一个Follower可用客户端列表。将来客户端可以和这些Follower节点进行通信。
  • 同步:Leader要负责将本身的数据与Follower完成同步,做到多副本存储。这样也是体现了CAP中的高可用和分区容错。Follower将队列中未处理的请求消费完成后,写入本地事务日志中。
  • 广播:leader可以接受客户端新的事务Proposal请求,将新的Proposal请求广播给所有的Follower。
ZAB协议内容

ZAB协议包括两种模式:崩溃恢复消息广播
奔溃恢复就是选举新的Leader的过程,具体流程上面已经介绍了,当集群选举了新的Leader。同时集群中有半数的机器与该Leader服务器完成了状态同步之后,ZAB协议就会推出恢复模式,进入消息广播模式。在消息广播模式中,当Leader崩溃或者集群重启之后,又会重新进入崩溃恢复模式。

消息广播

上面已经详细介绍了Leader选举的过程,下面讲一下消息广播模式。
在整个消息广播中,Leader会将每一个事务请求转换成对应的proposal来进行广播,并且在广播事务Proposal之前,Leader服务器会首先为这个事务Proposal分配一个全局单递增的唯一ID,也就是事务ID(zxid),由于ZAB协议需要保证每一个消息的严格顺序关系,因此必须将每一个Proposal按照其zxid的先后顺序进行排序和处理。
ZAB协议中Leader等待Follower的ACK反馈消息是指“半数以上的Follower成功反馈即可,不需要收到全部Follower反馈”。具体流程如下:

  1. 客户端发起写请求
  2. Leader服务器将客户端的请求转化为Proposal,同时为其分配一个全局的ID,即zxid。
  3. Leader服务器为每个Folloer服务器分配一个单独的队列,然后将需要广播的Proposal依次放到队列中取,并且根据FIFO策略进行发送。
  4. Follower接收到Proposal后,会首先将其以事务日志的方式写入本地磁盘,写入成功后向Leader反馈一个ACK响应消息。
  5. Leader接受半数以上的Follower的ACK响应消息后,即可认为消息发送成功,可以发送commit消息。
  6. Leader向所有Follower广播commit消息,同时自身也会完成事务提交。Follower接收到commit消息后,会将上一条事务提交。
    这里有人可能会有一个问题,既然Leader在半数Follower发送响应消息的时候就发送commit消息,那另外的服务器不会造成数据不一致吗。
    这里我们需要想到这个,这里讲的是集群模式,也就是说整个集群对外提供服务,我们知道当集群中有半数以上的服务器正常的时候,ZooKeeper就可以对外提供服务,所有,这里当半数以上的Follower完成之后,就可以对外提供服务,这时,客户端会连接到已经同步完毕的Follower,其余的会继续同步。同步不是目的,对外提供服务才是目的,这是所有分布式框架的共同特点,当已经可以对外提供服务时,我们可以没必要等待全部节点同步完成。
ZAB数据同步

1、当完成Leader选举后(新的Leader具有最高的zxid,也就是最新的数据),在正式开始工作之前,Leader服务器会首先确认事务日志中所有的Proposal是否已经被集群中过半的服务器Commit。
2、Leader服务器需要确保所有的Follower服务器能够接收每一条事务的Proposal,并且能将所有已经提交的事务Proposal应用到内存数据中。等到Follower将所有尚未同步的事务Proposal都从Leader服务器上同步过来并且应用到内存数据中一后,Leader才会把该Follower加入到真正可用的Follower列表中。

数据同步过程中,处理需要丢弃的Proposal

在ZAB的事务编号zxid设计中,zxid是一个64位的数字。其中低32位可用看成一个简单的单增计数器,针对客户端的每一个事务请求,Leader才产生新的Proposal的同时,都会对该计数器加1,而高32位则代表了Leader周期的epoch编号。

epoch编号可用看成集群所处的年代或者周期。每次Leader变更之后都会在epoch的基础上加1,这样旧的Leader崩溃恢复之后,其他的Follower就不会听他的了,因为Follower只服从epoch最高的Leader的命令。

每当选举一个新的Leader,就会从这个Leader的服务器上取出本地事务日志最大编号Proposal的zxid,并从zxid中解析得到对应的epoch编号,然后对其加1,之后该编号作为新的epoch,并将低32位归零,由0开始重新生成zxid。
ZAB协议通过epoch编号来区分Leader变化周期,避免了不同的Leader错误的使用了相同的zxid编号提出了不一样的Proposal的情况。
当一个包含了上一个Leader周期中尚未提交过的事务Proposal的服务器启动时,这台服务器加入集群,Leader会根据自己服务器上最后提交的Proposal和它进行比对,结果肯定是不一致,Leader会要求它进行回退,回退到一个确实已经被集群中过半的机器Commit的最新的Proposal。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值