ZooKeeper

一、初识Zookeeper

  ZooKeeper是一个典型的分布式数据一致性解决方案,分布式应用程序可以基于ZooKeeper实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。
  Zookeeper一个最常用的使用场景就是用于担任服务生产者和服务消费者的注册中心(提供发布订阅服务)。 服务生产者将自己提供的服务注册到Zookeeper中心,服务的消费者在进行服务调用的时候先到Zookeeper中查找服务,获取到服务生产者的详细信息之后,再去调用服务生产者的内容与数据。
  如下图,在Dubbo架构中Zookeeper就担任了注册中心这一角色。

  • ZooKeeper特点
      1、顺序一致性: 从同一客户端发起的事务请求,最终将会严格地按照顺序被应用到ZooKeeper中去。

  有序性是Zookeeper中非常重要的一个特性,所有的更新都是全局有序的,每个更新都有一个唯一的时间戳,这个时间戳称为zxid(Zookeeper Transaction Id)。而读请求只会相对于更新有序,也就是读请求的返回结果中会带有这个Zookeeper最新的 zxid。

    2、原子性: 所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的。也就是说,要么整个集群中所有的机器都成功应用了某一个事务,要么都没有应用。
    3、单一系统映像 : 无论客户端连到哪一个ZooKeeper服务器上,其看到的服务端数据模型都是一致的。
    4、可靠性: 一旦一次更改请求被应用,更改的结果就会被持久化,直到被下一次更改覆盖。

  • 为什么最好使用奇数台服务器构成ZooKeeper集群
      ZooKeeper容错:当宕掉几个ZooKeeper服务器之后,剩下的个数必须大于宕掉的个数的话整个ZooKeeper才依然可用。假如我们的集群中有n台zookeeper服务器,那么也就是剩下的服务数必须大于n/2。先说一下结论,2n和2n-1的容忍度是一样的,都是n-1。
      比如假如我们有3台,那么最大允许宕掉1台zookeeper服务器,如果我们有4台的的时候也同样只允许宕掉1台。假如我们有5台,那么最大允许宕掉2台zookeeper服务器,如果我们有6台的的时候也同样只允许宕掉2台。

1.1 ZooKeeper概念

  1、ZooKeeper本身就是一个分布式程序(只要半数以上节点存活,ZooKeeper就能正常服务)。
  2、为了保证高可用,最好是以集群形态来部署ZooKeeper,这样只要集群中大部分机器是可用的(能够容忍一定的机器故障),那么ZooKeeper本身仍然是可用的。
  3、ZooKeeper将数据保存在内存中,这也就保证了高吞吐量和低延迟(但是内存限制了能够存储的容量不太大,此限制也是保持ZNode中存储的数据量较小的进一步原因)。
  4、ZooKeeper是高性能的。 在“读”多于“写”的应用程序中尤其地高性能,因为“写”会导致所有的服务器间同步状态。
  5、ZooKeeper有临时节点的概念。 当创建临时节点的客户端会话一直保持活动,瞬时节点就一直存在。而当会话终结时,瞬时节点被删除。持久节点是指一旦这个ZNode被创建了,除非主动进行ZNode的移除操作,否则这个ZNode将一直保存在Zookeeper上。
  6、ZooKeeper底层其实只提供了两个功能:①管理(存储、读取)用户程序提交的数据;②为用户程序提供数据节点监听服务。

  • 1、会话(Session)
      Session指的是ZooKeeper服务器与客户端会话。在ZooKeeper中,一个客户端连接是指客户端和服务器之间的一个TCP长连接。客户端启动的时候,首先会与服务器建立一个TCP连接,从第一次连接建立开始,客户端会话的生命周期也开始了。通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够向Zookeeper服务器发送请求并接受响应,同时还能够通过该连接接收来自服务器的Watch事件通知。 Session的sessionTimeout值用来设置一个客户端会话的超时时间。
      当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在sessionTimeout规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。
      在为客户端创建会话之前,服务端首先会为每个客户端都分配一个sessionID。由于sessionID是Zookeeper会话的一个重要标识,许多与会话相关的运行机制都是基于这个sessionID的。因此,无论是哪台服务器为客户端分配的sessionID,都务必保证全局唯一。
  • 2、Znode
      在谈到分布式的时候,我们通常说的“节点"是指组成集群的每一台机器。然而,在Zookeeper中,“节点"分为两类,第一类同样是指构成集群的机器,我们称之为机器节点;第二类则是指数据模型中的数据单元,我们称之为数据节点一一ZNode。
      Zookeeper将所有数据存储在内存中,数据模型是一棵树(Znode Tree),由斜杠(/)的进行分割的路径,就是一个Znode,例如/foo/path1。每个Znode上都会保存自己的数据内容,同时还会保存一系列属性信息。
      在Zookeeper中,Znode可以分为持久节点和临时节点两类。所谓持久节点是指一旦这个ZNode被创建了,除非主动进行ZNode的移除操作,否则这个ZNode将一直保存在Zookeeper上。而临时节点就不一样了,它的生命周期和客户端会话绑定,一旦客户端会话失效,那么这个客户端创建的所有临时节点都会被移除。
      另外,ZooKeeper还允许用户为每个节点添加一个特殊的属性:SEQUENTIAL,一旦节点被标记上这个属性,那么在这个节点被创建的时候,Zookeeper会自动在其节点名后面追加上一个整型数字,这个整型数字是一个由父节点维护的自增数字。
      Znode四种形式的目录节点:
  1. PERSISTENT:持久的节点。
  2. EPHEMERAL:暂时的节点。
  3. PERSISTENT_SEQUENTIAL:持久化顺序编号目录节点。
  4. EPHEMERAL_SEQUENTIAL:暂时化顺序编号目录节点。
  • 3、版本
      Zookeeper的每个ZNode上都会存储数据,对应于每个ZNode,Zookeeper都会为其维护一个叫作Stat的数据结构,Stat中记录了这个ZNode的三个数据版本,分别是version(当前ZNode的版本)、cversion(当前ZNode子节点的版本)和aversion(当前ZNode的ACL版本)。
  • 4、Watcher
      Watcher(事件监听器),是Zookeeper中的一个很重要的特性。Zookeeper允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,ZooKeeper服务端会将事件通知到感兴趣的客户端上去,该机制是Zookeeper实现分布式协调服务的重要特性。
  • 5、ACL
      Zookeeper采用ACL(AccessControlLists)策略来进行权限控制,类似于 UNIX 文件系统的权限控制。Zookeeper 定义了如下5种权限:

CREATE:获取子节点的权限。
READ:获取节点数据和子节点列表的权限。
WRITE:更新节点数据的权限。
DELETE:删除子节点的权限。
ADMIN:设置节点ACL的权限。

  CREATE和DELETE这两种权限都是针对子节点的权限控制。

1.2 ZooKeeper设计目标

  • 1、简单的数据模型
      ZooKeeper允许分布式进程通过共享的层次结构命名空间进行相互协调,这与标准文件系统类似。ZooKeeper数据保存在内存中,这意味着ZooKeeper可以实现高吞吐量和低延迟。
  • 2、可构建集群
      为了保证高可用,最好是以集群形态来部署ZooKeeper,这样只要集群中大部分机器是可用的(能够容忍一定的机器故障),那么ZooKeeper本身仍然是可用的。 客户端在使用ZooKeeper时,需要知道集群机器列表,通过与集群中的某一台机器建立TCP连接来使用服务,客户端使用这个TCP链接来发送请求、获取结果、获取监听事件以及发送心跳包。如果这个连接异常断开了,客户端可以连接到另外的机器上。
      ZooKeeper官方提供的架构图:

      每一个Server代表一个安装Zookeeper服务的服务器。组成ZooKeeper服务的服务器都会在内存中维护当前的服务器状态,并且每台服务器之间都互相保持着通信。集群间通过Zab协议(Zookeeper Atomic Broadcast)来保持数据的一致性。
  • 3、顺序访问
      对于来自客户端的每个更新请求,ZooKeeper都会分配一个全局唯一的递增编号,这个编号反应了所有事务操作的先后顺序,应用程序可以使用ZooKeeper这个特性来实现更高层次的同步原语。 这个编号也叫做时间戳——zxid(Zookeeper Transaction Id)。
  • 4、高性能
      ZooKeeper是高性能的。 在“读”多于“写”的应用程序中尤其地高性能,因为“写”会导致所有的服务器间同步状态。

1.3 ZooKeeper集群角色

  最典型集群模式: Master/Slave模式(主备模式)。在这种模式中,通常 Master服务器作为主服务器提供写服务,其他的Slave服务器从服务器通过异步复制的方式获取Master服务器最新的数据提供读服务。
  在ZooKeeper中,没有选择传统的Master/Slave概念,而是引入了Leader、Follower和Observer三种角色。

  ZooKeeper集群中的所有机器,通过一个Leader选举过程来选定一台称为 “Leader” 的机器。Leader既可以为客户端提供写服务又能提供读服务。除了Leader外,Follower和Observer都只能提供读服务。Follower和Observer唯一的区别在于Observer机器不参与Leader的选举过程,也不参与写操作的“过半写成功”策略,因此Observer机器可以在不影响写性能的情况下提升集群的读性能。

  • 1、Leader
      1、一个Zookeeper集群同一时间只会有一个实际工作的Leader,它会发起并维护与各Follwer及Observer间的心跳。
      2、所有的写操作必须要通过Leader完成,再由Leader将写操作广播给其它服务器。只要有超过半数节点(不包括observer节点)写入成功,该写请求就会被提交。

  • 2、Follower
      1、一个Zookeeper集群可能同时存在多个Follower,它会响应Leader的心跳。
      2、Follower可直接处理并返回客户端的读请求,同时会将写请求转发给 Leader 处理。
      3、并且负责在Leader处理写请求时对请求进行投票。

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

      当Leader服务器出现网络中断、崩溃退出与重启等异常情况时,ZAB协议就会进人恢复模式并选举产生新的Leader服务器。

  • 投票机制
      每个sever首先给自己投票,然后用自己的选票和其他sever选票对比,权重大的胜出,使用权重较大的更新自身选票箱。具体选举过程:

  1. 每个Server启动以后,都询问其它的Server它要投票给谁。对于其他server的询问,Server每次根据自己的状态都回复自己推荐的Leader的id和上一次处理事务的zxid(系
    统启动时每个server都会推荐自己)。
  2. 收到所有Server回复以后,就计算出zxid最大的哪个Server,并将这个Server相关信息设置成下一次要投票的Server。
  3. 计算这过程中获得票数最多的的sever为获胜者,如果获胜者的票数超过半数,则该server被选为Leader。否则,继续这个过程,直到Leader被选举出来。
  4. Leader就会开始等待server连接。
  5. Follower连接Leader,将最大的zxid发送给Leader。
  6. Leader根据follower的zxid确定同步点,至此选举阶段完成。
  7. 选举阶段完成Leader同步后,通知follower已经成为uptodate状态。
  8. Follower收到uptodate消息后,又可以重新接受client的请求进行服务了。

  假如目前有5台服务器,每台服务器均没有数据,它们的编号分别是 1、2、3、4、5,按编号依次启动,它们的选择举过程:

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

1.4 ZAB协议

  ZAB( ZooKeeper Atomic Broadcast,ZooKeeper原子消息广播协议)协议是为分布式协调服务Zookeeper专门设计的一种支持崩溃恢复的原子广播协议。
  ZAB协议包括两种基本的模式:崩溃恢复消息广播
  当整个Zookeeper集群刚刚启动,或者Leader服务器宕机、重启或者网络故障导致不存在过半的服务器与Leader服务器保持正常通信时,所有进程(服务器)进入崩溃恢复模式,首先选举产生新的Leader服务器,然后集群中Follower服务器开始与新的Leader服务器进行数据同步,当集群中超过半数机器与该Leader服务器完成数据同步之后,退出恢复模式进入消息广播模式,Leader服务器开始接收客户端的事务请求生成事物提案来进行事务请求处理。

  • 事务编号Zxid (事务请求计数器 + epoch )
      在ZAB协议的事务编号Zxid设计中,Zxid 是一个64位的数字,其中低32位是一个简单的单调递增的计数器,针对客户端每一个事务请求,计数器加1;而高32位则代表Leader周期epoch的编号,每个当选产生一个新的Leader服务器,就会从这个Leader服务器上取出其本地日志中最大事务的Zxid,并从中读取epoch值,然后加1,以此作为新的epoch,并将低32位从0开始计数。
      Zxid类似于RDBMS中的事务ID,用于标识一次更新操作的Proposal(提议)ID。为了保证顺序性,该zkid必须单调递增。
  • epoch
      可以理解为当前集群所处的年代或者周期,每个Leader就像皇帝,都有自己的年号,所以每次改朝换代,Leader变更之后,都会在前一个年代的基础上加1。这样就算旧的Leader崩溃恢复之后,也没有人听他的了,因为follower只听从当前年代的Leader的命令。
  • Zab协议的两种模式
      Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和Leader的状态同步以后,恢复模式就结束了。状态同步保证了Leader和Server具有相同的系统状态。
  • ZAB协议4阶段
      1、Leader election(选举阶段:选出准Leader):节点在一开始都处于选举阶段,只要有一个节点得到超半数节点的票数,它就可以当选准Leader。只有到达广播阶段准Leader才会成为真正的Leader。这一阶段的目的是就是为了选出一个准Leader,然后进入下一个阶段。
      2、Discovery(发现阶段:接受提议、生成epoch 、接受epoch ):在这个阶段,followers跟准Leader进行通信,同步followers最近接收的事务提议。这个一阶段的主要目的是发现当前大多数节点接收的最新提议,并且准Leader生成新的epoch,让followers接受,更新它们的accepted Epoch一个follower只会连接一个Leader,如果有一个节点f认为另一个follower p是Leader,f在尝试连接p时会被拒绝,f被拒绝之后,就会进入重新选举阶段。
      3、Synchronization(同步阶段: 同步follower副本):同步阶段主要是利用Leader前一阶段获得的最新提议历史,同步集群中所有的副本。只有当大多数节点都同步完成,准Leader才会成为真正的Leader。follower只会接收zxid比自己的lastZxid大的提议。
      4、Broadcast(广播阶段:Leader消息广播):到了这个阶段,Zookeeper 集群才能正式对外提供事务服务,并且Leader可以进行消息广播。同时如果有新的节点加入,还需要对新节点进行同步。

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

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

1.5 Zookeeper工作原理

  1. Zookeeper的核心是原子广播,这个机制保证了各个server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式和广播模式。
  2. 当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数server的完成了和Leader的状态同步以后,恢复模式就结束了。
  3. 状态同步保证了Leader和server具有相同的系统状态。
  4. 一旦Leader已经和多数的follower进行了状态同步后,他就可以开始广播消息了,即进入广播状态。这时候当一个server加入zookeeper服务中,它会在恢复模式下启动,发现Leader,并和Leader进行状态同步。待到同步结束,它也参与消息广播。Zookeeper服务一直维持在Broadcast状态,直到Leader崩溃了或者Leader失去了大部分的followers支持。
  5. 广播模式需要保证proposal被按顺序处理,因此zk采用了递增的事务id号(zxid)来保
    证。所有的提议(proposal)都在被提出的时候加上了zxid。
  6. 实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch。低32位是个递增计数。
  7. 当Leader崩溃或者Leader失去大多数的follower,这时候zk进入恢复模式,恢复模式
    需要重新选举出一个新的leader,让所有的server都恢复到一个正确的状态。

1.6 监视与通知

  ZooKeeper通常以远程服务的方式被访问,如果每次访问znode时,客户端都需要获得节点中的内容,这样的代价就非常大。
  ZooKeeper选择了基于通知的机制:客户端向ZooKeeper注册需要接收通知的znode,通过对znode设置监视点(watch)来接收通知。监视点是一个单次触发的操作,意即监视点会触发一个通知。为了接收多个通知,客户端必须在每次通知后设置一个新的监视点。
  示例:当节点/tasks发生变化时,客户端会收到一个通知,并从ZooKeeper读取一个新值。

  通知机制的一个重要保障是,对同一个znode的操作,先向客户端传送通知,然后再对该节点进行变更。如果客户端对一个znode设置了监视点,而该znode发生了两个连续更新。第一次更新后,客户端在观察第一次变化前就接收到了通知,然后读取znode中的数据。我们认为主要特性在于通知机制阻止了客户端所观察的更新顺序。虽然ZooKeeper的状态变化传播给某些客户端时更慢,但我们保障客户端以全局的顺序来观察ZooKeeper的状态。
  ZooKeeper可以定义不同类型的通知,这依赖于设置监视点对应的通知类型。客户端可以设置多种监视点,如监控znode的数据变化、监控znode子节点的变化、监控znode的创建或删除。

1.6.1 客户端注册Watcher

  1)调用getData()/getChildren()/exist()三个API,传入Watcher对象。
  2)标记请求request,封装Watcher到WatchRegistration。
  3)封装成Packet对象,发服务端发送request。
  4)收到服务端响应后,将Watcher注册到ZKWatcherManager中进行管理。
  5)请求返回,完成注册。

1.6.2 服务端处理Watcher
  • 1、服务端接收Watcher并存储
      接收到客户端请求,处理请求判断是否需要注册Watcher,需要的话将数据节点的节点路径和ServerCnxn(ServerCnxn代表一个客户端和服务端的连接,实现了 Watcher的process接口,此时可以看成一个Watcher对象)存储在WatcherManager的WatchTable和watch2Paths中去。
  • 2、Watcher触发
      以服务端接收到setData() 事务请求触发NodeDataChanged事件为例:
  1. 封装WatchedEvent。将通知状态(SyncConnected)、事件类型(NodeDataChanged)以及节点路径封装成一个WatchedEvent对象。
  2. 查询Watcher。从WatchTable中根据节点路径查找Watcher。
  3. 没找到。说明没有客户端在该数据节点上注册过Watcher。
  4. 找到。提取并从WatchTable和Watch2Paths中删除对应Watcher(从这里可以看出 Watcher 在服务端是一次性的,触发一次就失效了)。
  • 3、调用process方法来触发Watcher
      这里process主要就是通过ServerCnxn对应的TCP连接发送Watcher事件通知。
1.6.3 客户端回调Watcher

  客户端SendThread线程接收事件通知,交由EventThread线程回调Watcher。
  客户端的Watcher机制同样是一次性的,一旦被触发后,该Watcher就失效了。

二、相关问题

2.1 ZooKeeper是什么

  ZooKeeper是一个为分布式应用提供一致性服务的软件,分布式应用程序可以基于Zookeeper实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。
  ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。ZooKeeper一般以集群的方式对外提供服务,一个集群包含多个节点,每个节点对应一台ZooKeeper服务器,所有的节点共同对外提供服务,整个集群环境对分布式数据一致性提供了全面的支持。
  ZooKeeper的特点:

  • 高性能
      ZooKeeper将全量数据存储在内存中,并直接服务于客户端的所有非事务请求,尤其适用于以读为主的应用场景。
  • 高可用
      ZooKeeper一般以集群的方式对外提供服务,一般3 ~ 5台机器就可以组成一个可用的Zookeeper集群了,每台机器都会在内存中维护当前的服务器状态,并且每台机器之间都相互保持着通信。只要集群中超过一般的机器都能够正常工作,那么整个集群就能够正常对外服务。
  • 严格顺序访问
      对于来自客户端的每个更新请求,ZooKeeper都会分配一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序。

2.2 ZooKeeper提供了什么

  文件系统、通知机制。
  通知机制:简单来说,客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。

2.2.1 Zookeeper文件系统


  Zookeeper提供一个多层级的节点命名空间(节点称为znode)。与文件系统不同的是,这些节点都可以设置关联的数据,而文件系统中只有文件节点可以存放数据而目录节点不行。
  Zookeeper为了保证高吞吐和低延迟,在内存中维护了这个树状的目录结构,这种特性使得Zookeeper不能用于存放大量的数据,每个节点的存放数据上限为1M。
  每个子目录项如NameService都被称作为znode,和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一的不同在于znode是可以存储数据的。

 【四种类型的数据节点znode】

  • 1、PERSISTENT:持久化目录节点
      客户端与ZooKeeper断开连接后,该节点依旧存在。除非手动删除,否则节点一直存在于Zookeeper上。
  • 2、 PERSISTENT_SEQUENTIAL:持久化顺序编号目录节点
      客户端与Zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号。基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。
  • 3、EPHEMERAL:临时目录节点
      客户端与ZooKeeper断开连接后,该节点被删除。临时节点的生命周期与客户端会话绑定,一旦客户端会话失效(客户端与ZooKeeper连接断开不一定会话失效),那么这个客户端创建的所有临时节点都会被移除。
  • 4、EPHEMERAL_SEQUENTIAL:临时顺序编号目录节点
      客户端与ZooKeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号。基本特性同临时节点,增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。
2.2.2 Zookeeper Watcher机制

  Zookeeper允许客户端向服务端的某个Znode注册一个Watcher监听,当服务端的一些指定事件触发了这个Watcher,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然后客户端根据Watcher通知状态和事件类型做出业务上的改变。
  工作机制:

  • 1、客户端注册watcher
      在创建一个ZooKeeper客户端对象实例时,可以向构造方法中传入一个Watcher,这个Watcher将作为整个ZooKeeper会话期间的默认Watcher,一致保存在客户端,并向ZooKeeper服务器注册Watcher。
      客户端并不会把真实的Watcher对象传递到服务器,仅仅只是在客户端请求中使用boolean类型属性进行标记,降低网络开销和服务器内存开销。
  • 2、服务端处理watcher
      服务端执行数据变更,当Watcher监听的对应数据节点的数据内容发生变更,如果找到对应的Watcher,会将其提取出来,同时从管理中将其删除(说明Watcher在服务端是一次性的,即触发一次就失效了),触发Watcher,向客户端发送通知。
  • 3、客户端回调watcher
      客户端获取通知,识别出事件类型,从相应的Watcher存储中去除对应的Watcher(说明客户端也是一次性的,即一旦触发就会失效)。

  Watcher机制特性总结:

  • 1、一致性
      无论是客户端还是服务器,一旦一个Watcher被触发,ZooKeeper都会将其从相应的存储中移除,因此开发人员在Watcher使用上要反复注册,这样可以有效减轻服务器压力。
  • 2、客户端串行执行
      客户端Watcher回调的过程是一个串行同步的过程,这保证了顺序。
  • 3、轻量
      客户端并不会把真实的Watcher对象传递到服务器,仅仅只是在客户端请求中使用boolean类型属性进行标记,降低网络开销和服务器内存开销。
      3.1)Watcher通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。
      3.2)客户端向服务端注册Watcher的时候,并不会把客户端真实的Watcher对象实体传递到服务端,仅仅是在客户端请求中使用boolean类型属性进行了标记。
  • 4、Zookeeper只能保证最终的一致性,而无法保证强一致性
      watcher event异步发送watcher的通知事件从server发送到client是异步的,这就存在一个问题,不同的客户端和服务器之间通过socket进行通信,由于网络延迟或其他因素导致客户端在不通的时刻监听到事件,由于Zookeeper本身提供了ordering guarantee,即客户端监听事件后,才会感知它所监视znode发生了变化。所以我们使用Zookeeper不能期望能够监控到节点每次的变化。
  • 5、当一个客户端连接到一个新的服务器上时,watch将会被以任意会话事件触发
      当与一个服务器失去连接的时候,是无法接收到watch的。而当client重新连接时,如果需要的话,所有先前注册过的watch,都会被重新注册。通常这是完全透明的。只有在一个特殊情况下,watch可能会丢失:对于一个未创建的znode的exist watch,如果在客户端断开连接期间被创建了,并且随后在客户端连接上之前又删除了,这种情况下,这个watch事件可能会被丢失。

2.3 Zookeeper怎么保证主从节点的状态同步

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

  • 1、恢复模式
      当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数server完成了和Leader的状态同步以后,恢复模式就结束了。状态同步保证了Leader和server具有相同的系统状态。
  • 2、广播模式
      一旦Leader已经和多数的follower进行了状态同步后,它就可以开始广播消息了,即进入广播状态。这时候当一个server加入ZooKeeper服务中,它会在恢复模式下启动,发现Leader,并和Leader进行状态同步。待到同步结束,它也参与消息广播。ZooKeeper服务一直维持在Broadcast状态,直到Leader崩溃了或者Leader失去了大部分的followers支持。

2.4 ACL权限控制机制

  UGO(User/Group/Others),目前在Linux/Unix文件系统中使用,也是使用最广泛的权限控制方式。是一种粗粒度的文件系统权限控制模式。
  ACL(Access Control List)访问控制列表,包括三个方面:

  • 1、权限模式
      1)IP:从IP地址粒度进行权限控制。
      2)Digest:最常用,用类似于username:password的权限标识来进行权限配置,便于区分不同应用来进行权限控制。
      3)World:最开放的权限控制方式,是一种特殊的digest模式,只有一个权限标识“world:anyone”。
      4)Super:超级用户。
  • 2、授权对象
      授权对象指的是权限赋予的用户或一个指定实体,例如IP地址。
  • 3、权限
      1)CREATE:数据节点创建权限,允许授权对象在该Znode下创建子节点。
      2)DELETE:子节点删除权限,允许授权对象删除该数据节点的子节点。
      3)READ:数据节点的读取权限,允许授权对象访问该数据节点并读取其数据内容或子节点列表等。
      4)WRITE:数据节点更新权限,允许授权对象对该数据节点进行更新操作。
      5)ADMIN:数据节点管理权限,允许授权对象对该数据节点进行ACL相关设置操作。

2.5 Chroot特性

  3.2.0 版本后,添加了Chroot特性,该特性允许每个客户端为自己设置一个命名空间。如果一个客户端设置了Chroot,那么该客户端对服务器的任何操作,都将会被限制在其自己的命名空间下。
  通过设置Chroot,能够将一个客户端应用于Zookeeper服务端的一颗子树相对应,在那些多个应用公用一个Zookeeper进群的场景下,对实现不同应用间的相互隔离非常有帮助。

2.6 会话管理

  分桶策略:将类似的会话放在同一区块中进行管理,以便于Zookeeper对会话进行不同区块的隔离处理以及同一区块的统一处理。
  分配原则:每个会话的“下次超时时间点”(ExpirationTime)
  计算公式:

    ExpirationTime_ = currentTime + sessionTimeout
    #ExpirationInterval 是指 Zookeeper 会话超时检查时间间隔,默认 tickTime
    ExpirationTime = (ExpirationTime_ / ExpirationInrerval + 1) * ExpirationInterval 

2.7 Zookeeper下Server工作状态

  服务器具有四种状态,分别是 LOOKING、FOLLOWING、LEADING、OBSERVING。

  • LOOKING
      寻找Leader状态。当服务器处于该状态时,它会认为当前集群中没有Leader,因此需要进入Leader选举状态。
  • FOLLOWING
      跟随者状态。表明当前服务器角色是Follower。
  • LEADING
      领导者状态。表明当前服务器角色是Leader。
  • OBSERVING
      观察者状态。表明当前服务器角色是 Observer。

2.8 Zookeeper是如何保证事务的顺序一致性的

  Zookeeper采用了全局递增的事务Id来标识,所有的proposal(提议)都在被提出的时候加上了zxid,zxid实际上是一个64位的数字,高32位是epoch用来标识Leader周期,如果有新的Leader产生出来,epoch会自增,低32位用来递增计数。当新产生proposal的时候,会依据数据库的两阶段过程,首先会向其他的server发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会开始执行。

2.9 分布式集群中为什么会有Master主节点

  在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机器可以共享这个结果,这样可以大大减少重复计算,提高性能,于是就需要进行Leader选举。

2.10 zk节点宕机如何处理

  Zookeeper本身也是集群,推荐配置不少于3个服务器。Zookeeper自身也要保证当一个节点宕机时,其他节点会继续提供服务。
  如果是一个Follower宕机,还有2台服务器提供访问,因为Zookeeper上的数据是有多个副本的,数据并不会丢失;
  如果是一个Leader宕机,Zookeeper会选举出新的Leader。
  ZK集群的机制是只要超过半数的节点正常,集群就能正常提供服务。只有在 ZK节点挂得太多,只剩一半或不到一半节点能工作,集群才失效。
  所以,3个节点的cluster可以挂掉1个节点(leader可以得到2票>1.5),2个节点的cluster就不能挂掉任何1个节点了(leader可以得到 1 票<=1)。

2.11 Zookeeper负载均衡和Nginx负载均衡的区别

  zk的负载均衡是可以调控,nginx只是能调权重,其他需要可控的都需要自己写插件;但是nginx的吞吐量比zk大很多,应该说按业务选择用哪种方式。

2.12 Zookeeper有哪几种几种部署模式

  • 单机部署:一台集群上运行;
  • 集群部署:多台集群运行;
  • 伪集群部署:一台集群启动多个Zookeeper实例运行。

2.13 集群最少要几台机器,集群规则是怎样的?集群中有3台服务器,其中一个节点宕机,这个时候Zookeeper还可以使用吗?

  集群规则为2N+1台,N>0,即3台。
  可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。

2.14 集群支持动态添加机器吗

  其实就是水平扩容,Zookeeper 在这方面不太好。两种方式:

  • 1、全部重启
      关闭所有Zookeeper服务,修改配置之后启动。不影响之前客户端的会话。
  • 2、逐个重启
      在过半存活即可用的原则下,一台机器重启不影响整个集群对外提供服务。这是比较常用的方式。

  3.5 版本开始支持动态扩容。

2.15 Zookeeper对节点的watch监听通知是永久的吗

  不是。官方声明:一个Watch事件是一个一次性的触发器,当被设置了Watch的数据发生了改变的时候,则服务器将这个改变发送给设置了Watch的客户端,以便通知它们。

  • 为什么不是永久的?
      举个例子,如果服务端变动频繁,而监听的客户端很多情况下,每次变动都要通知到所有的客户端,给网络和服务器造成很大压力。
      一般是客户端执行getData(“/节点 A”,true),如果节点A发生了变更或删除,客户端会得到它的watch事件,但是在之后节点A又发生了变更,而客户端又没有设置watch事件,就不再给客户端发送。
      在实际应用中,很多情况下,我们的客户端不需要知道服务端的每一次变动,我只要最新的数据即可。

2.16 Zookeeper的典型应用场景

  Zookeeper是一个典型的发布/订阅模式的分布式数据管理与协调框架,开发人员可以使用它来进行分布式数据的发布和订阅。
  通过对Zookeeper中丰富的数据节点进行交叉使用,配合Watcher事件通知机制,可以非常方便的构建一系列分布式应用中都会涉及的核心功能,如:

  • 1、数据发布/订阅
      数据发布/订阅系统,即所谓的配置中心,顾名思义就是发布者发布数据供订阅者进行数据订阅。
      目的:动态获取数据(配置信息),实现数据(配置信息)的集中式管理和数据的动态更新。
      2种模式:Push模式、Pull模式。
      数据(配置信息)特性:1、数据量通常比较小。2、数据内容在运行时会发生动态更新。3、集群中各机器共享,配置一致。
      基于Zookeeper的实现方式:
       1)数据存储:将数据(配置信息)存储到Zookeeper上的一个数据节点。
       2)数据获取:应用在启动初始化节点从Zookeeper数据节点读取数据,并在该节点上注册一个数据变更Watcher。
       3)数据变更:当变更数据时,更新Zookeeper对应节点数据,Zookeeper会将数据变更通知发到各客户端,客户端接到通知后重新读取变更后的数据即可。
  • 2、负载均衡
      zk的命名服务,命名服务是指通过指定的名字来获取资源或者服务的地址,利用zk创建一个全局的路径,这个路径就可以作为一个名字,指向集群中的集群,提供的服务的地址,或者一个远程的对象等等。
  • 3、命名服务
      命名服务是指通过指定的名字来获取资源或者服务的地址,利用zk创建一个全局的路径,这个路径就可以作为一个名字,指向集群中的集群,提供的服务的地址,或者一个远程的对象等等。
  • 4、分布式协调/通知
      对于系统调度来说:操作人员发送通知实际是通过控制台改变某个节点的状态,然后zk将这些变化发送给注册了这个节点的watcher的所有客户端。
      对于执行情况汇报:每个工作进程都在某个目录下创建一个临时节点。并携带工作的进度数据,这样汇总的进程可以监控目录子节点的变化获得工作进度的实时的全局情况。
  • 4、配置管理
      程序分布式的部署在不同的机器上,将程序的配置信息放在zk的znode下,当有配置发生改变时,也就是znode发生变化时,可以通过改变zk中某个目录节点的内容,利用watcher通知给各个客户端,从而更改配置。
  • 5、集群管理
      所谓集群管理无在乎两点:是否有机器退出和加入、选举master。
      对于第一点,所有机器约定在父目录下创建临时目录节点,然后监听父目录节点的子节点变化消息。一旦有机器挂掉,该机器与zookeeper的连接断开,其所创建的临时目录节点被删除,所有其他机器都收到通知:某个兄弟目录被删除。
      对于第二点,我们稍微改变一下,所有机器创建临时顺序编号目录节点,每次选取编号最小的机器作为master就好。
  • 6、Master 选举
  • 7、分布式锁
      有了Zookeeper的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一个是保持独占,另一个是控制时序。
      对于第一类,我们将ZooKeeper上的一个znode看作是一把锁,通过create znode的方式来实现。所有客户端都去创建/distribute_lock节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的distribute_lock节点就释放出锁。
      对于第二类,/distribute_lock已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便。
  • 8、分布式队列
      两种类型的队列:
       1)同步队列,当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。
       2)队列按照FIFO方式进行入队和出队操作。
      第一类,在约定目录下创建临时目录节点,监听节点数目是否是我们要求的数目。
      第二类,和分布式锁服务中的控制时序场景基本原理一致,入列有编号,出列按编号。在特定的目录下创建PERSISTENT_SEQUENTIAL节点,创建成功时Watcher 通知等待的队列,队列删除序列号最小的节点用以消费。此场景下Zookeeper的znode用于消息存储,znode存储的数据就是消息队列中的消息内容,SEQUENTIAL序列号就是消息的编号,按序取出即可。由于创建的节点是持久化的,所以不必担心队列消息的丢失问题。

2.17 Zookeeper都有哪些功能

  • 1、集群管理:监控节点存活状态、运行请求等;
  • 2、主节点选举:主节点挂掉了之后可以从备用的节点开始新一轮选主,主节点选举说的就是这个选举的过程,使用Zookeeper可以协助完成这个过程;
  • 3、分布式锁:Zookeeper提供两种锁:独占锁、共享锁。独占锁即一次只能有一个线程使用资源,共享锁是读锁共享,读写互斥,即可以有多线线程同时读同一个资源,如果要使用写锁也只能有一个线程使用。Zookeeper可以对分布式锁进行控制。
  • 4、命名服务:在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。

2.18 Zookeeper和Dubbo的关系

  • Zookeeper
      Zookeeper用来注册服务和进行负载均衡,哪一个服务由哪一个机器来提供必需让调用者知道,简单来说就是ip地址和服务名称的对应关系。当然也可以通过硬编码的方式把这种对应关系在调用方业务代码中实现,但是如果提供服务的机器挂掉调用者无法知晓,如果不更改代码会继续请求挂掉的机器提供服务。Zookeeper通过心跳机制可以检测挂掉的机器并将挂掉机器的ip和服务对应关系从列表中删除。至于支持高并发,简单来说就是横向扩展,在不更改代码的情况通过添加机器来提高运算能力。通过添加新的机器向zookeeper注册服务,服务的提供者多了能服务的客户就多了。
  • dubbo
      是管理中间层的工具,在业务层到数据仓库间有非常多服务的接入和服务提供者需要调度,dubbo提供一个框架解决这个问题。
      注意这里的dubbo只是一个框架,至于你架子上放什么是完全取决于你的,就像一个汽车骨架,你需要配你的轮子引擎。这个框架中要完成调度必须要有一个分布式的注册中心,储存所有服务的元数据,你可以用zk,也可以用别的,只是大家都用zk。
  • zookeeper和dubbo的关系
      Dubbo的将注册中心进行抽象,它可以外接不同的存储媒介给注册中心提供服务,有ZooKeeper、Memcached、Redis等。
      引入了ZooKeeper作为存储媒介,也就把ZooKeeper的特性引进来。首先是负载均衡,单注册中心的承载能力是有限的,在流量达到一定程度的时 候就需要分流,负载均衡就是为了分流而存在的,一个ZooKeeper群配合相应的Web应用就可以很容易达到负载均衡;资源同步,单单有负载均衡还不够,节点之间的数据和资源需要同步,ZooKeeper集群就天然具备有这样的功能;命名服务,将树状结构用于维护全局的服务地址列表,服务提供者在启动 的时候,向ZooKeeper上的指定节点 /dubbo/${serviceName}/providers目录下写入自己的URL地址,这个操作就完成了服务的发布。 其他特性还有Master选举,分布式锁等。

2.19 Zookeeper做了什么

  1、命名服务
  2、配置管理
  3、集群管理
  4、分布式锁
  5、队列管理

2.20 获取分布式锁的流程


  在获取分布式锁的时候在locker节点下创建临时顺序节点,释放锁的时候删除该临时节点。客户端调用createNode方法在locker下创建临时顺序节点,然后调用getChildren(“locker”)来获取locker下面的所有子节点,此时不用设置任何Watcher。
  客户端获取到所有的子节点path之后,如果发现自己创建的节点在所有创建的子节点序号最小,那么就认为该客户端获取到了锁。如果发现自己创建的节点并非locker所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点 ,然后对其调用exist()方法,同时对其注册事件监听器。
  之后,让这个被关注的节点删除,则客户端的Watcher会收到相应通知,此时再次判断自己创建的节点是否是locker子节点中序号最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。当前这个过程中还需要许多的逻辑判断。

  代码的实现主要是基于互斥锁,获取分布式锁的重点逻辑在于BaseDistributedLock ,实现了基于Zookeeper实现分布式锁的细节。

2.21 Zookeeper工作原理

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

2.22 ZooKeeper是如何选取主Leader的

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

  • Zookeeper选主流程(basic paxos)
      1)选举线程由当前Server发起选举的线程担任,其主要功能是对投票结果进行统计,并选出推荐的Server;
      2)选举线程首先向所有Server发起一次询问(包括自己);
      3)选举线程收到回复后,验证是否是自己发起的询问(验证zxid是否一致),然后获取对方的id(myid),并存储到当前询问对象列表中,最后获取对方提议的Leader相关信息(id、zxid),并将这些信息存储到当次选举的投票记录表中;
      4)收到所有Server回复以后,就计算出zxid最大的那个Server,并将这个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会记录事务日志并定期进行快照,方便在恢复时进行状态恢复。
  • 流程(fast paxos)
      fast paxos流程是在选举过程中,某Server首先向所有Server提议自己要成为Leader,当其它Server收到提议以后,解决epoch和zxid的冲突,并接受对方的提议,然后向对方发送接受提议完成的消息,重复这个流程,最后一定能选举出Leader。

2.23 ZAB和Paxos算法的联系与区别

  • 相同点
      1)两者都存在一个类似于Leader进程的角色,由其负责协调多个Follower 进程的运行。
      2)Leader进程都会等待超过半数的Follower做出正确的反馈后,才会将一个提案进行提交。
      3)ZAB协议中,每个Proposal中都包含一个epoch值来代表当前的Leader周期,Paxos中名字为Ballot。
  • 不同点
      ZAB用来构建高可用的分布式数据主备系统(Zookeeper),Paxos是用来构建分布式一致性状态机系统。

2.24 Zookeeper同步流程

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

1、Leader等待server连接;
2、Follower连接Leader,将最大的zxid发送给Leader;
3、Leader根据follower的zxid确定同步点;
4、完成同步后通知follower已经成为uptodate状态;
5、Follower收到uptodate消息后,又可以重新接受client的请求进行服务了。

2.25 有哪些著名的开源项目用到了ZooKeeper

  1、Kafka : ZooKeeper主要为Kafka提供Broker和Topic的注册以及多个 Partition的负载均衡等功能。
  2、Hbase : ZooKeeper为Hbase提供确保整个集群只有一个Master以及保存和提供regionserver状态信息(是否在线)等功能。
  3、Hadoop : ZooKeeper为Namenode提供高可用支持。

相关推荐

  1. Zookeeper

    2024-01-13 23:34:03       59 阅读
  2. <span style='color:red;'>ZooKeeper</span>

    ZooKeeper

    2024-01-13 23:34:03      70 阅读
  3. <span style='color:red;'>Zookeeper</span>

    Zookeeper

    2024-01-13 23:34:03      45 阅读
  4. <span style='color:red;'>ZooKeeper</span>

    ZooKeeper

    2024-01-13 23:34:03      49 阅读
  5. <span style='color:red;'>zookeeper</span>

    zookeeper

    2024-01-13 23:34:03      42 阅读
  6. <span style='color:red;'>Zookeeper</span>

    Zookeeper

    2024-01-13 23:34:03      36 阅读
  7. <span style='color:red;'>ZooKeeper</span>

    ZooKeeper

    2024-01-13 23:34:03      42 阅读
  8. <span style='color:red;'>ZooKeeper</span>

    ZooKeeper

    2024-01-13 23:34:03      35 阅读
  9. ZooKeeper

    2024-01-13 23:34:03       41 阅读
  10. <span style='color:red;'>Zookeeper</span>

    Zookeeper

    2024-01-13 23:34:03      36 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-13 23:34:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-13 23:34:03       100 阅读
  3. 在Django里面运行非项目文件

    2024-01-13 23:34:03       82 阅读
  4. Python语言-面向对象

    2024-01-13 23:34:03       91 阅读

热门阅读

  1. 第一天业务题目

    2024-01-13 23:34:03       60 阅读
  2. 【万题详解1】洛谷P1230 智力大冲浪

    2024-01-13 23:34:03       69 阅读
  3. 基于AidLux的智慧教育版面分析应用

    2024-01-13 23:34:03       82 阅读
  4. MyBatis面试题及高级用法

    2024-01-13 23:34:03       54 阅读
  5. AI监控技术与应用案例

    2024-01-13 23:34:03       61 阅读
  6. Rust变量、常量声明与基本数据类型

    2024-01-13 23:34:03       63 阅读
  7. 聊聊PowerJob的UseCacheLock

    2024-01-13 23:34:03       66 阅读
  8. 设计模式-工厂方法模式

    2024-01-13 23:34:03       62 阅读
  9. 计算机体系中程序、进程与线程的关系解读

    2024-01-13 23:34:03       65 阅读
  10. 如何建立正确的文献阅读习惯

    2024-01-13 23:34:03       56 阅读