zookeeper3.3.3源码分析(一)工做原理概述

阅读时参考的版本是3.3.3.html

简单的说一下zookeeper工做的过程,若是对这个过程还不太清楚,或者说对它如何使用等不太清楚的,能够参考一下其余的文章,好比这篇,这一系列的文章将不讲解它如何使用(实际上我也没有在具体项目中使用过,只是简单的配置运行起来大概晓得如何工做而已).算法

zookeeper有两种工做的模式,一种是单机方式,另外一种是集群方式.单机方式不属于这里分析的范畴,由于研究zookeeper的目的就在于研究一个zookeeper集群的机器如何协调起来工做的.服务器

要配置几台zookeeper一块儿工做,你们在开始必须使用相同的配置文件,配置文件中有一些配置项,可是与集群相关的是这一项:

server.1=192.168.211.1:2888:3888
server.2=192.168.211.2:2888:3888

这里定义了两台服务器的配置,格式为:

server.serverid=serverhost:leader_listent_port:quorum_port

顾 名思义,serverid是本服务器的id,leader_listen_port是该服务器一旦成为leader以后须要监听的端口,用于接收来自 follower的请求,quorum_port是集群中的每个服务器在最开始选举leader时监听的端口,用于服务器互相之间通讯选举 leader.函数

须要注意的是,server id并无写在这个配置文件中,而是在datadir中的myid文件中指定,我理解这么作的目的是:全部的服务器统一使用一个配置文件,该配置文件里面 没有任何与特定服务器相关的信息,这样便于发布服务的时候不会出错,而独立出来一个文件专门存放这个server id值.大数据

zookeeper集群工做的过程包括以下几步:
1) recovery,这个过程泛指集群服务器的启动和恢复,由于恢复也能够理解为另外一种层面上的”启动”–须要恢复历史数据的启动,后面会详细讲解.
2) broadcast,这是启动完毕以后,集群中的服务器开始接收客户端的链接一块儿工做的过程,若是客户端有修改数据的改动,那么必定会由leader广播给follower,因此称为”broadcast”.spa

展开来讲,zookeeper集群大概是这样工做的:
1) 首先每一个服务器读取配置文件和数据文件,根据serverid知道本机对应的配置(就是前面那些地址和端口),而且将历史数据加载进内存中.
2) 集群中的服务器开始根据前面给出的quorum port监听集群中其余服务器的请求,而且把本身选举的leader也通知其余服务器,来来每每几次,选举出集群的一个leader.
3) 选举完leader其实还不算是真正意义上的”leader”,由于到了这里leader还须要与集群中的其余服务器同步数据,若是这一步出错,将返回 2)中从新选举leader.在leader选举完毕以后,集群中的其余服务器称为”follower”,也就是都要遵从leader的指令.
4) 到了这里,集群中的全部服务器,不管是leader仍是follower,你们的数据都是一致的了,能够开始接收客户端的链接了.若是是读类型的请求,那 么直接返回就是了,由于并不改变数据;不然,都要向leader汇报,如何通知leader呢?就是经过前面讲到的 leader_listen_port.leader收到这个修改数据的请求以后,将会广播给集群中其余follower,当超过一半数量的 follower有了回复,那么就至关于这个修改操做哦了,这时leader能够告诉以前的那台服务器能够给客户端一个回应了.
能够看到,上面1),2),3)对应的recovery过程,而4)对应的broadcast过程.线程

这里只是简单的描述了一下zookeeper集群的工做原理,后面将分别展开来讨论.code

 



二、Fast Leader选举算法(领导者选举)server

 

link:http://www.codedump.info/?p=210htm

link:http://www.codedump.info/?p=224

 

如何在zookeeper集群中选举出一个leader,zookeeper使用了三种算法,具体使用哪一种算法,在配置文件中是能够配 置的,对应的配置项是”electionAlg”,其中1对应的是LeaderElection算法,2对应的是 AuthFastLeaderElection算法,3对应的是FastLeaderElection算法.默认使用 FastLeaderElection算法.其余两种算法我没有研究过,就很少说了.

要理解这个算法,最好须要一些paxos算法的理论基础.

1) 数据恢复阶段
首先,每一个在zookeeper服务器先读取当前保存在磁盘的数据,zookeeper中的每份数据,都有一个对应的id值,这个值是依次递增的,换言之,越新的数据,对应的ID值就越大.

2) 向其余节点发送投票值
在读取数据完毕以后,每一个zookeeper服务器发送本身选举的leader(首次选本身),这个协议中包含了如下几部分的数据:
    a)所选举leader的id(就是配置文件中写好的每一个服务器的id) ,在初始阶段,每台服务器的这个值都是本身服务器的id,也就是它们都选举本身为leader.
    b) 服务器最大数据的id,这个值大的服务器,说明存放了更新的数据.
    c)逻辑时钟的值,这个值从0开始递增,每次选举对应一个值,也就是说:  若是在同一次选举中,那么这个值应该是一致的 ;  逻辑时钟值越大,说明这一次选举leader的进程更新.
    d) 本机在当前选举过程当中的状态,有如下几种:LOOKING,FOLLOWING,OBSERVING,LEADING,顾名思义没必要解释了吧.

3)接受来自其余节点的数据

每台服务器将本身服务器的以上数据发送到集群中的其余服务器以后,一样的也须要接收来自其余服务器的数据,它将作如下的处理:
(1)若是所接收数据中服务器的状态仍是在选举阶段(LOOKING 状态),那么首先判断逻辑时钟值,又分为如下三种状况:
     a) 若是发送过来的逻辑时钟大于目前的逻辑时钟,那么说明这是更新的一次选举,此时须要更新一下本机的逻辑时钟值,同时将以前收集到的来自其余服务器的选举清 空,由于这些数据已经再也不有效了.而后判断是否须要更新当前本身的选举状况.在这里是根据选举leader id,保存的最大数据id来进行判断的,这两种数据之间对这个选举结果的影响的权重关系是:首先看数据id,数据id大者胜出;其次再判断leader id,leader id大者胜出.而后再将自身最新的选举结果(也就是上面提到的三种数据)广播给其余服务器).

    b) 发送过来数据的逻辑时钟小于本机的逻辑时钟,说明对方在一个相对较早的选举进程中,这里只须要将本机的数据发送过去就是了

    c) 两边的逻辑时钟相同,此时也只是调用totalOrderPredicate函数判断是否须要更新本机的数据,若是更新了再将本身最新的选举结果广播出去就是了.

而后再处理两种状况:
    1)服务器判断是否是已经收集到了全部服务器的选举状态,若是是,那么这台服务器选举的leader就定下来了,而后根据选举结果设置本身的角色(FOLLOWING仍是LEADER),而后退出选举过程就是了.
    2)即便没有收集到全部服务器的选举状态,也能够根据该节点上选择的最新的leader是否是获得了超过半数以上服务器的支持,若是是,那么当前线程将被阻塞等待一段时间(这个时间在finalizeWait定义)看看是否是还会收到当前leader的数据更优的leader,若是通过一段时间尚未这个新的leader提出来,那么这台服务器最终的leader就肯定了,不然进行下一次选举. 

(2) 若是所接收服务器不在选举状态,也就是在FOLLOWING或者LEADING状态

作如下两个判断:
    a) 若是逻辑时钟相同,将该数据保存到recvset,若是所接收服务器宣称本身是leader,那么将判断是否是有半数以上的服务器选举它,若是是则设置选举状态退出选举过程
    b) 不然这是一条与当前逻辑时钟不符合的消息,那么说明在另外一个选举过程当中已经有了选举结果,因而将该选举结果加入到outofelection集合中,再根 据outofelection来判断是否能够结束选举,若是能够也是保存逻辑时钟,设置选举状态,退出选举过程.
代码以下:

以一个简单的例子来讲明整个选举的过程.
假设有五台服务器组成的zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是同样的.假设这些服务器依序启动,来看看会发生什么.
1) 服务器1启动,此时只有它一台服务器启动了,它发出去的报没有任何响应,因此它的选举状态一直是LOOKING状态
2) 服务器2启动,它与最开始启动的服务器1进行通讯,互相交换本身的选举结果,因为二者都没有历史数据,因此id值较大的服务器2胜出,可是因为没有达到超 过半数以上的服务器都赞成选举它(这个例子中的半数以上是3),因此服务器1,2仍是继续保持LOOKING状态.
3) 服务器3启动,根据前面的理论分析,服务器3成为服务器1,2,3中的老大,而与上面不一样的是,此时有三台服务器选举了它,因此它成为了此次选举的leader.
4) 服务器4启动,根据前面的分析,理论上服务器4应该是服务器1,2,3,4中最大的,可是因为前面已经有半数以上的服务器选举了服务器3,因此它只能接收当小弟的命了.
5) 服务器5启动,同4同样,当小弟.

以上就是fastleader算法的简要分析,还有一些异常状况的处理,好比某台服务器宕机以后的处理,当leader宕机以后的处理等等,后面再谈.

 



三、Leader与Follower同步数据(原子广播)

 

 

      根据 Fast Leader选举算法中 的分析,若是一台zookeeper服务器成为集群中的leader,那么必定是当前全部服务器中保存数据最多(不是最新??)的服务器,因此在这台服务 器成为leader以后,首先要作的事情就是与集群中的其它服务器(如今是follower)同步数据,保证你们的数据一致,这个过程完毕了才开始正式处 理来自客户端的链接请求.

      Fast Leader选举算法中提到的同步数据时使用的逻辑时钟,它的初始值是0,每次选举过程都会递增的,在leader正式上任以后作的第一件事情,就是根据当前保存的数据id值,设置最新的逻辑时钟值。

    随后,leader构建NEWLEADER封包, 该封包的数据是当前最大数据的id,广播给全部的follower,也就是告知follower leader保存的数据id是多少,你们看看是否是须要同步。而后,leader根据follower数量给每一个follower建立一个线程 LearnerHandler,专门负责接收它们的同步数据请求.leader主线程开始阻塞在这里,等待其余follower的回应(也就是 LearnerHandler线程的处理结果),一样的,只有在超过半数的follower已经同步数据完毕,这个过程才能结束,leader才能正式成 为leader.

leader所作的工做:

因此其实leader与follower同步数据的大部分操做都在LearnerHandler线程中处理的,接着看这一块.
leader接收到的来自某个follower封包必定是FOLLOWERINFO,该封包告知了该服务器保存的数据id.以后根据这个数据id与本机保存的数据进行比较:
1) 若是数据彻底一致,则发送DIFF封包告知follower当前数据就是最新的了.
2) 判断这一阶段以内有没有已经被提交的提议值,若是有,那么:
    a) 若是有部分数据没有同步,那么会发送DIFF封包将有差别的数据同步过去.同时将follower没有的数据逐个发送COMMIT封包给follower要求记录下来.
    b) 若是follower数据id更大,那么会发送TRUNC封包告知截除多余数据.(一台leader数据没同步就宕掉了,选举以后恢复了,数据比如今leader更新)
3) 若是这一阶段内没有提交的提议值,直接发送SNAP封包将快照同步发送给follower.
4)消息完毕以后,发送UPTODATE封包告知follower当前数据就是最新的了,再次发送NEWLEADER封包宣称本身是leader,等待follower的响应.

follower作的工做:
(1)会尝试与leader创建链接,这里有一个机制,若是必定时间内没有链接上,就报错退出,从新回到选举状态.
(2)其次在发送FOLLOWERINFO封包,该封包中带上本身的最大数据id,也就是会告知leader本机保存的最大数据id.
(3)根据前面对LeaderHandler的分析,leader会根据不一样的状况发送DIFF,UPTODATE,TRUNC,SNAP,依次进行处理就是了,此时follower跟leader的数据也就同步上了.
(4)因为leader端发送的最后一个封包是UPTODATE,所以在接收到这个封包以后follower结束同步数据过程,发送ACK封包回复leader.

以上过程当中,任何状况出现的错误,服务器将自动将选举状态切换到LOOKING状态,从新开始进行选举.

相关文章
相关标签/搜索