Zookeeper相关原理解析

一、Zookeeper节点的4种状态:
      LEADING:说明此节点已经是leader节点,处于领导者地位的状态,差不多就是一般集群中的master。但在zookeeper中,只有leader才有写权限,其他节点(FOLLOWING)是没有写权限的,可以读

      LOOKING:选举中,正在寻找leader,即将进入leader选举流程中

      FOLLOWING:跟随者,表示当前集群中的leader已经选举出来了,主要具备以下几个功能点             

                向leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息)

                接收leader消息并进行处理;

                接收client发送过来的请求,如果为写请求,会发送给Leader进行投票处理,然后返回client结果。

      OBSERVING:OBSERVING和FOLLOWING差不多,但不参加投票和选举,接受leader选举后的结果

二、Zookeeper中Leader选举

初始化
当启动初始化集群的时候,server1的myid为1,zxid为0   server2的myid为2,zxid同样是0,以此类推。此种情况下zxid都是为0。先比较zxid,再比较myid

服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking(选举状态)。
服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的myid大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。
服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的myid最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟。
服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的myid大,但之前服务器3已经胜出,所以服务器4只能成为小弟。
服务器5启动,后面的逻辑同服务器4成为小弟
当选举机器过半的时候,已经选举出leader后,后面的就跟随已经选出的leader,所以4和5跟随成为leader的server3

所以,在初始化的时候,一般到过半的机器数的时候谁的myid最大一般就是leader

运行期间
按照上述初始化的情况,server3成为了leader,在运行期间处于leader的server3挂了,那么非Observer服务器server1、server2、server4、server5会将自己的节点状态变为LOOKING状态

1、开始进行leader选举。现在选举同样是根据myid和zxid来进行

2、首先每个server都会给自己投一票竞选leader。假设server1的zxid为123,server2的zxid为124,server4的zxid为169,server5的zxid为188

3、同样先是比较zxid再比较,server1、server2、server4比较server4根据优先条件选举为leader。然后server5还是跟随server4,即使server5的zxid最大,但是当选举到server4的时候,机器数已经过半。不再进行选举,跟随已经选举的leader

zookeeper集群为保证数据的一致性所有的操作都是由leader完成,之后再由leader同步给follower。重点就在这儿,zookeeper并不会确保所有节点都同步完数据,只要有大多数节点(即n/2+1)同步成功即可。

咱们假设有一个写操作成功那么现在数据只存在于节点leader,之后leader再同步给其他follower。这时候宕掉3个机器,已经过半的机器无法进行投票选举,剩余2台不足过半,无法选举=无法提供任何服务。再启动一个机器恢复服务。所以宕掉的机器不要过半,过半就会导致无法正常服务。

在leader选举的时候会有30s-120s的过程,在这期间也是无法提供服务的。如果用zookeeper要作为服务发现是个弊端,基本无法忍受,zookeeper本身是一个CP系统,保证数据的一致性,在恢复的时候再提供服务,并没有多好高可用的方案。如果leader发生故障选举时无法提供服务发现对一个大型应用来说可能是致命的。它可以为同在一个分布式系统中的其他服务提供:统一命名服务、配置管理、分布式锁服务、集群管理等功能)是个伟大的开源项目,很成熟
 

选举比对值分析
zxid:zxid(64位)又称事务id,它如果最大,那么会成为leader。它越大,数据也越新。
myid:服务器id,又称sid。它如果越大,在leader选举机制中权重越大。
epoch:属于zxid里的高32位,每一轮leader选举投票,它都会递增

QuorunCnxManager:网络IO,通信

每台服务器再启动的过程中,会启动一个QuorCnxManager,负责各个服务器之间底层的通信,这是实现leader选举的必要前提。

QuorCnxManager中维护了一系列的队列,用来保存接受到的,待发送的消息以及消息的发送器

recvQueue:消息接受队列,用于存放那些从其他服务器接受到的消息

queueSendMap:消息发送队列,用于保存那些待发送的消息,按照SID进行分组

senderWorkerMap:发送器集合,每个SenderWorker消息发送器,都对应一台远程Zookeeper服务器,负责消息的发送

lastMessageSent:最近发送过的消息,为每个SID保留最近发送过的一个消息

建立连接:为了能够相互投票,zookeeper集群中的所有机器都需要俩俩建立起网络连接,QuorumCnxManager在启动的时候会创建一个ServerSocket来监听leader悬崖边还有的通信端口,默认3888。开启监听后, zookeeper能够不断的接受来自其他服务器的创建连接请求,在接受其他服务器的TCP请求时会进行处理,为了避免俩台机器之间重复的创建TCP连接,zookeeper只允许SID大的服务器主动连接其他服务器,否则断开连接,在接收到创建连接请求后,服务器通过对比自己和远程服务器的sid值来判断时候接受连接请求,一旦建立连接,就会根据远程服务器的sid创建对应的消息发送器SendWorker和消息接收RecvWorker,

消息接受与发送

消息接受:由消息接收器RecvWorker负责,由于zookeeper为每隔远程服务器都分配一个单独的RecvWorker,因此,每个RecvWorker只需要不断地从这个连接中读取消息,并且将其消息保存到recvQueue队列中

消息发送:由于zookeeper中为每个远程服务器都分配一个单独的SendWorker,因此,每个SendWorker只需要不断地从对应的消息发送队列中获取出一个消息发送即可,同时将这个消息放入到lastMessageSent中,在SendWorker中,一旦zookeeper发现针对当前服务器的消息发送队列为空,那么此时就需要从lastMessageSent中取出一个最近发送过的消息来进行再次发送,这是为了解决接收方在消息接受前或者接受到消息后服务器挂了,导致消息尚未被处理

FaseLeadElection:选举算法核心

选举轮次:Zookeeper服务器Leader选举的轮次,即logicalclock。

sendQueue:选票发送队列,用于保存待发送的选票

recvQueue:选票接受队列,用于保存接受到的外部服务器的投票

WorkerReceiver:选票接收器,会不断地从QuorumCnxMangager中获取其他服务器发送过来的选举消息,并将其保存到recvQueue中,在选票接受的过程中,如果发现该外部选票的选举轮次小于当前服务器的,那么忽略外投票,同时立即发送自己的内部投票

workerSender:选票发送器,不断地从sendqueue中获取待发送的选票,并将其传递到底层QuorumCnxManager中

1、自增选举轮次。Zookeeper规定所有有效的投票都必须在同一轮次中,在开始新一轮投票时,会首先对logicalclock进行自增操作。

2、初始化选票。在开始进行新一轮投票之前,每个服务器都会初始化自身的选票,并且在初始化阶段,每台服务器都会将自己推举为Leader。

3、发送初始化选票。完成选票的初始化后,服务器就会发起第一次投票。Zookeeper会将刚刚初始化好的选票放入sendqueue中,由发送器WorkerSender负责发送出去。

4、接收外部投票。每台服务器会不断地从recvqueue队列中获取外部选票。如果服务器发现无法获取到任何外部投票,那么就会立即确认自己是否和集群中其他服务器保持着有效的连接,如果没有连接,则马上建立连接,如果已经建立了连接,则再次发送自己当前的内部投票。

5、判断选举轮次。在发送完初始化选票之后,接着开始处理外部投票。在处理外部投票时,会根据选举轮次来进行不同的处理。

  • 外部投票的选举轮次大于内部投票。若服务器自身的选举轮次落后于该外部投票对应服务器的选举轮次,那么就会立即更新自己的选举轮次(logicalclock),并且清空所有已经收到的投票,然后使用初始化的投票来进行PK以确定是否变更内部投票。最终再将内部投票发送出去。

  • 外部投票的选举轮次小于内部投票。若服务器接收的外选票的选举轮次落后于自身的选举轮次,那么Zookeeper就会直接忽略该外部投票,不做任何处理,并返回步骤4。

  • 外部投票的选举轮次等于内部投票。此时可以开始进行选票PK。

6、选票PK。在进行选票PK时,符合任意一个条件就需要变更投票。

  • 若外部投票中推举的Leader服务器的选举轮次大于内部投票,那么需要变更投票。

  • 若选举轮次一致,那么就对比两者的ZXID,若外部投票的ZXID大,那么需要变更投票。

  • 若两者的ZXID一致,那么就对比两者的SID,若外部投票的SID大,那么就需要变更投票。

7、变更投票。经过PK后,若确定了外部投票优于内部投票,那么就变更投票,即使用外部投票的选票信息来覆盖内部投票,变更完成后,再次将这个变更后的内部投票发送出去。

8、选票归档。无论是否变更了投票,都会将刚刚收到的那份外部投票放入选票集合recvset中进行归档。recvset用于记录当前服务器在本轮次的Leader选举中收到的所有外部投票(按照服务队的SID区别,如{(1, vote1), (2, vote2)...})。

9、统计投票。完成选票归档后,就可以开始统计投票,统计投票是为了统计集群中是否已经有过半的服务器认可了当前的内部投票,如果确定已经有过半服务器认可了该投票,则终止投票。否则返回步骤4。

10、更新服务器状态。若已经确定可以终止投票,那么就开始更新服务器状态,服务器首选判断当前被过半服务器认可的投票所对应的Leader服务器是否是自己,若是自己,则将自己的服务器状态更新为LEADING,若不是,则根据具体情况来确定自己是FOLLOWING或是OBSERVING

【二】zookeeper中watcher机制解析

 

Zookeeper中引入了watcher机制实现分布式的通知功能,允许客户端向服务端注册一个watcher监听,当服务端的一些事件触发了这个watcher,那么就会向客户端发送一个事件通知来实现分布式的通知功能。触发事件的种类很多,如创建节点、删除节点,修改节点,子节点改变

总的来说watcher有以下三个过程

客户端向服务器端注册watcher

服务器端事件发生触发watcher

客户端回调watcher得到触发事件的情况

watcher机制特性

次性触发

事件发生触发监听,一个watcher event就会发送到设置监听的客户端,这种效果是一次性的,因为事件其实是存储在Map中,如果不是一次性的话,会浪费内存空间

事件封装 

使用watcherEvent对象来封装服务端事件并传递,每一个事件有通知状态,事件类型,节点路径三个基本属性

event异步发送

watcher的通知事件从服务器发送到客户端是异步的

先注册再触发

Watcher机制:

1 客户端向zk服务器注册watcher的同时,会将watcher的对象储存在客户端的watcherManager中;

2 zk服务器的触发watcher事件后会向客户端发送通知,客户端线程从watcherManager中掉起watcher执行

【三】zookeeper中数据同步