暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

zookeeper原理浅析

Renk 2021-03-20
740

基础概念

zookeeper是一个分布式协调框架,集中式维护配置信息等。提供分布式同步/协调。它的本质是一个分布式的文件系统,类似linux那种文件目录结构。通过zookeeper来协调多个进程之间的活动或数据同步。

znode

zookeeper操作和维护一个个小型的数据节点,这些节点被称为znode,这些节点被统一组成为文件目录树结构,类似下图所示。

节点有4种类型,临时节点,临时顺序节点,持久化节点,持久化顺序节点。

临时节点:由当前进程创建的节点,并且随着该进程结束而删除。

临时顺序节点:在临时节点的基础上加上顺序,具体就是在创建节点时,zookeeper服务会自动为节点名加一个数字后缀,由小到大单调递增。当用zookeeper做分布式锁时,可以基于此特性来做。

持久节点:该节点被创建后,会一直保存在zookeeper服务器上,直到有主动删除操作来删除。

持久化顺序节点:结合了持久化及顺序性的节点。

注意:1.znode节点可以保存一些数据,最大1M。2.临时节点不允许添加子节点。

watcher

zookeeper支持watch操作,client客户端可以在znode节点上注册一个watcher,用来watch(监听)所需要的变化事件,比如删除该节点的事件。

watcher监听原理如下图所示

具体流程如下

1,创建zookeeper client客户端。

2,创建客户端对象同时,启动两个线程,分别是connect和listener。connect的作用是网络通信相关。listener则是用于监听事件相关。

3,connect将客户端需要监听的事件发送给zookeeper的注册监听器

4,注册监听器将该监听事件加入到监听列表中。

5,一旦zookeeper中所监听的数据或路径等发生变化,就会把消息发送给listener线程,同时将其在服务端wathcer管理器中删除。

6,listener线程收到回调事件后会调用内部process()方法处理业务。

zookeeper中的watcher是一次性的,即出发一次后会被取消,如果想要继续watch的话,得重新设置watcher。

集群原理

之前几篇文章也讲过,任何系统单机部署都会存在单节点故障不可用的情况。为了高可用,我们一般有两种方式,一种是主从方式,另一种是主备方式。两个都会涉及到切换的问题。不管是从节点升主,还是主备切换。这里面需要一种机制保证自动切换。zookeeper集群也不例外。它选择的是主从架构(leader-follower)方式部署。内部是通过Zab协议(原子广播协议)选取leader来进行数据同步与服务的。

zookeeper集群服务中各节点有如下几种状态:

LOOKING:竞选状态,投票选主

FOLLOWING:跟随状态,可参与投票

OBSERVING:观察状态,同步leader数据,不参与投票

LEADING:领导者状态

Zab协议

Zab协议即原子广播协议,其有两种机制,一种是崩溃恢复模式,还有一种是广播服务机制。

崩溃恢复模式,当集群开始启动中或者leader服务网络中断/宕机时,就会进入该模式。该模式流程如下

1,启动时集群中所有的服务节点处于LOOKING状态,开始投票选leader。

2,各节点各自投自己,通过相互发送投票,基于先到先得原则,超过半数即为选主成功,各节点除主节点外进入FOLLOWING状态,主节点进入LEADING状态。

3,选主结束,各从节点(follower)同步leader数据。

4,leader将数据同步给所有follower,同时相互之间建立心跳。同步完成后,崩溃恢复模式结束。

广播模式,该模式对外提供服务阶段,主要是响应客户端发来的读写请求等集群内数据同步相关。下面介绍写流程的步骤如图。

整个写入流程其实就是类似一个2PC(两阶段提及)的过程,具体流程如下:

1,客户端发送写请求给zookeeper集群中任意节点。

2.1,如果当前接收请求的节点为follower,则会将请求转发到leader节点,leader负责所有的写入。

2.2,leader节点收到请求后,先在本地生成一个proposal(提案),并分配一个全局唯一单调递增的事务ID(zid)

3,leader服务为每个follower分配一个单独的队列,然后将生成的proposal依次放入到该队列中,并根据FIFO策略广播给对应的follower节点。

4,follower服务节点收到leader发送过来的proposal,先以事务日志方式写入本地磁盘,加入到待写队列成功后,返回给leader服务节点成功的ack响应。

5,leader在接收到大多数(超过一半)follower的成功ack响应后,认为成功后进行commit操作。

6,leader向所有follower广播通知commit操作,同时leader自身也会commit。

7,follower收到commit通知后,将事务提交并返回响应回leader服务。

8,如果在leader通知commit时,突然宕机了,其他follower节点还未将proposal提交到本地,这时集群会进入崩溃恢复模式,选取主节点即leader成功后,会判断当前各节点的等待写入队列中还有未commit的数据,则会继续把未完成的commit继续执行。

zookeeper集群中读取操作相对于写操作简单一些,集群中所有的节点(leader和follower)都可以提供读功能,这正是基于其最终一致性所提供的。

选举机制

选主时并不是无规则的进行,这里涉及到多种判断逻辑在里面。首先是非observer节点才能选举,因为observer节点只负责同步数据,不参与投票。然后选举会根据以下几个重要的参数:

1,ZXID即事务ID,每个节点都会带有自己最大的事务id,值越大说明越新,选举的权重就越大,zxid是一个64位的数字,它的高32位是epoch用来标识leader关系是否改变,每次leader被选出来,它都会对应一个新的epoch,标识当前属于哪个leader的统治任期内,低32位用于单调递增计数,新leader产生后该值变为0。

2,ServerID即服务ID,编号越大权重越大,其在节点中唯一,通过myid文件中配置。

3,Epoch逻辑时钟,也叫投票次数,同一轮投票中该值是相同的,每次投完票后会增加。

判断的顺序为:zxid,server.id,同时都要在looking状态下。

在选举开始时,各节点发起proposal,在提出时都会加上zxid事务ID,得到超过半数的选票即为当选,follower们如果确认来新的leader,知道其epoch。以后收到小于此值的leader epoch都会拒绝。

脑裂问题

在遇到主从架构自动切换时都要考虑到集群脑裂,集群脑裂简单来讲就是集群中出现来两个从节点,导致不知道向谁同步数据/写入数据,类似两个大脑。而zookeepr集群解决脑裂问题简单来讲就是上面讲的,基于过半数的选主和ZXID事务id(带epoch)机制就可以保证不会出问题。

分布式锁

用zookeeper做分布式锁,前面也讲过就是基于其临时节点来做一个简单实现。方案如下图所示:

1,客户端调用create创建方法,在自定目录下创建临时顺序节点如/test/thread_id-{1}

2,客户端调用getChildren()方法获取该节点下所有的子节点

3,判断当前创建的节点序号是否是最小的,如果是最小节点则表明当前客户端最先创建该临时顺序节点,即获取到锁。

4,若当前节点前还有更小序号的节点,则客户端注册一个watch,对第一个小于自己的节点上监听其删除事件。

5,当被客户端监听的节点被删除了,客户端收到通知后,继续从第2步操作进行,直到获取dao锁。

6,不管是客户端自己释放锁(删除创建的节点),还是客户端未释放锁而宕机了,zookeeper会有定时检查机制判断该客户端是否还在,否则会自动删除并发送相应的删除事件给注册了该事件的客户端通知。

最后

zookeeper在大数据应用中是很常见的,Hadoop中会使用。在分布式集群中也可以作为分布式数据中心,为诸如dubbo等提供注册中心。当然做分布式锁也是常见方案。


文章转载自Renk,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论