Zookeeper原理

最近做项目要用到Kafka和Zookeeper,网上资料也不少,但是总是零零碎碎的,好像都没有讲太清楚。文章根据引用文章整理而成,一直看的迷迷糊糊,希望能不断更新,纠正错误,方便自己和大家学习。

Kafka参考:https://blog.csdn.net/yangwei256/article/details/83787293


Zookeeper原理

目录

1 Zookeeper的基本概念.... 1

1.1 Zookeeper的定义... 1

1.2 Zookeeper的作用... 1

1.2.1 配置管理.... 1

1.2.2 名字服务.... 2

1.2.3 分布式锁.... 2

1.2.4 集群管理.... 3

1.3 文件系统/数据模型.... 4

1.4 通知机制.... 5

1.5 会话.... 5

1.6 手表.... 5

1.7 Zookeeper的优点... 6

1.7.1 为什么要用Zookeeper?.... 6

1.7.2 Zookeeper的优点... 6

2 Zookeeper的工作原理、过程.... 9

2.1 Zookeeper的架构... 9

2.2 Zookeeper工作原理与流程.... 10

2.2.1 Leader选举与数据同步.... 11

2.2.2 Leader选举的算法详细(扩展)... 17

2.2.3 Zookeeper节点数据操作流程.... 21

2.2.4 ZAB处理过程.... 23

引用文献:.... 28

 

1.Zookeeper的基本概念

1.1 Zookeeper的定义

Zookeeper从名字直译为动物园管理员,管理园子里老虎、狮子,狗狗,兔子等动物,可以辅助我们形象的理解。

定义:Zookeeper 是分布式系统中,实现配置管理、名字服务、同步服务和集群管理的应用程序协调服务,相当于分布式集群的管理者或协调者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户。

1.2 Zookeeper的作用

Zookeeper在分布式系统中主要有四大作用,配置管理、名字服务、同步服务和集群管理等。

1.2.1 配置管理

分布式系统都有好多机器,各个机器拿到的配置信息是一致的,才能成功运行系统。Zookeeper提供了这样的一种服务:一种集中管理配置的方法,我们在这个集中的地方修改了配置,所有对这个配置感兴趣的都可以获得变更。这样就省去手动拷贝配置了,还保证了可靠和一致性,如图1-1所示。

https://i-blog.csdnimg.cn/blog_migrate/1675854c1104c6ecb57e8a726fd213cc.png

图1-1 Zookeeper配置管理的作用

1.2.2 名字服务

这个可以简单理解为一个电话薄,电话号码不好记,但是人名好记,要打谁的电话,直接查人名就好了。 分布式环境下,经常需要对应用/服务进行统一命名,便于识别不同服务;类似于域名与ip之间对应关系,域名容易记住;通过名称来获取资源或服务的地址,提供者等信息。

1.2.3 分布式锁

分布式程序分布在各个主机上的进程对互斥资源进行访问时需要加锁,保证在我访问时候你不能访问。很多分布式系统有多个可服务的窗口,但是在某个时刻只让一个服务去干活,当这台服务出问题的时候锁释放,立即fail over到另外的服务。这在很多分布式系统中都是这么做,这种设计有一个更好听的名字叫Leader Election(leader选举)。举个通俗点的例子,比如银行取钱,有多个窗口,但是呢对你来说,只能有一个窗口对你服务,如果正在对你服务的窗口的柜员突然有急事走了,那咋办?找大堂经理(zookeeper)!大堂经理指定另外的一个窗口继续为你服务!

​​​​​​​1.2.4集群管理

在分布式的集群中,经常会由于各种原因,比如硬件故障,软件故障,网络问题,有些节点会进进出出。有新的节点加入进来,也有老的节点退出集群。这个时候,集群中有些机器(比如Master节点)需要感知到这种变化,然后根据这种变化做出对应的决策。我已经知道HDFS中namenode是通过datanode的心跳机制来实现上述感知的,那么我们可以先假设Zookeeper其实也是实现了类似心跳机制的功能吧!如图1-2所示。

https://i-blog.csdnimg.cn/blog_migrate/b95d2a899743435f42a07a4855503c01.png

图1-2 Zookeeper的集群管理功能

​​​​​​​1.3 文件系统/数据模型

https://i-blog.csdnimg.cn/blog_migrate/0392e9d3452f3fd50193c6811c8fcf89.png

图1-3 Zookeeper的文件系统

每个子目录项如 NameService 都被称作为znode,和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一的不同在于znode是可以存储数据的。

有四种类型的znode:

(1)、PERSISTENT-持久化目录节点

客户端与zookeeper断开连接后,该节点依旧存在

(2)、PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点

客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号

(3)、EPHEMERAL-临时目录节点

客户端与zookeeper断开连接后,该节点被删除

(4)、EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点

客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号。

​​​​​​​1.4 通知机制

户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。

1.5 会话

会话对于ZooKeeper的操作非常重要。 会话中的请求按FIFO顺序执行。 一旦客户端连接到服务器,将建立会话并向客户端分配会话ID 。

客户端以特定的时间间隔发送心跳以保持会话有效。 如果ZooKeeper集合没有从客户端接收到超过在服务启动时指定的时间段(会话超时)的心跳,则它决定客户端死亡。

会话超时通常用毫秒表示。 当会话由于任何原因结束时,在该会话期间创建的临时znode也会被删除。

​​​​​​​1.6 手表

手表是一种简单的机制,使客户端得到关于ZooKeeper集合中的更改的通知。 客户端可以在读取特定znode时设置手表。 手表会向注册的客户端发送任何znode(客户端注册表)更改的通知。

Znode更改是与znode的相关数据的修改或znode的孩子的更改。 只触发一次手表。 如果客户端想要再次通知,则必须通过另一个读取操作来完成。 当连接会话过期时,客户端将与服务器断开连接,相关联的手表也将被删除。

1.7 Zookeeper的优点

1.7.1 为什么要用Zookeeper?

大部分分布式应用需要一个主控、协调器或控制器来管理物理分布的子进程(如资源、任务分配等),没有zookeeper,大部分应用需要开发私有的协调程序,缺乏一个通用的机制,协调程序的反复编写浪费资源和效率,且难以形成通用、伸缩性好的协调器。

对比 Keepalived:提供通用的分布式锁服务,用以协调分布式应用但是:
    • Keepalived监控节点不好管理
    • Keepalive 采用优先级监控
    • 没有协同工作
    • 功能单一
    • Keepalive可扩展性差

​​​​​​​1.7.2 Zookeeper的优点

Zookeeper在分布式系统中应用广泛,有很多优点,主要包括最终一致性、可靠性、可用性、实时性、等待无关、原子性、顺序性、单系统镜像等。

(1)最终一致性:client不论连接到哪个Server,展示给它都是同一个视图,这是zookeeper最重要的性能。

(2)可靠性:具有简单、健壮、良好的性能,如果消息m被到一台服务器接受,那么它将被所有的服务器接受。一旦一个更新操作被应用,那么在客户端再次更新它之前,它的值将不会改变。这个保证将会产生下面两种结果:

  1. 如果客户端成功地获得了正确的返回代码,那么说明更新已经成果。如果不能够获得返回代码(由于通信错误、超时等等),那么客户端将不知道更新操作是否生效。
  2. 当从故障恢复的时候,任何客户端能够看到的执行成功的更新操作将不会被回滚。

(3)可用性:Zookeeper保证了可用性,数据总是可用的,没有锁.并且有一大半的节点所拥有的数据是最新的,实时的. 如果想保证取得

(4)实时性:Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。Zookeeper提供的一致性是弱一致性,首先数据的复制有如下规则:zookeeper确保对znode树的每一个修改都会被复制到集合体中超过半数的机器上,那么就有可能有节点的数据不是最新的而被客户端访问到,并且会有一个时间点,在集群中是不一致的,同时由于网络延时等原因,Zookeeper不能保证两个客户端能同时得到刚更新的数据。因此Zookeeper只保证最终一致性,但是实时的一致性可以由客户端调用自己来保证,通过调用sync()方法,在十几秒可以Sync到各个节点。

(5)等待无关(wait-free):慢的或者失效的client不得干预快速的client的请求,使得每个client都能有效的等待。

(6)原子性:更新只能成功或者失败,没有中间状态。

(7)顺序性:包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有Server上消息a都将在消息b前被发布;偏序是指如果一个消息b在消息a后被同一个发送者发布,a必将排在b前面。

(8)节点多了会导致写数据延时非常大,因为需要多个节点同步,节点多了Leader选举非常耗时, 就会放大网络的问题. 可以通过引入observer节点缓解这个问题.

9. 单系统镜像:无论客户端连接到哪一个服务器,客户端将看到相同的 ZooKeeper 视图。

https://i-blog.csdnimg.cn/blog_migrate/238ded5bd41abe3e5131941c1ab15440.png

图1.3 Zookeeper的主要优点

2 Zookeeper的工作原理、过程

2.1 Zookeeper的架构

在Zookeeper集群中,主要分为三者角色,而每一个节点同时只能扮演一种角色。这三种角色分别是:

(1). Leader 

接受所有Follower的提案请求并统一协调发起提案的投票,负责与所有的Follower进行内部的数据交换(同步);

(2). Follower 

直接为客户端服务并参与提案的投票,同时与Leader进行数据交换(同步);

(3). Observer 

直接为客户端服务但并不参与提案的投票,同时也与Leader进行数据交换(同步);

Zookeeper的基本架构图如图2-1所示,Zookeeper中的角色如图2-2所示。

https://i-blog.csdnimg.cn/blog_migrate/95838218aab11faf8271211233e39649.png

2-1 Zookeeper基本架构

  1. 每个Server在内存中存储了一份数据;
  2. 2 Zookeeper启动时,将从实例中选举一个leader(Paxos协议);
  3. 3 Leader负责处理数据更新等操作(Zab协议);
  4. 4 一个更新操作成功,当且仅当大多数Server在内存中成功修改 数据。

图 2-2 Zookeeper中的角色

​​​​​​​2.2 Zookeeper工作原理与流程

Zookeeper的核心是原子广播,这个机制保证了各个server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式和广播模式。

当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数server完成了和leader的状态同步以后,恢复模式就结束了。

状态同步保证了leader和server具有相同的系统状态,一旦leader已经和多数的follower进行了状态同步后,他就可以开始广播消息了,即进入广播状态。这时候当一个server加入zookeeper服务中,它会在恢复模式下启动,发现leader,并和leader进行状态同步。待到同步结束,它也参与消息广播。Zookeeper服务一直维持在Broadcast状态,直到leader崩溃了或者leader失去了大部分的followers支持。

广播模式需要保证proposal被按顺序处理,因此zk采用了递增的事务id号(zxid)来保证。所有的提议(proposal)都在被提出的时候加上了zxid。

实现中zxid是一个64为的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch。低32位是个递增计数。

每个Server在工作过程中有三种状态:

LOOKING:当前Server不知道leader是谁,正在搜寻

LEADING:当前Server即为选举出来的leader

FOLLOWING:leader已经选举出来,当前Server与之同步

​​​​​​​2.2.1 Leader选举与数据同步

(1)选取leader——Fast Leader选举算法

如何在zookeeper集群中选举出一个leader,zookeeper使用了三种算法,具体使用哪种算法,在配置文件中是可以配置的,对应的配置项是”electionAlg”,其中1对应的是LeaderElection算法,2对应的是AuthFastLeaderElection算法,3对应的是FastLeaderElection算法.默认使用FastLeaderElection算法。

要理解这个算法,最好需要一些paxos算法的理论基础.可以参考小编之前的文章(https://blog.csdn.net/yangwei256/article/details/83337563)

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)接受来自其他节点的数据

每台服务器将自己服务器的以上数据发送到集群中的其他服务器之后,同样的也需要接收来自其他服务器的数据,它将做以下的处理:

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就确定了,否则进行下一次选举.

3.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宕机之后的处理等等,后面再谈.

(2)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状态,重新开始进行选举.

https://i-blog.csdnimg.cn/blog_migrate/1e0236dec7167206554baf1b84dba5bb.png

图2-3 Leader选举并同步数据.

​​​​​​​2.2.2 Leader选举的算法详细(扩展)

(1)LeaderElection选举算法

选举线程由当前Server发起选举的线程担任,他主要的功能对投票结果进行统计,并选出推荐的Server。选举线程首先向所有Server发起一次询问(包括自己),被询问方,根据自己当前的状态作相应的回复,选举线程收到回复后,验证是否是自己发起的询问(验证xid 是否一致),然后获取对方的id(myid),并存储到当前询问对象列表中,最后获取对方提议 的

leader 相关信息(id,zxid),并将这些 信息存储到当次选举的投票记录表中,当向所有Server都询问完以后,对统计结果进行筛选并进行统计,计算出当次询问后获胜的是哪一个Server,并将当前zxid最大的Server 设置为当前Server要推荐的Server(有可能是自己,也有可以是其它的Server,根据投票结果而定,但是每一个Server在第一次投票时都会投自己),如果此时获胜的Server获得n/2 + 1的Server票数,设置当前推荐的leader为获胜的Server。根据获胜的Server相关信息设置自己的状态。每一个Server都重复以上流程直到选举出Leader。

初始化选票(第一张选票): 每个quorum节点一开始都投给自己;

收集选票: 使用UDP协议尽量收集所有quorum节点当前的选票(单线程/同步方式),超时设置200ms;

统计选票: 1).每个quorum节点的票数;

         2).为自己产生一张新选票(zxid、myid均最大);

选举成功: 某一个quorum节点的票数超过半数;

更新选票: 在本轮选举失败的情况下,当前quorum节点会从收集的选票中选取合适的选票(zxid、myid均最大)作为自己下一轮选举的投票;

异常问题的处理

1). 选举过程中,Server的加入

当一个Server启动时它都会发起一次选举,此时由选举线程发起相关流程,那么每个 Serve r都会获得当前zxid最大的哪个Serve r是谁,如果当次最大的Serve r没有获得n/2+1 个票数,那么下一次投票时,他将向zxid最大的Server投票,重复以上流程,最后一定能选举出一个Leader。

2). 选举过程中,Server的退出

只要保证n/2+1个Server存活就没有任何问题,如果少于n/2+1个Server 存活就没办法选出Leader。

3). 选举过程中,Leader死亡

当选举出Leader以后,此时每个Server应该是什么状态(FLLOWING)都已经确定,此时由于Leader已经死亡我们就不管它,其它的Fllower按正常的流程继续下去,当完成这个流程以后,所有的Fllower都会向Leader发送Ping消息,如果无法ping通,就改变自己的状为(FLLOWING ==> LOOKING),发起新的一轮选举。

4). 选举完成以后,Leader死亡

处理过程同上。

5). 双主问题

Leader的选举是保证只产生一个公认的Leader的,而且Follower重新选举与旧Leader恢复并退出基本上是同时发生的,当Follower无法ping同Leader是就认为Leader已经出问题开始重新选举,Leader收到Follower的ping没有达到半数以上则要退出Leader重新选举。

(2)FastLeaderElection选举算法

FastLeaderElection是标准的fast paxos的实现,它首先向所有Server提议自己要成为leader,当其它Server收到提议以后,解决 epoch 和 zxid 的冲突,并接受对方的提议,然后向对方发送接受提议完成的消息。

FastLeaderElection算法通过异步的通信方式来收集其它节点的选票,同时在分析选票时又根据投票者的当前状态来作不同的处理,以加快Leader的选举进程。

每个Server都一个接收线程池和一个发送线程池, 在没有发起选举时,这两个线程池处于阻塞状态,直到有消息到来时才解除阻塞并处理消息,同时每个Serve r都有一个选举线程(可以发起选举的线程担任)。

1). 主动发起选举端(选举线程)的处理

首先自己的 logicalclock加1,然后生成notification消息,并将消息放入发送队列中,系统中配置有几个Server就生成几条消息,保证每个Server都能收到此消息,如果当前Server 的状态是LOOKING就一直循环检查接收队列是否有消息,如果有消息,根据消息中对方的状态进行相应的处理。

2).主动发送消息端(发送线程池)的处理

将要发送的消息由Notification消息转换成ToSend消息,然后发送对方,并等待对方的回复。

3). 被动接收消息端(接收线程池)的处理

将收到的消息转换成Notification消息放入接收队列中,如果对方Server的epoch小于logicalclock则向其发送一个消息(让其更新epoch);如果对方Server处于Looking状态,自己则处于Following或Leading状态,则也发送一个消息(当前Leader已产生,让其尽快收敛)。

(3) AuthFastLeaderElection选举算法

AuthFastLeaderElection算法同FastLeaderElection算法基本一致,只是在消息中加入了认证信息,该算法在最新的Zookeeper中也建议弃用。

​​​​​​​2.2.3 Zookeeper节点数据操作流程

https://i-blog.csdnimg.cn/blog_migrate/f666cdd881d5213f49209dc0a1e92507.png

图 2-4节点数据操作流程图

Zookeeper节点数据操作流程

写入数据:

(1)在Client向Follwer发出一个写的请求

(2)Follwer把请求发送给Leader

(3)Leader接收到以后开始发起投票并通知Follwer进行投票

(4)Follwer把投票结果发送给Leader

(5)Leader将结果汇总后如果需要写入,则开始写入同时把写入操作通知给Leader,然后commit;

(6)Follwer把请求结果返回给Client

读取数据没有投票过程,直接通过客户端和follower交互即可完成。详细过程见图2-4、2-5和2-6所示。 值得注意的是,Follower/Leader上的读操作是并行的,读写操作是串行的,当CommitRequestProcessor处理一个写请求时,会阻塞之后所有的读写请求。

https://i-blog.csdnimg.cn/blog_migrate/39f9ee15ca94bd80cf8884c2376bda34.png

图2-5 Follwer节点处理用户的读写请求流程

https://i-blog.csdnimg.cn/blog_migrate/afd37fb4050491d6a0b87b4440fb55ef.png

图2-6 Leader节点处理写请求流程

Follower主要有四个功能:

(1)向Leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息);

(2)接收Leader消息并进行处理;

(3)接收Client的请求,如果为写请求,发送给Leader进行投票;

(4)返回Client结果。

Follower的消息循环处理如下几种来自Leader的消息:

(1)PING消息: 心跳消息;

(2)PROPOSAL消息:Leader发起的提案,要求Follower投票;

(3)COMMIT消息:服务器端最新一次提案的信息;

(4)UPTODATE消息:表明同步完成;

(5) REVALIDATE消息:根据Leader的REVALIDATE结果,关闭待revalidate的session还是允许其接受消息;

(6)SYNC消息:返回SYNC结果到客户端,这个消息最初由客户端发起,用来强制得到最新的更新。

​​​​​​​2.2.4 ZAB处理过程

Zookeeper 客户端会随机连接到 Zookeeper 集群的一个节点,如果是读请求,就直接从当前节点中读取数据;如果是写请求,那么节点就会向 leader 提交事务,leader 会广播事务,只要有超过半数节点写入成功,该写请求就会被提交(类 2PC 协议)。

那么问题来了:

•     主从架构下,leader 崩溃,数据一致性怎么保证?

•     选举 leader 的时候,整个集群无法处理写请求的,如何快速进行 leader 选举?

术语解释

•     quorum:集群中超过半数的节点集合

ZAB 中的节点有三种状态

•     following:当前节点是跟随者,服从 leader 节点的命令

•     leading:当前节点是 leader,负责协调事务

•     election/looking:节点处于选举状态

代码实现中多了一种:observing 状态,这是 Zookeeper 引入 Observer 之后加入的,Observer 不参与选举,是只读节点,跟 ZAB 协议没有关系。

节点的持久状态

•     history当前节点接收到事务提议的 log

•     acceptedEpochfollower 已经接受的 leader 更改年号的 NEWEPOCH 提议

•     currentEpoch当前所处的年代

•     lastZxidhistory 中最近接收到的提议的 zxid (最大的)

在 ZAB 协议的事务编号 Zxid 设计中,Zxid 是一个 64 位的数字,其中低 32 位是一个简单的单调递增的计数器,针对客户端每一个事务请求,计数器加 1;而高 32 位则代表 Leader 周期 epoch 的编号,每个当选产生一个新的 Leader 服务器,就会从这个 Leader 服务器上取出其本地日志中最大事务的ZXID,并从中读取 epoch 值,然后加 1,以此作为新的 epoch,并将低 32 位从 0 开始计数。

epoch:可以理解为当前集群所处的年代或者周期,每个 leader 就像皇帝,都有自己的年号,所以每次改朝换代,leader 变更之后,都会在前一个年代的基础上加 1。这样就算旧的 leader 崩溃恢复之后,也没有人听他的了,因为 follower 只听从当前年代的 leader 的命令。*

Phase 0: Leader election(选举阶段)

节点在一开始都处于选举阶段,只要有一个节点得到超半数节点的票数,它就可以当选准 leader。只有到达 Phase 3 准 leader 才会成为真正的 leader。这一阶段的目的是就是为了选出一个准 leader,然后进入下一个阶段。

协议并没有规定详细的选举算法,常用 Fast Leader Election。

Phase 1: Discovery(发现阶段)

在这个阶段,followers 跟准 leader 进行通信,同步 followers 最近接收的事务提议。这个一阶段的主要目的是发现当前大多数节点接收的最新提议,并且准 leader 生成新的 epoch,让 followers 接受,更新它们的 acceptedEpoch。

一个 follower 只会连接一个 leader,如果有一个节点 f 认为另一个 follower p 是 leader,f 在尝试连接 p 时会被拒绝,f 被拒绝之后,就会进入 Phase 0。

Phase 2: Synchronization(同步阶段)

同步阶段主要是利用 leader 前一阶段获得的最新提议历史,同步集群中所有的副本。只有当 quorum 都同步完成,准 leader 才会成为真正的 leader。follower 只会接收 zxid 比自己的 lastZxid 大的提议。

Phase 3: Broadcast(广播阶段)

到了这个阶段,Zookeeper 集群才能正式对外提供事务服务,并且 leader 可以进行消息广播。同时如果有新的节点加入,还需要对新节点进行同步。

值得注意的是,ZAB 提交事务并不像 2PC 一样需要全部 follower 都 ACK,只需要得到 quorum (超过半数的节点)的 ACK 就可以了。

协议实现

协议的 Java 版本实现跟上面的定义有些不同,选举阶段使用的是 Fast Leader Election(FLE),它包含了 Phase 1 的发现职责。因为 FLE 会选举拥有最新提议历史的节点作为 leader,这样就省去了发现最新提议的步骤。实际的实现将 Phase 1 和 Phase 2 合并为 Recovery Phase(恢复阶段)。所以,ZAB 的实现只有三个阶段:

•     Fast Leader Election

•     Recovery Phase

•     Broadcast Phase

Fast Leader Election

前面提到 FLE 会选举拥有最新提议历史(lastZixd最大)的节点作为 leader,这样就省去了发现最新提议的步骤。这是基于拥有最新提议的节点也有最新提交记录的前提。

成为 leader 的条件

1.   选epoch最大的

2.   epoch相等,选 zxid 最大的

3.   epoch和zxid都相等,选择server id最大的(就是我们配置zoo.cfg中的myid)

节点在选举开始都默认投票给自己,当接收其他节点的选票时,会根据上面的条件更改自己的选票并重新发送选票给其他节点,当有一个节点的得票超过半数,该节点会设置自己的状态为 leading,其他节点会设置自己的状态为 following。

Recovery Phase (恢复阶段)

这一阶段 follower 发送它们的 lastZixd 给 leader,leader 根据 lastZixd 决定如何同步数据。这里的实现跟前面 Phase 2 有所不同:Follower 收到 TRUNC 指令会中止 L.lastCommittedZxid 之后的提议,收到 DIFF 指令会接收新的提议。

history.lastCommittedZxid:最近被提交的提议的 zxid

history:oldThreshold:被认为已经太旧的已提交提议的 zxid

经过上面的分析,我们可以来回答开始提到的两个问题

     主从架构下,leader 崩溃,数据一致性怎么保证?

leader 崩溃之后,集群会选出新的 leader,然后就会进入恢复阶段,新的 leader 具有所有已经提交的提议,因此它会保证让 followers 同步已提交的提议,丢弃未提交的提议(以 leader 的记录为准),这就保证了整个集群的数据一致性。

•     选举 leader 的时候,整个集群无法处理写请求的,如何快速进行 leader 选举?

这是通过 Fast Leader Election 实现的,leader 的选举只需要超过半数的节点投票即可,这样不需要等待所有节点的选票,能够尽早选出 leader。


引用文献:

1、https://blog.csdn.net/xqb_756148978/article/details/52259381

2、https://blog.csdn.net/u013068377/article/details/52620647

3https://blog.csdn.net/xuxiuning/article/details/51218941?utm_source=blogxg

wz0

4、https://blog.csdn.net/xhh198781/article/details/10949697

5、http://www.cnblogs.com/raphael5200/p/5285583.html

6、https://www.ctolib.com/docs-zookeeper

7、https://blog.csdn.net/xhh198781/article/details/10949697

8、http://blog.xiaohansong.com/2016/08/25/zab/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值