hadoop生态圈面试精华之zookeeper(一)
Zookeeper面试题
介绍下Zookeeper是什么?
可回答:谈谈你对Zookeeper的理解
问过的一些公司:京东x2,字节,美团x2,蘑菇街,映客直播参考答案:
Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目。
Zookeeper从设计模式角度来理解,是一个基于观察者模式设计的分布式服务管理框架,它负责存储和 管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生了变化,Zookeeper就负责 通知已经在Zookeeper上注册的那些观察者做出相应的反应。
Zookeeper提供的服务包括:统一命名服务、统一配置管理、统一集群管理、服务器节点动态上下线、 软负载均衡等。它致力于为那些高吞吐的大型分布式系统提供一个高性能、高可用、且具有严格顺序访 问控制能力的分布式协调服务。
特性
Zookeeper有什么作用?优缺点?有什么应用场景?
可回答:你觉得Zookeeper比较重要的功能
问过的一些公司:阿里x3,京东x3,阿里,字节,字节(2021.10),美团x3,蘑菇街x2,深信服,头条,
vivo,ebay
参考答案:
作用
Zookeeper作用包括存储数据(文件系统)和监听(监听通知机制)
优点
7. 分布式协调过程简单
8. 同步:zk高度同步,这意味着服务器进程之间既存在互斥又存在合作,同步有助于Apache HBase进行配置管理。
9. 有序消息:zk跟踪一个数字,表示每个更新的顺序,保证消息有序
10. 序列化:根据具体规则,zk对数据进行编码。 另外,它还可确保我们的应用程序始终如一地运行。但是,在MapReduce中,我们使用此方法(序列化)来协调队列以执行正在运行的线程
11. 速度:在读请求多的情况下,能以很快的速度运行
12. 可扩展性:此外,可以通过部署更多机器来加强zk的性能
13. 有序性有何优势?:众所周知,zk中的消息是有序的。所以,为了实现更高级别的抽象,需要有序 性。 这就是有序性对我们有利的方式
14. 快:在读多的情况下,zk会非常快
15. 可靠性:zk非常可靠,因为一旦zk更新了,更新后的数据会一直保持,直到被覆盖更新
16. 原子性:zk只有两种情况,要么全部成功,要么全部失败,没有中间状态的情况
17. 实时性:zk保证在一定时间段内,客户端最终一定能从服务器上读到最新的数据状态
缺点
18. 增加新的zk服务器时可能导致数据丢失
在现有服务器中,当新zk服务器数量超过zk服务中已存在的数量时数据会丢失。 同时,向zk服务发出
Start命令,新服务器可能形成仲裁
19. 不能迁移
在没有用户干预的情况下,zk服务器无法从版本3.4迁移到3.3,然后再迁移到3.4。
20. 节点数(其实集群中只要存活数过半即对外可用,但是会有脑裂情况发生)
要求3或5个这样奇数个zk节点(要求奇数是为了保证选举的正常进行因为leader选举要求 可用节点数量
总节点数/2,防止脑裂造成集群不可用。同时在容错能力相同的情况下,奇数个节点更节省资源)
Zookeeper的选举策略,leader和follower的区别?
可回答:1)Zookeeper的选举过程(选举机制);2)leader的选举是如何实现的;3)说说Zookeeper的 启动过程,比如现在有五台机器,ABCDE依次启动起来,那么哪台是leader?4)Zookeeper的选主策略 了解过吗
问过的一些公司:阿里,字节,字节(2021.10),去哪儿,网易,贝壳,京东,蘑菇屋,端点数据
(2021.07),华为精英计划(2021.07),大华(2021.08)
参考答案:
半数机制:集群中半数以上机器存活,集群可用。所以Zookeeper适合安装奇数台服务器。
Zookeeper虽然在配置文件中并没有指定Master和Slave。但是,Zookeeper工作时,是有一个节点为Leader,其他则为Follower,Leader是通过内部的选举机制临时产生的。
以一个简单的例子来说明整个选举的过程
假设有五台服务器组成的Zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史 数据,在存放数据量这一点上,都是一样的。假设这些服务器依序启动,来看看会发生什么。
32. 服务器1启动,发起一次选举。服务器1投自己一票。此时服务器1票数一票,不够半数以上(3
票),选举无法完成,服务器1状态保持为LOOKING;
33. 服务器2启动,再发起一次选举。服务器1和2分别投自己一票并交换选票信息:此时服务器1发现服 务器2的ID比自己目前投票推举的(服务器1)大,更改选票为推举服务器2。此时服务器1票数0票,服 务器2票数2票,没有半数以上结果,选举无法完成,服务器1,2状态保持LOOKING
34. 服务器3启动,发起一次选举。此时服务器1和2都会更改选票为服务器3。此次投票结果:服务器1为
0票,服务器2为0票,服务器3为3票。此时服务器3的票数已经超过半数,服务器3当选Leader。服务器
1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;
35. 服务器4启动,发起一次选举。此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息。交 换选票信息结果:服务器3为3票,服务器4为1票。此时服务器4服从多数,更改选票信息为服务器3,并 更改状态为FOLLOWING;
36. 服务器5启动,同4一样当小弟。
领导者Leader
Leader在集群中只有一个节点,可以说是老大,是zookeeper集群的中心,负责协调集群中的其他节点。 从性能的角度考虑,leader可以选择不接受客户端的连接。
主要作用
37. 发起与提交写请求。
所有的跟随者Follower与观察者Observer节点的写请求都会转交给领导者Leader执行。Leader接受到一 个写请求后,首先会发送给所有的Follower,统计Follower写入成功的数量。当有超过半数的Follower写 入成功后,Leader就会认为这个写请求提交成功,通知所有的Follower commit这个写操作,保证事后哪怕是集群崩溃恢复或者重启,这个写操作也不会丢失。
38. 与Learner(Follower与Observer)保持心跳 3)崩溃恢复时负责恢复数据以及同步数据到Learner 跟随者Follower
Follow在集群中有多个
主要作用
39. 与老大Leader保持心跳连接
40. 当Leader挂了的时候,经过投票后成为新的leader。leader的重新选举是由老二Follower们内部投票决 定的。
41. 向leader发送消息与请求 4)处理leader发来的消息与请求
介绍下Zookeeper选举算法
问过的一些公司:海康(2021.08) 参考答案:
在ZooKeeper中,提供了三种Leader选举的算法,分别是
42. LeaderElection
43. UDP版本的FastLeaderElection 3)TCP版本的FastLeaderElection
可以通过在配置文件zoo.cfg中使用electionAlg属性来指定,分别使用数字0~3来表示。0代表LeaderElection,这是一种纯UDP实现的Leader选举算法:1代表UDP版本的FastLeaderElection,并且是非 授权模式;2也代表UDP版本的FastLeaderElection,但使用授权模式;3代表TCP版本的FastLeaderElection。不过从3.4.0版本开始,ZooKeeper废弃了0、1、2这三种Leader选举算法,只保留了 TCP版本的FastLeaderElection选举算法。
1、Leader选举
Leader选举是保证分布式数据一致性的关键所在。当Zookeeper集群中的一台服务器出现以下两种情况 之一时,需要进入Leader选举。
44. 服务器初始化启动。(集群的每个节点都没有数据 → 以SID的大小为准)
45. 服务器运行期间无法和Leader保持连接。(集群的每个节点都有数据 ,或者Leader 宕机→ 以ZXID 和
SID 的最大值为准)
47.1 服务器启动时期的Leader选举
若进行Leader选举,则至少需要2台机器,两台的高可用性会差一些,如果Leader 宕机,就剩下一台, 自己没办法选举。一般集群也是3台机器,这里选取3台机器组成的服务器集群为例。
在集群初始化阶段,当有一台服务器Server1启动时,其单独无法进行和完成Leader选举,当第二台服务 器Server2启动时,此时两台机器可以相互通信,每台机器都试图找到Leader,于是进入Leader选举过程。选举过程如下
46. 每个Server发出一个投票。由于是初始情况,Server1和Server2都会将自己作为Leader服务器来进行 投票,每次投票会包含所推举的服务器的myid和ZXID,使用(myid, ZXID)来表示,此时Server1的投票为(1, 0),Server2的投票为(2, 0),然后各自将这个投票发给集群中其他机器。
47. 接受来自各个服务器的投票。集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是 否是本轮投票、是否来自LOOKING状态的服务器。
48. 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下:
优先检查ZXID。ZXID比较大的服务器优先作为Leader。(这个很重要:是数据最新原则,保证数 据的完整性)
如果ZXID相同,那么就比较myid。myid较大的服务器作为Leader服务器。(集群的节点标识)
对于Server1而言,它的投票是(1, 0),接收Server2的投票为(2, 0),首先会比较两者的ZXID,均为0。再比较myid,此时Server2的myid最大,于是更新自己的投票为(2, 0),然后重新投票,对于Server2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。
49. 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信 息,对于Server1、Server2而言,都统计出集群中已经有两台机器接受了(2, 0)的投票信息,此时便认为已经选出了Leader。
50. 改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变 更为FOLLOWING,如果是Leader,就变更为LEADING。
47.2 服务器运行时期的Leader选举
在Zookeeper运行期间,Leader与非Leader服务器各司其职,即便当有非Leader服务器宕机或新加入,此 时也不会影响Leader,但是一旦Leader服务器挂了,那么整个集群将暂停对外服务,进入新一轮Leader 选举,其过程和启动时期的Leader选举过程基本一致。
假设正在运行的有Server1、Server2、Server3三台服务器,当前Leader是Server2,若某一时刻Leader挂 了,此时便开始Leader选举。
选举过程如下:
52. 变更状态。Leader挂后,余下的非Observer服务器都会讲自己的服务器状态变更为LOOKING,然后开 始进入Leader选举过程。
53. 每个Server会发出一个投票。在运行期间,每个服务器上的ZXID可能不同,此时假定Server1的ZXID 为123,Server3的ZXID为122;在第一轮投票中,Server1和Server3都会投自己,产生投票(1, 123),(3, 122),然后各自将投票发送给集群中所有机器。
54. 接收来自各个服务器的投票。与启动时过程相同。
55. 处理投票。与启动时过程相同,此时,Server1将会成为Leader。
56. 统计投票。与启动时过程相同。
57. 改变服务器的状态。与启动时过程相同。
2、Leader选举算法分析
最开始也提过,在3.4.0后的Zookeeper的版本只保留了TCP版本的FastLeaderElection选举算法。当一台机 器进入Leader选举时,当前集群可能会处于以下两种状态
集群中已经存在Leader。集群中不存在Leader。
对于集群中已经存在Leader而言,此种情况一般都是某台机器启动得较晚,在其启动之前,集群已经在 正常工作,对这种情况,该机器试图去选举Leader时,会被告知当前服务器的Leader信息,对于该机器 而言,仅仅需要和Leader机器建立起连接,并进行状态同步即可。
而在集群中不存在Leader情况下则会相对复杂,其步骤如下
58. 第一次投票。无论哪种导致进行Leader选举,集群的所有机器都处于试图选举出一个Leader的状态,即LOOKING状态,LOOKING机器会向所有其他机器发送消息,该消息称为投票。投票中包含了SID(服务器的唯一标识)和ZXID(事务ID),(SID, ZXID)形式来标识一次投票信息。假定Zookeeper由5
台机器组成,SID分别为1、2、3、4、5,ZXID分别为9、9、9、8、8,并且此时SID为2的机器是Leader机 器,某一时刻,1、2所在机器出现故障,因此集群开始进行Leader选举。在第一次投票时,每台机器都 会将自己作为投票对象,于是SID为3、4、5的机器投票情况分别为(3, 9),(4, 8), (5, 8)。
59. 变更投票。每台机器发出投票后,也会收到其他机器的投票,每台机器会根据一定规则来处理收到 的其他机器的投票,并以此来决定是否需要变更自己的投票,这个规则也是整个Leader选举算法的核心 所在,其中术语描述如下
vote_sid:接收到的投票中所推举Leader服务器的SID。vote_zxid:接收到的投票中所推举Leader服务器的ZXID。self_sid: 当 前 服 务 器 自 己 的 SID 。 self_zxid:当前服务器自己的ZXID。
每次对收到的投票的处理,都是对(vote_sid, vote_zxid)和(self_sid, self_zxid)对比的过程。规则一:如果vote_zxid大于self_zxid,就认可当前收到的投票,并再次将该投票发送出去。 规则二:如果vote_zxid小于self_zxid,那么坚持自己的投票,不做任何变更。
规则三:如果vote_zxid等于self_zxid,那么就对比两者的SID,如果vote_sid大于self_sid,那么就认可当 前收到的投票,并再次将该投票发送出去。
规则四:如果vote_zxid等于self_zxid,并且vote_sid小于self_sid,那么坚持自己的投票,不做任何变 更。
结合上面规则,给出下面的集群变更过程。
60. 确定Leader。经过第二轮投票后,集群中的每台机器都会再次接收到其他机器的投票,然后开始统 计投票,如果一台机器收到了超过半数的相同投票,那么这个投票对应的SID机器即为Leader。此时Server3将成为Leader。
由上面规则可知,通常那台服务器上的数据越新(ZXID会越大),其成为Leader的可能性越大,也就越 能够保证数据的恢复。如果ZXID相同,则SID越大机会越大。
3、Leader选举实现细节
a. 服务器状态
服务器具有四种状态,分别是LOOKING、FOLLOWING、LEADING、OBSERVING。
LOOKING:寻找Leader状态。当服务器处于该状态时,它会认为当前集群中没有Leader,因此需要进入
Leader选举状态。
FOLLOWING:跟随者状态。表明当前服务器角色是Follower。LEADING:领导者状态。表明当前服务器角色是Leader。OBSERVING:观察者状态。表明当前服务器角色是Observer。
47.3 投票数据结构
每个投票中包含了两个最基本的信息,所推举服务器的SID和ZXID,投票(Vote)在Zookeeper中包含字 段如下
id:被推举的Leader的SID。
zxid:被推举的Leader事务ID。
electionEpoch:逻辑时钟,用来判断多个投票是否在同一轮选举周期中,该值在服务端是一个自增序 列,每次进入新一轮的投票后,都会对该值进行加1操作。
peerEpoch:被推举的Leader的epoch。
state:当前服务器的状态。
Zookeeper的节点类型有哪些?分别作用是什么?
可回答:说下Zookeeper的临时节点和永久节点问过的一些公司:字节,eBay,快手,蘑菇街 参考答案:
Znode有两种类型:
短暂(ephemeral):客户端和服务器端断开连接后,创建的节点自己删除
持久(persistent):客户端和服务器端断开连接后,创建的节点不删除Znode 有 四 种 形 式 的 目 录 节 点 ( 默 认 是 persistent ) 1)持久化目录节点(PERSISTENT)
客户端与zookeeper断开连接后,该节点依旧存在
Zookeeper的节点数怎么设置比较好?
可回答:Zookeeper集群一般设置多少个节点,允许几个节点挂问过的一些公司:bigo,华为精英计划(2021.07)
参考答案:
2n+1,半数机制。一般是3个或者3个以上节点。
Zookeeper架构
问过的一些公司:字节参考答案:
Zookeeper本身可以是单机模式,也可以是集群模式,为了Zookeeper本身不出现单点故障,通常情况下 使用集群模式,而且是master/slave模式的集群
Zookeeper集群角色
Leader(领导者)
Leader不直接接收client的请求,但接受由其他Follower和Observer转发过来的client请求。此外,Leader
还负责投票的发起和决议,即时更新状态和数据。
Follower(跟随者)
Follower角色接手客户端请求并返回结果,参与Leader发起的投票和选举,但不具有写操作的权限。
Observer(观察者)
Observer角色接受客户端连接,将写操作转给Leader,但Observer不参与投票(即不参加一致性协议的 达成),只同步Leader节点的状态,Observer角色是为集群系统扩展而生的。
Clinet(客户端)
连接Zookeeper集群的使用者,请求的发起者,独立于Zookeeper集群的角色。
Zookeeper的功能有哪些
可回答:Zookeeper的作用
问过的一些公司:字节,阿里,美团x2,蘑菇街
Zookeeper作用包括存储数据(文件系统)和监听(监听通知机制)
Zookeeper的数据结构(树)?基于它实现的分布式锁?基于它实现的Master选举?基于它的集群管理?Zookeeper的注册(watch)机制使用场景?
问过的一些公司:阿里,ebay 参考答案:
1、数据结构
Zookeeper数据模型的结构与Unix文件系统很类似,整体上可以看作一棵树,每个节点称作一个ZNode。 每一个ZNode默认能够存储1MB的数据,每个ZNode都可以通过其路径唯一标识。
ZNode有两种类型
短暂(ephemeral):客户端和服务器端断开连接后,创建的节点自己删除 持久(persistent):客户端和服务器端断开连接后,创建的节点不删除
ZNode有四种形式的目录节点(默认是PERSISTENT)
类型 描述
PERSISTENT 持久化节点
PERSISTENT_SEQUENTIAL 顺序自动编号持久化节点,这种节点会根据当前已存在的节点数自动+1
EPHEMERAL 临时节点,客户端session超时这类节点会被自动删除
EPHEMERAL_SEQUENTIAL 临时自动编号节点
2、分布式锁
a. 为什么需要分布式锁
锁是多线程代码中的概念,只有当多任务访问同一个互斥的共享资源时才需要。如下图:
在我们进行单机应用开发,涉及并发同步的时候,我们往往采用synchronized或者Lock的方式来解决多 线程间的代码同步问题,这时多线程的运行都是在同一个JVM之下。但当我们的应用是分布式集群工作 的情况下,属于多JVM下的工作环境,JVM之间已经无法通过多线程的锁解决同步问题。那么就需要一 种更加高级的锁机制,来处理种跨机器的进程之间的数据同步问题——这就是分布式锁。
如下例:携程、美团、飞猪、去哪儿四个购票网站实际上都没有最终售票权,只有12306铁道总局有火 车票,那么四个购票网站都需要购买火车票,那么四个网站必须排队进行同步,否则不同步会造成多售
(类似同一进程中多线程间不同步也会造成多售)。这时他们就需要有一个公共的锁管理方案,来保证
APP间的购票是同步的。要想购票:
基于Redis实现分布式锁
相比较于基于数据库实现分布式锁的方案来说,基于缓存来实现在性能方面会表现的更好一点,Redis就 是其中一种。由于Redis可以设置字段的有效期,因此可以实现自动释放超期的锁,不需要多个监视锁字 段进程进行锁守护,可以依旧存在上述mysql实现中除了3以外1、2、4中的问题。
基于Zookeeper实现分布式锁
相比于前面两种实现方式,基于zookeeper实现分布式锁的方案优于上述两种方案。由于zookeeper有以 下特点:
基于Zookeeper实现排他锁流程:
47.6 共享锁
共享锁,又称读锁。如果事务T1对数据对象O1加上了共享锁,那么当前事务只能对O1进行读取操作, 其他事务也只能对这个数据对象加共享锁,直到该数据对象上的所有共享锁都被释放。
共享锁与排他锁的区别在于,加了排他锁之后,数据对象只对当前事务可见,而加了共享锁之后,数据 对象对所有事务都可见。
定义锁:通过Zookeeper上的数据节点来表示一个锁,是一个类似于/lockpath/[hostname]-请求类型-
序号的临时顺序节点
获取锁:客户端通过调用 create 方法创建表示锁的临时顺序节点,如果是读请求,则创建
/lockpath/[hostname]-R-序号节点,如果是写请求则创建 /lockpath/[hostname]-W-序号节点
判断读写顺序:大概分为4个步骤
① 创建完节点后,获取 /lockpath 节点下的所有子节点,并对该节点注册子节点变更的Watcher监听
② 确定自己的节点序号在所有子节点中的顺序
③ 对于读请求:
如果没有比自己序号更小的子节点,或者比自己序号小的子节点都是读请求,那么表明自己已经成 功获取到了共享锁,同时开始执行读取逻辑
如果有比自己序号小的子节点有写请求,那么等待
对于写请求,如果自己不是序号最小的节点,那么等待
④ 接收到Watcher通知后,重复步骤①
释放锁:与排他锁逻辑一致
基于Zookeeper实现共享锁流程:
47.7 羊群效应
在实现共享锁的 “判断读写顺序” 的第1个步骤是:创建完节点后,获取 /lockpath 节点下的所有子节点,并对该节点注册子节点变更的Watcher监听。这样的话,任何一次客户端移除共享锁之后, Zookeeper将会发送子节点变更的Watcher通知给所有机器,系统中将有大量的 “Watcher通知” 和 “子节
点列表获取” 这个操作重复执行,然后所有节点再判断自己是否是序号最小的节点(写请求)或者判断比自己序号小的子节点是否都是读请求(读请求),从而继续等待下一次通知。
然而,这些重复操作很多都是 “无用的”,实际上每个锁竞争者只需要关注序号比自己小的那个节点是否存在即可。
当集群规模比较大时,这些 “无用的” 操作不仅会对Zookeeper造成巨大的性能影响和网络冲击,更为严重的是,如果同一时间有多个客户端释放了共享锁,Zookeeper服务器就会在短时间内向其余客户端发 送大量的事件通知,这就是所谓的 “羊群效应”。
改进后的分布式锁实现:
但这种方式会存在一个隐患,就是网络故障问题。看一下图2:
也就是说,我们的主节点并没有挂掉,只是在备用节点ping主节点,请求应答的时候发生网络故障,这 样我们的备用节点同样收不到应答,就会认为主节点挂掉,然后备机会启动自己的master实例。这样就 会导致系统中有两个主节点,也就是双master。出现双master以后,我们的从节点会将它做的事情一部 分汇报给主节点,一部分汇报给备用节点,这样服务就乱套了。为了防止这种情况出现,我们可以考虑 采用zookeeper,虽然它不能阻止网络故障的出现,但它能保证同一时刻系统中只存在一个主节点。我 们来看zookeeper是怎么实现的:在此处,抢主程序是包含在服务程序中,需要程序员来手动写抢主逻 辑 的 , 比 如 当 当 开 源 框 架 elastic-job 中 , 就 有 关 于 选 主 的 部 分 , 参 见 :elastic-job- core/main/java/com/dangdang/ddframe/job/internal/election文件夹下的选主代码。
Zookeeper自己在集群环境下的抢主算法有三种,可以通过配置文件来设定,默认采用FastLeaderElection,不作赘述;此处主要讨论集群环境中,应用程序利用master的特点,自己选主的过 程。程序自己选主,每个人都有自己的一套算法,有采用“最小编号”的,有采用类似“多数投票”的,各 有优劣,本文的算法仅作演示理解使用:
结构图如下:
左侧树状结构为zookeeper集群,右侧为程序服务器。所有的服务器在启动的时候,都会订阅zookeeper 中master节点的删除事件,以便在主服务器挂掉的时候进行抢主操作;所有服务器同时会在servers节点 下注册一个临时节点(保存自己的基本信息),以便于应用程序读取当前可用的服务器列表。
选主原理介绍:Zookeeper的节点有两种类型,持久节点跟临时节点。临时节点有个特性,就是如果注 册这个节点的机器失去连接(通常是宕机),那么这个节点会被zookeeper删除。选主过程就是利用这 个特性,在服务器启动的时候,去zookeeper特定的一个目录下注册一个临时节点(这个节点作为master,谁注册了这个节点谁就是master),注册的时候,如果发现该节点已经存在,则说明已经有别 的服务器注册了(也就是有别的服务器已经抢主成功),那么当前服务器只能放弃抢主,作为从机存 在。同时,抢主失败的当前服务器需要订阅该临时节点的删除事件,以便该节点删除时(也就是注册该 节点的服务器宕机了或者网络断了之类的)进行再次抢主操作。从机具体需要去哪里注册服务器列表的 临时节点,节点保存什么信息,根据具体的业务不同自行约定。选主的过程,其实就是简单的争抢在zookeeper注册临时节点的操作,谁注册了约定的临时节点,谁就是master。
5、Watch机制
a. 为什么添加Watch
ZooKeeper是用来协调(同步)分布式进程的服务,提供了一个简单高性能的协调内核,用户可以在此 之上构建更多复杂的分布式协调功能。
多个分布式进程通过ZooKeeper提供的API来操作共享的ZooKeeper内存数据对象ZNode来达成某种一致的 行为或结果,这种模式本质上是基于状态共享的并发模型,与Java的多线程并发模型一致,他们的线程 或进程都是”共享式内存通信“。Java没有直接提供某种响应式通知接口来监控某个对象状态的变化,只 能要么浪费CPU时间毫无响应式的轮询重试,或基于Java提供的某种主动通知(Notif)机制(内置队
列)来响应状态变化,但这种机制是需要循环阻塞调用。而ZooKeeper实现这些分布式进程的状态
(ZNode的Data、Children)共享时,基于性能的考虑采用了类似的异步非阻塞的主动通知模式即Watch 机制,使得分布式进程之间的“共享状态通信”更加实时高效,其实这也是ZooKeeper的主要任务决定的— 协调。Consul虽然也实现了Watch机制,但它是阻塞的长轮询。