zookeeper(1)-概述

ZooKeeper概述

  ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现。它提供了简单原始的功能,分布式应用能够基于它实现更高级的服务,比 如分布式同步,配置管理,集群管理,命名管理,队列管理。它被设计为易于编程,使用文 件系统目录树做为数据模型。服务端跑在 java 上,提供 java 和 C 的客户端 API 众所周知,协调服务很是容易出错,可是却很难恢复正常,例如,协调服务很容易处于 竞态以致于出现死锁。咱们设计 ZooKeeper 的目的是为了减轻分布式应用程序所承担的协 调任务 ZooKeeper 是集群的管理者,监视着集群中各节点的状态,根据节点提交的反馈进行下 一步合理的操做。最终,将简单易用的接口和功能稳定,性能高效的系统提供给用户。java

  前面提到了那么多的服务,好比分布式锁、配置维护、组服务等,那它们是如何实现的呢,我相信这才是你们关心的东西。ZooKeeper在实现这些服务时,首先它设计一种新的数据结构——Znode,而后在该数据结构的基础上定义了一些原语,也就是一些关于该数据结构的一些操做。有了这些数据结构和原语还不够,由于咱们的ZooKeeper是工做在一个分布式的环境下,咱们的服务是经过消息以网络的形式发送给咱们的分布式应用程序,因此还须要一个通知机制——Watcher机制。那么总结一下,ZooKeeper所提供的服务主要是经过:数据结构+原语+watcher机制,三个部分来实现的。那么我就从这三个方面,给你们介绍一下ZooKeeper。node

ZooKeeper数据模型

ZooKeeper数据模型Znode

  ZooKeeper拥有一个层次的命名空间,这个和标准的文件系统很是类似,以下图所示。数据库

  从图中咱们能够看出ZooKeeper的数据模型,在结构上和标准文件系统的很是类似,都是采用这种树形层次结构,ZooKeeper树中的每一个节点被称为—Znode。和文件系统的目录树同样,ZooKeeper树中的每一个节点能够拥有子节点。但也有不一样之处:编程

(1) 引用方式服务器

  Zonde经过路径引用,如同Unix中的文件路径。路径必须是绝对的,所以他们必须由斜杠字符来开头。除此之外,他们必须是惟一的,也就是说每个路径只有一个表示,所以这些路径不能改变。在ZooKeeper中,路径由Unicode字符串组成,而且有一些限制。字符串"/zookeeper"用以保存管理信息,好比关键配额信息。网络

(2) Znode结构数据结构

  ZooKeeper命名空间中的Znode,兼具文件和目录两种特色。既像文件同样维护着数据、元信息、ACL、时间戳等数据结构,又像目录同样能够做为路径标识的一部分。图中的每一个节点称为一个Znode。 每一个Znode由3部分组成:异步

  ① stat:此为状态信息, 描述该Znode的版本, 权限等信息分布式

  ② data:与该Znode关联的数据函数

  ③ children:该Znode下的子节点

  ZooKeeper虽然能够关联一些数据,但并无被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,好比分布式应用中的配置文件信息、状态信息、聚集位置等等。这些数据的共同特性就是它们都是很小的数据,一般以KB为大小单位。ZooKeeper的服务器和客户端都被设计为严格检查并限制每一个Znode的数据大小至多1M,但常规使用中应该远小于此值。

(3) 数据访问

  ZooKeeper中的每一个节点存储的数据要被原子性的操做。也就是说读操做将获取与节点相关的全部数据,写操做也将替换掉节点的全部数据。另外,每个节点都拥有本身的ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点能够执行的操做。

(4) 节点类型

  ZooKeeper中的节点有两种,分别为临时节点永久节点。节点的类型在建立时即被肯定,而且不能改变。

  ① 临时节点:该节点的生命周期依赖于建立它们的会话。一旦会话(Session)结束,临时节点将被自动删除,固然能够也能够手动删除。虽然每一个临时的Znode都会绑定到一个客户端会话,但他们对全部的客户端仍是可见的。另外,ZooKeeper的临时节点不容许拥有子节点。

  ② 永久节点:该节点的生命周期不依赖于会话,而且只有在客户端显示执行删除操做的时候,他们才能被删除。

(5) 顺序节点

  当建立Znode的时候,用户能够请求在ZooKeeper的路径结尾添加一个递增的计数。这个计数对于此节点的父节点来讲是惟一的,它的格式为"%10d"(10位数字,没有数值的数位用0补充,例如"0000000001")。当计数值大于232-1时,计数器将溢出。

(6) 观察

  客户端能够在节点上设置watch,咱们称之为监视器。当节点状态发生改变时(Znode的增、删、改)将会触发watch所对应的操做。当watch被触发时,ZooKeeper将会向客户端发送且仅发送一条通知,由于watch只能被触发一次,这样能够减小网络流量。

ZooKeeper中的时间

导致ZooKeeper节点状态改变的每个操做都将使节点接收到一个Zxid格式的时间戳,而且这个时间戳全局有序。也就是说,每一个对节点的改变都将产生一个惟一的Zxid。若是Zxid1的值小于Zxid2的值,那么Zxid1所对应的事件发生在Zxid2所对应的事件以前。实际上,ZooKeeper的每一个节点维护者三个Zxid值,为别为:cZxid、mZxid、pZxid。

 cZxid: 是节点的建立时间所对应的Zxid格式时间戳。

② mZxid:是节点的修改时间所对应的Zxid格式时间戳。

③ pZxid: 是与 该节点的子节点(或该节点)的最近一次 建立 / 删除 的时间戳对应

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

对节点的每个操做都将导致这个节点的版本号增长。每一个节点维护着三个版本号,他们分别为:

① version:节点数据版本号
② cversion:子节点版本号
③ aversion:节点所拥有的ACL版本号

ZooKeeper节点属性

经过前面的介绍,咱们能够了解到,一个节点自身拥有表示其状态的许多重要属性,以下图所示。

图 4.2 Znode节点属性结构

 

ZooKeeper服务中操做

在ZooKeeper中有9个基本操做,以下图所示:

  更新ZooKeeper操做是有限制的。delete或setData必须明确要更新的Znode的版本号,咱们能够调用exists找到。若是版本号不匹配,更新将会失败。

  更新ZooKeeper操做是非阻塞式的。所以客户端若是失去了一个更新(因为另外一个进程在同时更新这个Znode),他能够在不阻塞其余进程执行的状况下,选择从新尝试或进行其余操做。

  尽管ZooKeeper能够被看作是一个文件系统,可是处于便利,摒弃了一些文件系统地操做原语。由于文件很是的小而且使总体读写的,因此不须要打开、关闭或是寻地的操做。

监听机制

watch触发器

(1) watch概述

ZooKeeper能够为全部的读操做设置watch,这些读操做包括:exists()、getChildren()及getData()。watch事件是一次性的触发器,当watch的对象状态发生改变时,将会触发此对象上watch所对应的事件。watch事件将被异步地发送给客户端,而且ZooKeeper为watch机制提供了有序的一致性保证。理论上,客户端接收watch事件的时间要快于其看到watch对象状态变化的时间。

(2) watch类型

ZooKeeper所管理的watch能够分为两类:

 数据watch(data  watches):getDataexists负责设置数据watch
② 孩子watch(child watches):getChildren负责设置孩子watch

咱们能够经过操做返回的数据来设置不一样的watch:

① getData和exists:返回关于节点的数据信息
② getChildren:返回孩子列表

所以

① 一个成功的setData操做将触发Znode的数据watch

 一个成功的create操做将触发Znode的数据watch以及孩子watch

③ 一个成功的delete操做将触发Znode的数据watch以及孩子watch

(3) watch注册与处触发

下图  watch设置操做及相应的触发器如图下图所示:

 

① exists操做上的watch,在被监视的Znode建立删除数据更新时被触发。
 getData操做上的watch,在被监视的Znode删除数据更新时被触发。在被建立时不能被触发,由于只有Znode必定存在,getData操做才会成功。
 getChildren操做上的watch,在被监视的Znode的子节点建立删除,或是这个Znode自身被删除时被触发。能够经过查看watch事件类型来区分是Znode,仍是他的子节点被删除:NodeDelete表示Znode被删除,NodeDeletedChanged表示子节点被删除。

Watch由客户端所链接的ZooKeeper服务器在本地维护,所以watch能够很是容易地设置、管理和分派。当客户端链接到一个新的服务器时,任何的会话事件都将可能触发watch。另外,当从服务器断开链接的时候,watch将不会被接收。可是,当一个客户端从新创建链接的时候,任何先前注册过的watch都会被从新注册。

(4) 须要注意的几点

Zookeeper的watch实际上要处理两类事件:

① 链接状态事件(type=None, path=null)

这类事件不须要注册,也不须要咱们连续触发,咱们只要处理就好了。

② 节点事件

节点的创建,删除,数据的修改。它是one time trigger,咱们须要不停的注册触发,还可能发生事件丢失的状况。

上面2类事件都在Watch中处理,也就是重载的process(Event event)

节点事件的触发,经过函数exists,getData或getChildren来处理这类函数,有双重做用:

① 注册触发事件

② 函数自己的功能

函数的自己的功能又能够用异步的回调函数来实现,重载processResult()过程当中处理函数自己的的功能。

监听工做原理

ZooKeeper 的 Watcher 机制主要包括客户端线程、客户端 WatcherManager、Zookeeper 服务器三部分。客户端在向 ZooKeeper 服务器注册的同时,会将 Watcher 对象存储在客户端的 WatcherManager 当中。当 ZooKeeper 服务器触发 Watcher 事件后,会向客户端发送通知, 客户端线程从 WatcherManager 中取出对应的 Watcher 对象来执行回调逻辑

ZooKeeper应用举例 

  为了方便你们理解ZooKeeper,在此就给你们举个例子,看看ZooKeeper是如何实现的他的服务的,我以ZooKeeper提供的基本服务分布式锁为例。

分布式锁应用场景

在分布式锁服务中,有一种最典型应用场景,就是经过对集群进行Master选举,来解决分布式系统中的单点故障。什么是分布式系统中的单点故障:一般分布式系统采用主从模式,就是一个主控机链接多个处理节点。主节点负责分发任务,从节点负责处理任务,当咱们的主节点发生故障时,那么整个系统就都瘫痪了,那么咱们把这种故障叫做单点故障。以下图7.1和7.2所示:

          图 7.1 主从模式分布式系统                             图7.2 单点故障

 

传统解决方案

  传统方式是采用一个备用节点,这个备用节点按期给当前主节点发送ping包,主节点收到ping包之后向备用节点发送回复Ack,当备用节点收到回复的时候就会认为当前主节点还活着,让他继续提供服务。如图7.3所示:

  当主节点挂了,这时候备用节点收不到回复了,而后他就认为主节点挂了接替他成为主节点以下图7.4所示:

可是这种方式就是有一个隐患,就是网络问题,来看一网络问题会形成什么后果,以下图7.5所示:

            图 7.5 网络故障

 

 也就是说咱们的主节点的并无挂,只是在回复的时候网络发生故障,这样咱们的备用节点一样收不到回复,就会认为主节点挂了,而后备用节点将他的Master实例启动起来,这样咱们的分布式系统当中就有了两个主节点也就是---双Master,出现Master之后咱们的从节点就会将它所作的事一部分汇报给了主节点,一部分汇报给了从节点,这样服务就全乱了。为了防止出现这种状况,咱们引入了ZooKeeper,它虽然不能避免网络故障,但它可以保证每时每刻只有一个Master。我么来看一下ZooKeeper是如何实现的。

 ZooKeeper解决方案

 

1) Master启动

在引入了Zookeeper之后咱们启动了两个主节点,"主节点-A"和"主节点-B"他们启动之后,都向ZooKeeper去注册一个节点。咱们假设"主节点-A"锁注册地节点是"master-00001","主节点-B"注册的节点是"master-00002",注册完之后进行选举,编号最小的节点将在选举中获胜得到锁成为主节点,也就是咱们的"主节点-A"将会得到锁成为主节点,而后"主节点-B"将被阻塞成为一个备用节点。那么,经过这种方式就完成了对两个Master进程的调度。

              图7.6 ZooKeeper Master选举

(2) Master故障

若是"主节点-A"挂了,这时候他所注册的节点将被自动删除,ZooKeeper会自动感知节点的变化,而后再次发出选举,这时候"主节点-B"将在选举中获胜,替代"主节点-A"成为主节点。

                图7.7 ZooKeeper Master选举

(3) Master 恢复

              图7.8 ZooKeeper Master选举

若是主节点恢复了,他会再次向ZooKeeper注册一个节点,这时候他注册的节点将会是"master-00003",ZooKeeper会感知节点的变化再次发动选举,这时候"主节点-B"在选举中会再次获胜继续担任"主节点","主节点-A"会担任备用节点。