ZooKeeper简单概念及原理/应用场景

字数 8118阅读 2267

一个真正的写数据流程是怎么样的?一个真正的读数据流程是怎么样的?一个真正的同步数据流程是怎么样的?从哪里到哪里?什么时候开始进行同步?同步点是什么?同步的数据有哪些?ZXID最大意味着什么?

1.ZooKeeper是什么?

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户

2.ZooKeeper提供了什么?1)文件系统(持久化节点  和  临时节点的使用可以方便进行服务注册和服务发现等集群管理  以及顺序编号可以方便实现分布式锁(保持时序的)和分布式队列(先进先出)以及命名服务) 2)通知机制(监听机制可以实现 数据的发布与订阅 负载均衡 以及配置管理)

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

有四种类型的znode:(临时节点可以用来服务发现 服务注册   顺序编号可以用来实现保持时序的分布式锁 命名服务(分布式自增id)分布式队列先进先出 )

1、PERSISTENT-持久化目录节点:客户端与zookeeper断开连接后,该节点依旧存在

2、PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点:客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号

3、EPHEMERAL-临时目录节点:客户端与zookeeper断开连接后,该节点被删除(node_0000000001)

4、EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点:客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号


4.Zookeeper通知机制:客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除 实现对应接口时,调用对应的函数实现--回调)时,zookeeper会通知客户端。(zklient订阅 指定路径的监听接口)

5.Zookeeper做了什么?:1.命名服务   2.配置管理   3.集群管理(服务发现和master选举)   4.分布式锁 (独占锁和保持时序) 5.队列管理 6 负载均衡

6.Zookeeper命名服务:在zookeeper的文件系统里创建一个目录,即有唯一的path。在我们使用tborg无法确定上游程序的部署机器时即可与下游程序约定好path,通过path即能互相探索发现。(临时节点顺序编号)

7.Zookeeper的配置管理:程序总是需要配置的,如果程序分散部署在多台机器上,要逐个改变配置就变得困难。现在把这些配置全部放到zookeeper上去,保存在 Zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中就好。(监听机制 -- 回调实现)


8.Zookeeper集群管理:所谓集群管理无在乎两点:是否有机器退出和加入(服务发现)、选举master。

对于第一点,所有机器约定在父目录GroupMembers下创建临时目录节点,然后监听父目录节点的子节点变化消息。一旦有机器挂掉,该机器与 zookeeper的连接断开,其所创建的临时目录节点被删除,所有其他机器都收到通知:某个兄弟目录被删除,于是,所有人都知道:它上船了。(临时节点和监听机制)

新机器加入也是类似,所有机器收到通知:新兄弟目录加入,highcount又有了,对于第二点,我们稍微改变一下,所有机器创建临时顺序编号目录节点,每次选取编号最小的机器作为master就好。


9.Zookeeper分布式锁

有了zookeeper的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一个是保持独占(监听机制),另一个是控制时序(顺序编号和监听机制)。

对于第一类,我们将zookeeper上的一个znode看作是一把锁,通过createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的distribute_lock 节点就释放出锁。

对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便。


10.Zookeeper队列管理

两种类型的队列:

1、同步队列,当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。

2、队列按照 FIFO 方式进行入队和出队操作。

第一类,在约定目录下创建临时目录节点,监听节点数目是否是我们要求的数目。

第二类,和分布式锁服务中的控制时序场景基本原理一致,入列有编号,出列按编号。

11.分布式与数据复制

Zookeeper作为一个集群提供一致的数据服务,自然,它要在所有机器间做数据复制。数据复制的好处:

1、容错:一个节点出错,不致于让整个系统停止工作,别的节点可以接管它的工作;

2、提高系统的扩展能力 :把负载分布到多个节点上,或者增加节点来提高系统的负载能力;

3、提高性能:让客户端本地访问就近的节点,提高用户访问速度。

从客户端读写访问的透明度来看,数据复制集群系统分下面两种:

1、写主(WriteMaster) :对数据的修改提交给指定的节点。读无此限制,可以读取任何一个节点。这种情况下客户端需要对读与写进行区别,俗称读写分离

2、写任意(Write Any):对数据的修改可提交给任意的节点,跟读一样。这种情况下,客户端对集群节点的角色与变化透明。

对zookeeper来说,它采用的方式是写任意。通过增加机器,它的读吞吐能力和响应能力扩展性非常好,而写,随着机器的增多吞吐能力肯定下降(这也是它建立observer的原因),而响应能力则取决于具体实现方式,是延迟复制保持最终一致性,还是立即复制快速响应。

12.Zookeeper角色描述

领导者(Leader):领导者负责进行投票的发起和决议,更新系统状态。

学习者(Learner):

跟随者(Follower):follower用于接收客户请求并向客户端返回结果,在选主过程中参与投票。

观察者(observer):observer可以接收客户端连接,将写请求转发给leader节点。但observer不参加投票过程,只同步leader的状态。observer的目的是为了扩展系统,提高读取速度。

客户端(client):请求发起方

13.Zookeeper与客户端


14.Zookeeper设计目的

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

2.可靠性:具有简单、健壮、良好的性能,如果消息被到一台服务器接受,那么它将被所有的服务器接受。

3.实时性:Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。但由于网络延时等原因,Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。

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

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

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

15.Zookeeper工作原理

Zookeeper 的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和 leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。

为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。

16.Zookeeper 下 Server工作状态

每个Server在工作过程中有三种状态:LOOKING:当前Server不知道leader是谁,正在搜寻;LEADING:当前Server即为选举出来的leader;FOLLOWING:leader已经选举出来,当前Server与之同步

17.Zookeeper选主流程(basic paxos)(没明白!!!)

当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的Server都恢复到一个正确的状态。Zk的选举算法有两种:一种是基于basic paxos实现的,另外一种是基于fast paxos算法实现的。系统默认的选举算法为fast paxos。

1.选举线程由当前Server发起选举的线程担任,其主要功能是对投票结果进行统计,并选出推荐的Server;

2.选举线程首先向所有Server发起一次询问(包括自己);

3.选举线程收到回复后,验证是否是自己发起的询问(验证zxid是否一致),然后获取对方的id(myid),并存储到当前询问对象列表中,最后获取对方提议的leader相关信息(id,zxid),并将这些信息存储到当次选举的投票记录表中;

4.收到所有Server回复以后,就计算出zxid最大的那个Server(?? zxid最大意味着什么?),并将这个Server相关信息设置成下一次要投票的Server;

5.线程将当前zxid最大的Server设置为当前Server要推荐的Leader,如果此时获胜的Server获得n/2 + 1的Server票数,设置当前推荐的leader为获胜的Server,将根据获胜的Server相关信息设置自己的状态,否则,继续这个过程,直到leader被选举出来。 通过流程分析我们可以得出:要使Leader获得多数Server的支持,则Server总数必须是奇数2n+1,且存活的Server的数目不得少于n+1. 每个Server启动后都会重复以上流程。在恢复模式下,如果是刚从崩溃状态恢复的或者刚启动的server还会从磁盘快照中恢复数据和会话信息,zk会记录事务日志并定期进行快照,方便在恢复时进行状态恢复。选主的具体流程图所示:

18.Zookeeper选主流程(fast paxos)(没明白!!!)

fast paxos流程是在选举过程中,某Server首先向所有Server提议自己要成为leader,当其它Server收到提议以后,解决epoch和 zxid的冲突,并接受对方的提议,然后向对方发送接受提议完成的消息,重复这个流程,最后一定能选举出Leader。

19.Zookeeper同步流程(没明白?)

数据同步是指:每台Cilent机器都连接一个Follower或leader。当Cilent上数据被修改后,其连接的Follower或leader上的Server会首先接收到,而leader会随时检测所有与的Follower上的Server如果有数据变化就将数据同步到其他Follower上,

hadoop2.0中使用zookeeper确保整个NameService中只有一个活跃的NameNode(会有多个代用的NameNode但处于代用状态),当一个nameNode 当掉或会启用其他的备用的NameNode。此外在Hbase集群中也会使用zookeeper来保证Hbase集群中只有一个HMaster,同时通过zookeeper将Hbase客户端、RegionServer、Master三者联系在一起。

选完Leader以后,zk就进入状态同步过程。

1. Leader等待server连接;

2 .Follower连接leader,将最大的zxid发送给leader;(最大的zxid意味着什么?)

3 .Leader根据follower的zxid确定同步点;(同步点意味着什么?)

4 .完成同步后通知follower 已经成为uptodate状态;(到底是谁完成同步?)

5 .Follower收到uptodate消息后,又可以重新接受client的请求进行服务了。


20.Zookeeper工作流程-Leader --

1 .恢复数据;(恢复什么数据?怎么恢复?)

2 .维持与Learner的心跳(ping消息),接收Learner请求(写请求和同步请求  )并判断Learner的请求消息类型;

3 .Learner的消息类型主要有PING消息、REQUEST消息、ACK消息、REVALIDATE消息,根据不同的消息类型,进行不同的处理。

PING 消息是指Learner的心跳信息;

REQUEST消息是Follower发送的提议信息,包括写请求及同步请求

ACK消息是 Follower的对提议的回复,超过半数的Follower通过,则commit该提议;

REVALIDATE消息是用来延长SESSION有效时间。


21.Zookeeper工作流程-Follower

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、Zookeeper 的读写机制

Zookeeper是一个由多个server组成的集群;一个leader,多个follower;每个server保存一份数据副本;全局数据一致;分布式读写;更新请求转发,由leader实施

3、Zookeeper 的保证

 更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行(ZXID保证);数据更新原子性,一次数据更新要么成功,要么失败;全局唯一数据视图,client无论连接到哪个server,数据视图都是一致的(全局一致性);实时性,在一定事件范围内,client能读到最新数据

4、Zookeeper节点写数据操作流程

注:1.在Client向Follwer发出一个写的请求 -> 2.Follwer把请求发送给Leader -> 3.Leader接收到以后开始发起投票并通知Follwer进行投票 -> 4.Follwer把投票结果发送给Leader -> 5.Leader将结果汇总后如果需要写入,则开始写入同时把写入操作通知给Leader,然后commit; -> 6.Follwer把请求结果返回给Client

5、Zookeeper leader 选举

• 半数通过

– 3台机器 挂一台 2>3/2

– 4台机器 挂2台 2!>4/2

• A提案说,我要选自己,B你同意吗?C你同意吗?B说,我同意选A;C说,我同意选A。(注意,这里超过半数了,其实在现实世界选举已经成功了。

但是计算机世界是很严格,另外要理解算法,要继续模拟下去。)

• 接着B提案说,我要选自己,A你同意吗;A说,我已经超半数同意当选,你的提案无效;C说,A已经超半数同意当选,B提案无效。

• 接着C提案说,我要选自己,A你同意吗;A说,我已经超半数同意当选,你的提案无效;B说,A已经超半数同意当选,C的提案无效。

• 选举已经产生了Leader,后面的都是follower,只能服从Leader的命令。而且这里还有个小细节,就是其实谁先启动谁当头。

6、zxid

• znode节点的状态信息中包含czxid, 那么什么是zxid呢?

ZooKeeper状态的每一次改变, 都对应着一个递增的Transaction id, 该id称为zxid. 由于zxid的递增性质, 如果zxid1小于zxid2, 那么zxid1肯定先于zxid2发生.

创建任意节点, 或者更新任意节点的数据, 或者删除任意节点, 都会导致Zookeeper状态发生改变, 从而导致zxid的值增加.

7、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位是个递增计数。

» 当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader让所有的server都恢复到一个正确的状态

» 每个Server启动以后都询问其它的Server它要投票给谁。对于其他server的询问,server每次根据自己的状态都回复自己推荐的leader的id和上一次处理事务的zxid(系统启动时每个server都会推荐自己)收到所有Server回复以后,就计算出zxid最大的哪个Server,并将这个Server相关信息设置成下一次要投票的Server。计算这过程中获得票数最多的的sever为获胜者,如果获胜者的票数超过半数,则改server被选为leader。否则,继续这个过程,直到leader被选举出来。

» leader就会开始等待其他server连接;Follower连接leader,将最大的zxid发送给leader;Leader根据follower的zxid确定同步点(同步点在哪?);完成同步后通知follower 已经成为uptodate状态;Follower收到uptodate消息后,又可以重新接受client的请求进行服务了

8、数据一致性与paxos 算法

• 据说Paxos算法的难理解与算法的知名度一样令人敬仰,所以我们先看如何保持数据的一致性,这里有个原则就是:

• 在一个分布式数据库系统中,如果各节点的初始状态一致,每个节点都执行相同的操作序列,那么他们最后能得到一个一致的状态。

• Paxos算法解决的什么问题呢,解决的就是保证每个节点执行相同的操作序列。好吧,这还不简单,master维护一个全局写队列,所有写操作都必须 放入这个队列编号,那么无论我们写多少个节点,只要写操作是按编号来的,就能保证一致性。没错,就是这样,可是如果master挂了呢

Paxos算法通过投票来对写操作进行全局编号,同一时刻,只有一个写操作被批准,同时并发的写操作要去争取选票,只有获得过半数选票的写操作才会被 批准(所以永远只会有一个写操作得到批准),其他的写操作竞争失败只好再发起一轮投票,就这样,在日复一日年复一年的投票中,所有写操作都被严格编号排 序。编号严格递增,当一个节点接受了一个编号为100的写操作,之后又接受到编号为99的写操作(因为网络延迟等很多不可预见原因),它马上能意识到自己 数据不一致了,自动停止对外服务并重启同步过程。任何一个节点挂掉都不会影响整个集群的数据一致性(总2n+1台,除非挂掉大于n台)。

总结:• Zookeeper 作为 Hadoop 项目中的一个子项目,是 Hadoop 集群管理的一个必不可少的模块,它主要用来控制集群中的数据,如它管理 Hadoop 集群中的 NameNode,还有 Hbase 中 Master Election、Server 之间状态同步等。\

9、Observer

 Zookeeper需保证高可用和强一致性; 为了支持更多的客户端,需要增加更多Server; Server增多,投票阶段延迟增大,影响性能; 权衡伸缩性和高吞吐率,引入Observer; Observer不参与投票Observers接受客户端的连接,并将写请求转发给leader节点; 加入更多Observer节点,提高伸缩性,同时不影响吞吐率

10、 为什么zookeeper集群的数目,一般为奇数个?

•Leader选举算法采用了Paxos协议;

Paxos核心思想:当多数Server写成功,则任务数据写成功如果有3个Server,则两个写成功即可;如果有4或5个Server,则三个写成功即可。•Server数目一般为奇数(3、5、7)如果有3个Server,则最多允许1个Server挂掉;如果有4个Server,则同样最多允许1个Server挂掉由此,我们看出3台服务器和4台服务器的的容灾能力是一样的,所以为了节省服务器资源,一般我们采用奇数个数,作为服务器部署个数。

11、Zookeeper 的数据模型

» 层次化的目录结构,命名符合常规文件系统规范 » 每个节点在zookeeper中叫做znode,并且其有一个唯一的路径标识 » 节点Znode可以包含数据和子节点,但是EPHEMERAL类型的节点不能有子节点 » Znode中的数据可以有多个版本,比如某一个路径下存有多个数据版本,那么查询这个路径下的数据就需要带上版本 » 客户端应用可以在节点上设置监视器 » 节点不支持部分读写,而是一次性完整读写

12、Zookeeper 的节点

» Znode有两种类型,短暂的(ephemeral)和持久的(persistent)

» Znode的类型在创建时确定并且之后不能再修改

» 短暂znode的客户端会话结束时,zookeeper会将该短暂znode删除,短暂znode不可以有子节点

» 持久znode不依赖于客户端会话,只有当客户端明确要删除该持久znode时才会被删除

» Znode有四种形式的目录节点

» PERSISTENT(持久的)

» EPHEMERAL(暂时的)

» PERSISTENT_SEQUENTIAL(持久化顺序编号目录节点)

» EPHEMERAL_SEQUENTIAL(暂时化顺序编号目录节点)



Zookeeper 监视(Watches) 简介

Zookeeper C API 的声明和描述在 include/zookeeper.h 中可以找到,另外大部分的 Zookeeper C API 常量、结构体声明也在 zookeeper.h 中,如果如果你在使用 C API 是遇到不明白的地方,最好看看 zookeeper.h,或者自己使用 doxygen 生成 Zookeeper C API 的帮助文档。

Zookeeper 中最有特色且最不容易理解的是监视(Watches)。Zookeeper 所有的读操作——getData(),getChildren(), 和exists()都 可以设置监视(watch),监视事件可以理解为一次性的触发器, 官方定义如下: a watch event is one-time trigger, sent to the client that set the watch, which occurs when the data for which the watch was set changes。对此需要作出如下理解:

(一次性触发)One-time trigger

当设置监视的数据发生改变时,该监视事件会被发送到客户端,例如,如果客户端调用了 getData("/znode1", true) 并且稍后 /znode1 节点上的数据发生了改变或者被删除了,客户端将会获取到/znode1 发生变化的监视事件,而如果 /znode1 再一次发生了变化,除非客户端再次对 /znode1设置监视,否则客户端不会收到事件通知。

(发送至客户端)Sent to the client

Zookeeper 客户端和服务端是通过 socket 进行通信的,由于网络存在故障,所以监视事件很有可能不会成功地到达客户端,监视事件是异步发送至监视者的,Zookeeper 本身提供了保序性(ordering guarantee):即客户端只有首先看到了监视事件后,才会感知到它所设置监视的 znode 发生了变化(a client will never see a change for which it has set a watch until it first sees the watch event). 网络延迟或者其他因素可能导致不同的客户端在不同的时刻感知某一监视事件,但是不同的客户端所看到的一切具有一致的顺序。

(被设置 watch 的数据)The data for which the watch was set

这意味着znode 节点本身具有不同的改变方式。你也可以想象 Zookeeper 维护了两条监视链表:数据监视和子节点监视(data watches and child watches) getData() and exists() 设置数据监视,getChildren() 设置子节点监视。或者,你也可以想象 Zookeeper 设置的不同监视返回不同的数据,getData() 和 exists() 返回 znode节点的相关信息,而 getChildren() 返回子节点列表。因此, setData()会触发设置在某一节点上所设置的数据监视(假定数据设置成功),而一次成功的 create()操作则会出发当前节点上所设置的数据监视以及父节点的子节点监视。一次成功的 delete()操作将会触发当前节点的数据监视和子节点监视事件,同时也会触发该节点父节点的child watch。

Zookeeper中的监视是轻量级的,因此容易设置、维护和分发。当客户端与 Zookeeper服务器端失去联系时,客户端并不会收到监视事件的通知,只有当客户端重新连接后,若在必要的情况下,以前注册的监视会重新被注册并触发,对于开发人员来说这通常是透明的。只有一种情况会导致监视事件的丢失,即:通过 exists() 设置了某个 znode 节点的监视,但是如果某个客户端在此znode 节点被创建和删除的时间间隔内与 zookeeper 服务器失去了联系,该客户端即使稍后重新连接zookeeper服务器后也得不到事件通知。

Watch事件类型:

ZOO_CREATED_EVENT:节点创建事件,需要watch一个不存在的节点,当节点被创建时触发,此watch通过zoo_exists()设置

ZOO_DELETED_EVENT:节点删除事件,此watch通过zoo_exists()或zoo_get()设置

ZOO_CHANGED_EVENT:节点数据改变事件,此watch通过zoo_exists()或zoo_get()设置

ZOO_CHILD_EVENT:子节点列表改变事件,此watch通过zoo_get_children()或zoo_get_children2()设置

ZOO_SESSION_EVENT:会话失效事件,客户端与服务端断开或重连时触发

ZOO_NOTWATCHING_EVENT:watch移除事件,服务端出于某些原因不再为客户端watch节点时触发

推荐阅读更多精彩内容