1、ZooKeeper的背景java
1.1 认识ZooKeepernode
ZooKeeper---译名为“动物园管理员”。动物园里固然有好多的动物,游客能够根据动物园提供的向导图到不一样的场馆观赏各类类型的动物,而不是像走在原始丛林里,心惊胆颤的被动 物所观赏。为了让各类不一样的动物呆在它们应该呆的地方,而不是相互串门,或是相互厮杀,就须要动物园管理员按照动物的各类习性加以分类和管理,这样咱们才能更加放心安全的观赏动物。mysql
回到企业级应用系统中,随着信息化水平的不断提升,企业级系统变得愈来愈庞大臃肿,性能急剧降低,客户抱怨频频。拆分系统是目前咱们可选择的解决系统可伸缩性和性能问题的惟一行之有效的方法。可是拆分系统同时也带来了系统的复杂性——各子系统不是孤立存在的,它们彼此之间须要协做和交互,这就是咱们常说的分布式系统0。各个子系统就比如动物园里的动物,为了使各个子系统能正常为用户提供统一的服务,必须须要一种机制来进行协调——这就是ZooKeeper(动物园管理员)。算法
1.2 为何使用ZooKeepersql
咱们知道要写一个分布式应用是很是困难的,主要缘由就是局部故障。一个消息经过网络在两个节点之间传递时,网络若是发生故障,发送方并不知道接收方是否接收到了这个消息。他可能在网络故障迁就收到了此消息,也坑没有收到,又或者可能接收方的进程死了。发送方了解状况的惟一方法就是再次链接发送方,并向他进行询问。这就是局部故障:根本不知道操做是否失败。所以,大部分分布式应用须要一个主控、协调控制器来管理物理分布的子进程。目前,大部分应用须要开发私有的协调程序,缺少一个通用的机制。协调程序的反复编写浪费,且难以造成通用、伸缩性好的协调器。协调服务很是容易出错,并很难从故障中恢复。例如:协调服务很容易处于竞态1甚至死锁2。Zookeeper的设计目的,是为了减轻分布式应用程序所承担的协调任务。数据库
Zookeeper并不能阻止局部故障的发生,由于它们的本质是分布式系统。他固然也不会隐藏局部故障。ZooKeeper的目的就是提供一些工具集,用来创建安全处理局部故障的分布式应用。express
ZooKeeper是一个分布式小文件系统,而且被设计为高可用性。经过选举算法和集群复制能够避免单点故障3,因为是文件系统,因此即便全部的ZooKeeper节点所有挂掉,数据也不会丢失,重启服务器以后,数据便可恢复。另外ZooKeeper的节点更新是原子的,也就是说更新不是成功就是失败。经过版本号,ZooKeeper实现了更新的乐观锁4,当版本号不相符时,则表示待更新的节点已经被其余客户端提早更新了,而当前的整个更新操做将所有失败。固然全部的一切ZooKeeper已经为开发者提供了保障,咱们须要作的只是调用API。与此同时,随着分布式应用的的不断深刻,须要对集群管理逐步透明化监控集群和做业状态,能够充分利ZK的独有特性。apache
1.3 ZooKeeper的应用 安全
ZooKeeper本质上是一个分布式的小文件存储系统。本来是Apache Hadoop的一个组件,如今被拆分为一个Hadoop的独立子项目,在Hbase(Hadoop的另一个被拆分出来的子项目,用于分布式环境下的超大数据量的DBMS)中也用到了ZooKeeper集群。 服务器
Hadoop,使用Zookeeper的事件处理确保整个集群只有一个NameNode,存储配置信息等.
HBase,使用Zookeeper的事件处理确保整个集群只有一个HMaster,察觉HRegionServer联机和宕(dàng)机,存储访问控制列表等。
有人会怀疑ZooKeeper的执行能力,在ZooKeeper诞生的地方——Yahoo!他被用做雅虎消息代理的协调和故障恢复服务。雅虎消息代理是一个高度可扩展的发布-订阅系统,他管理着成千上万台联及程序和信息控制系统。它的吞吐量标准已经达到大约每秒10000基于写操做的工做量。对于读操做的工做量来讲,它的吞吐量标准还要高几倍。
2、ZooKeeper的介绍
2.1 ZooKeeper的概述
Zookeeper 是为分布式应用程序提供高性能协调服务的工具集合,也是Google的Chubby一个开源的实现,是Hadoop 的分布式协调服务。它包含一个简单的原语集5,分布式应用程序能够基于它实现配置维护、命名服务、分布式同步、组服务等。Zookeeper能够用来保证数据在ZK集群之间的数据的事务性一致6。其中ZooKeeper提供通用的分布式锁服务7,用以协调分布式应用。
Zookeeper做为Hadoop项目中的一个子项目,是 Hadoop集群管理的一个必不可少的模块,它主要用来解决分布式应用中常常遇到的数据管理问题,如集群管理、统一命名服务、分布式配置管理、分布式消息队列、分布式锁、分布式协调等。在Hadoop中,它管理Hadoop集群中的NameNode,还有在Hbase中Master Election、Server 之间状态同状步等。
Zoopkeeper 提供了一套很好的分布式集群管理的机制,就是它这种基于层次型的目录树的数据结构,并对树中的节点进行有效管理,从而能够设计出多种多样的分布式的数据管理模型。
2.2 ZooKeeper的设计目标
众所周知,分布式环境下的程序和活动为了达到协调一致目的,一般具备某些共同的特色,例如,简单性、有序性等。ZooKeeper不但在这些目标的实现上有自身特色,而且具备独特优点。下面咱们将简述ZooKeeper的设计目标。
(1)简单化
ZooKeeper容许各分布式进程经过一个共享的命名空间相互联系,该命名空间相似于一个标准的层次型的文件系统:由若干注册了的数据节点构成(用Zookeeper的术语叫znode),这些节点相似于文件和目录。典型的文件系统是基于存储设备的,文传统的文件系统主要用于存储功能,然而ZooKepper的数据是保存在内存中的。也就是说,能够得到高吞吐和低延迟。ZooKeeper的实现很是重视高性能、高可靠,以及严格的有序访问。
高性能保证了ZooKeeper能够用于大型的分布式系统,高可靠保证了ZooKeeper不会发生单点故障,严格的顺序访问保证了客户端能够得到复杂的同步操做原语。
(2)健壮性
就像ZooKeeper须要协调的分布式系统同样,它自己就是具备冗余结构,它构建在一系列主机之上,叫作一个”ensemble”。
构成ZooKeeper服务的各服务器之间必须相互知道,它们维护着一个状态信息的内存映像8,以及在持久化存储中维护着事务日志和快照9。只要大部分服务器正常工做,ZooKeeper服务就能正常工做。
客户端链接到一台ZooKeeper服务器。客户端维护这个TCP链接,经过这个链接,客户端能够发送请求、获得应答,获得监视事件以及发送心跳。若是这个链接断了,客户端能够链接到另外一个ZooKeeper服务器。
(3)有序性
ZooKeeper给每次更新附加一个数字标签,代表ZooKeeper中的事务顺序,后续操做能够利用这个顺序来完成更高层次的抽象功能,例如同步原语7。
(4)速度优点
ZooKeeper特别适合于以读为主要负荷的场合。ZooKeeper能够运行在数千台机器上,若是大部分操做为读,例如读写比例为10:1,ZooKeeper的效率会很高。
2.3 ZooKeeper的集群
ZK集群以下图2.1所示。这是实际应用的一个场景,该ZooKeeper集群当中一共有5台服务器,有两种角色Leader和Follwer,5台服务器连通在一块儿,客户端有分别连在不一样的ZK服务器上。若是当数据经过客户端1,在左边第一台Follower服务器上作了一次数据变动,他会把这个数据的变化同步到其余全部的服务器,同步结束以后,那么其余的客户端都会得到这个数据的变化。
图 2.1
注意:
一般Zookeeper由2n+1台servers组成,每一个server都知道彼此的存在。每一个server都维护的内存状态镜像以及持久化存储的事务日志和快照。为了保证Leader选举能过获得多数的支持,因此ZooKeeper集群的数量通常为奇数。对于2n+1台server,只要有n+1台(大多数)server可用,整个系统保持可用。
2.3.1 集群中的角色
在ZooKeeper集群当中,集群中的服务器角色有两种Leader和Learner,Learner角色又分为Observer和Follower,具体功能以下:
1.领导者(leader),负责进行投票的发起和决议,更新系统状态
2.学习者(learner),包括跟随者(follower)和观察者(observer),
3.follower用于接受客户端请求并向客户端返回结果,在选主过程当中参与投票
4.Observer能够接受客户端请求,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提升读取速度。
5. 客户端(client),请求发起方
ZooKeeper的组件图中给出了ZooKeeper服务的高层次的组件。除了请求处理器(requestprocessor)外,构成ZooKeeper服务的每一个服务器都有一个备份。复制的数据库(replicateddatabase)是一个内存数据库,包含整个数据树。为了可恢复,更新会被log到磁盘,而且在更新这个内存数据库以前,先序列化到磁盘。
每一个ZooKeeper都为客户端提供服务。客户端只链接到一个服务器,并提交请求。读请求直接由本地的复制数据库提供数据。对服务状态进行修改的请求、写请求经过一个约定的协议进行通信。
做为这个协议的一部分,全部的写请求都被传送到一个叫“首领(leader)”的服务器,而其余的服务器,叫作“(随从)followers”,follower从leader接收信息修改的提议,并赞成进行。当leader发生故障时,协议的信息层(messaginglayer)关注leader的替换,并同步到全部的follower。
ZooKeeper采用一个自定义的信息原子操做协议,因为信息层的操做是原子性的,ZooKeeper能保证本地的复制数据库不会产生不一致。当leader接收到一个写请求,它计算出写以后系统的状态,把它变成一个事务。
2.3.2 Zookeeper的读写机制和保证及特色
(1)ZooKeeper的读写机制
Zookeeper是一个由多个server组成的集群
一个leader,多个follower
每一个server保存一份数据副本
全局数据一致
分布式读写
更新请求转发,由leader实施
(2)ZooKeeper的保证
ZooKeeper运行很是快并且简单。虽然它的目标是构建更加复杂服务(例如同步)的基础,但它提供了一些保证,以下:
1.顺序一致性:来自于客户端的更新,根据发送的前后被顺序实施。
2.惟一的系统映像:尽管客户端链接到不一样的服务器,但它们看到的一个惟一(一致性)的系统服务,client不管链接到哪一个server,数据视图都是一致的。
3.可靠性:一旦实施了一个更新,就会一直保持那种状态,直到客户端再次更新它,同时数据更新原子性,一次数据更新要么成功,要么失败。
4.及时性:在一个肯定的时间内,客户端看到的系统状态是最新的。
(3)ZooKeeper特色
最终一致性:client不论链接到哪一个Server,展现给它都是同一个视图,这是zookeeper最重要的性能。
可靠性:具备简单、健壮、良好的性能,若是消息m被一台服务器接受,那么它将被全部的服务器接受。
实时性:Zookeeper保证客户端将在一个时间间隔范围内得到服务器的更新信息,或者服务器失效的信息。 但因为网络延时等缘由,Zookeeper不能保证两个客户端能同时获得刚更新的数据,若是须要最新数据,应该在读数据以前调用sync()接口。
等待无关(wait-free):慢的或者失效的client,不得干预快速的client的请求,使得每一个client都能有效的等待。
原子性:更新只能成功或者失败,没有中间状态。
顺序性:包括全局有序和偏序两种:
全局有序:是指若是在一台服务器上消息a在消息b前发布,则在全部Server上消息a都将在消息b前被发布;
偏序:是指若是一个消息b在消息a后被同一个发送者发布,a必将排在b前面
3、ZooKeeper服务
3.1 ZooKeeper数据模型
ZooKeeper拥有一个层次的命名空间,这个和分布式的文件系统很是类似。不一样的是ZooKeeper命名空间中的Znode,兼具文件和目录两种特色。既像文件同样维护着数据、元信息、ACL、时间戳等数据结构,又像目录同样能够做为路径标识的一部分,并能够具备子znode。用户对znode具备增、删、改、查等操做(权限容许的状况下)。
znode具备原子性操做,每一个znode的数据将被原子性地读写,读操做会读取与znode相关的全部数据,写操做会一次性替换全部数据。zookeeper并无被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,好比分布式应用中的配置文件信息、状态信息、聚集位置等等。这些数据的共同特性就是它们都是很小的数据,一般以KB为大小单位。zooKeeper的服务器和客户端都被设计为严格检查并限制每一个znode的数据大小至多1M,当时常规使用中应该远小于此值。
Zonde由路径标注,ZooKeeper中被表示成有反斜杠分割的Unicode字符串,如同Unix中的文件路径。路径必须是绝对的,所以他们必须由反斜杠来字符开头。除此之外,他们必须是惟一的,也就是说每个路径只有一个表示,所以这些路径不能改变。ZooKeeper的数据结构, 与普通的文件系统极为相似. 见下图:
图中的每一个节点称为一个znode. 每一个znode由3部分组成:
1.stat:此为状态信息, 描述该znode的版本, 权限等信息.
2.data:与该znode关联的数据.
3.children:该znode下的子节点.
3.1.1 ZooKeeper节点Znode
ZooKeeper目录树中每个节点对应一个Znode。每一个Znode维护着一个属性结构,它包含着版本号(dataVersion),时间戳(ctime,mtime)等状态信息。ZooKeeper正是使用节点的这些特性来实现它的某些特定功能。每当Znode的数据改变时,他相应的版本号将会增长。每当客户端检索数据时,它将同时检索数据的版本号。而且若是一个客户端执行了某个节点的更新或删除操做,他也必须提供要被操做的数据版本号。若是所提供的数据版本号与实际不匹配,那么这个操做将会失败。
Znode是客户端访问ZooKeeper的主要实体,它包含如下几个特征:
(1)Watches
客户端能够在节点上设置watch(咱们称之为监视器)。当节点状态发生改变时(数据的增、删、改)将会触发watch所对应的操做。当watch被触发时,ZooKeeper将会向客户端发送且仅发送一条通知,由于watch只能被触发一次。
(2)数据访问
ZooKeeper中的每一个节点存储的数据要被原子性的操做。也就是说读操做将获取与节点相关的全部数据,写操做也将替换掉节点的全部数据。另外,每个节点都拥有本身的ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点能够执行的操做。
(3)节点类型
ZooKeeper中的节点有两种,分别为临时节点和永久节点。节点的类型在建立时即被肯定,而且不能改变。
ZooKeeper的临时节点:该节点的生命周期依赖于建立它们的会话。一旦会话结束,临时节点将被自动删除,固然能够也能够手动删除。另外,须要注意是,ZooKeeper的临时节点不容许拥有子节点。
ZooKeeper的永久节点:该节点的生命周期不依赖于会话,而且只有在客户端显示执行删除操做的时候,他们才能被删除。
(4)顺序节点(惟一性的保证)
当建立Znode的时候,用户能够请求在ZooKeeper的路径结尾添加一个递增的计数。这个计数对于此节点的父节点来讲是惟一的,它的格式为“%10d”(10位数字,没有数值的数位用0补充,例如“0000000001”)。当计数值大于232-1时,计数器将溢出。
org.apache.zookeeper.CreateMode中定义了四种节点类型,分别对应:
PERSISTENT:永久节点
EPHEMERAL:临时节点
PERSISTENT_SEQUENTIAL:永久节点、序列化
EPHEMERAL_SEQUENTIAL:临时节点、序列化
3.1.2 ZooKeeper中的时间
ZooKeeper有多种记录时间的形式,其中包含如下几个主要属性:
(1)Zxid
导致ZooKeeper节点状态改变的每个操做都将使节点接收到一个zxid格式的时间戳,而且这个时间戳全局有序。也就是说,也就是说,每一个对节点的改变都将产生一个惟一的zxid。若是zxid1的值小于zxid2的值,那么zxid1所对应的事件发生在zxid2所对应的事件以前。实际上,ZooKeeper的每一个节点维护者三个zxid值,为别为:cZxid、mZxid、pZxid。
cZxid: 是节点的建立时间所对应的Zxid格式时间戳。
mZxid:是节点的修改时间所对应的Zxid格式时间戳。
实现中zxid是一个64为的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个 新的epoch。低32位是个递增计数。
(2)版本号
对节点的每个操做都将导致这个节点的版本号增长。每一个节点维护着三个版本号,他们分别为:
version 节点数据版本号
cversion 子节点版本号
aversion 节点所拥有的ACL版本号
3.1.3 节点的属性结构
经过前面的介绍,咱们能够了解到,一个节点自身拥有表示其状态的许多重要属性,以下图所示。
3.1.4 Zonde总结
(1)znode中的数据能够有多个版本,在查询该znode数据时就须要带上版本信息。如:set path version / delete path version
(2)znode能够是临时znode,由create -e 生成的节点,一旦建立这个znode的client与server断开链接,该znode将被自动删除。
client和server之间经过heartbeat来确认链接正常,这种状态称之为session,断开链接后session失效。
(3)临时znode不能有子znode。
(4)znode能够自动编号,由create -s 生成的节点,例如在 create -s /app/node 已存在时,将会生成 /app/node00***001节点。
(5)znode能够被监控,该目录下某些信息的修改,例如节点数据、子节点变化等,能够主动通知监控注册的client。事实上,经过这个特性,能够完成许多重要应用,例如配置管理、信息同步、分布式锁等等。
3.2 ZooKeeper服务中的操做
在ZooKeeper中有9个基本操做,以下图所示:
更新ZooKeeper操做是有限制的。delete或setData必须明确要更新的Znode的版本号,咱们能够调用exists找到。若是版本号不匹配,更新将会失败。
更新ZooKeeper操做是非阻塞式的。所以客户端若是失去了一个更新(因为另外一个进程在同时更新这个Znode),他能够在不阻塞其余进程执行的状况下,选择从新尝试或进行其余操做。
尽管ZooKeeper能够被看作是一个文件系统,可是处于便利,摒弃了一些文件系统地操做原语。由于文件很是的小而且使总体读写的,因此不须要打开、关闭或是寻地的操做。
3.2.1 watch触发器
读操做exists、getChildren和getData都被设置了watch,而且这些watch都由写操做来触发:create、delete和setData。ACL操做并不参与到watch中。当watch被触发时,watch事件被生成,他的类型由watch和触发他的操做共同决定。ZooKeeper所管理的watch能够分为两类:
1.数据watch(data watches):getData和exists负责设置数据watch;
2.孩子watch(child watches):getChildren负责设置孩子watch;
咱们能够经过操做返回的数据来设置不一样的watch:
1.getData和exists:返回关于节点的数据信息
2.getChildren:返回孩子列表
所以,一个成功的setData操做将触发Znode的数据watch。
一个成功的create操做将触发Znode的数据watch以及孩子watch。
一个成功的delete操做将触发Znode的数据watch以及孩子watch。
watch由客户端所链接的ZooKeeper服务器在本地维护,所以watch能够很是容易地设置、管理和分派。当客户端链接到一个新的服务器上时,任何的会话事件都将可能触发watch。另外,当从服务器断开链接的时候,watch将不会被接收。可是,当一个客户端从新创建链接的时候,任何先前注册过的watch都会被从新注册。
exists操做上的watch,在被监视的Znode建立、删除或数据更新时被触发。
getData操做上的watch,在被监视的Znode删除或数据更新时被触发。在被建立时不能被触发,由于只有Znode必定存在,getData操做才会成功。
getChildren操做上的watch,在被监视的Znode的子节点建立或删除,或是这个Znode自身被删除时被触发。能够经过查看watch事件类型来区分是Znode仍是他的子节点被删除:NodeDelete表示Znode被删除,NodeDeletedChanged表示子节点被删除。
watch设置操做及相应的触发器如图下图所示:
watch事件包括了事件所涉及的Znode的路径,所以对于NodeCreated和NodeDeleted事件来讲,根据路径就能够简单区分出是哪一个Znode被建立或是被删除了。为了查询在NodeChildrenChanged事件后哪一个子节点被改变了,须要再次调用getChildren来得到新的children列表。一样的,为了查询NodeDeletedChanged事件后产生的新数据,须要调用getData。在两种状况下,Znode可能在获取watch事件或执行读操做这两种状态下切换,在写应用程序时,必须记住这一点。
(1)Zookeeper的watch实际上要处理两类事件:
1. 链接状态事件(type=None, path=null)
这类事件不须要注册,也不须要咱们连续触发,咱们只要处理就好了。
2. 节点事件
节点的创建,删除,数据的修改。它是one time trigger,咱们须要不停的注册触发,还可能发生事件丢失的状况。
上面2类事件都在Watch中处理,也就是重载的process(Event event)
(2)节点事件的触发,经过函数exists,getData或getChildren来处理
这类函数,有双重做用:
1. 注册触发事件
2. 函数自己的功能
函数的自己的功能又能够用异步的回调函数来实现,重载processResult()过程当中处理函数自己的的功能。
函数还能够指定本身的watch,因此每一个函数都有4个版本。根据本身的须要来选择不一样的函数,不一样的版本。
3.3 ZooKeeper访问控制列表ACL
ZooKeeper使用ACL来对Znode进行访问控制。ACL的实现和Unix文件访问许可很是类似:它使用许可位来对一个节点的不一样操做进行容许或禁止的权限控制。可是,和标准的Unix许可不一样的是,Zookeeper对于用户类别的区分,不止局限于全部者(owner)、组 (group)、全部人(world)三个级别。Zookeeper中,数据节点没有“全部者”的概念。访问者利用id标识本身的身份,并得到与之相应的 不一样的访问权限。
注意:
传统的文件系统中,ACL分为两个维度,一个是属组,一个是权限,子目录/文件默认继承父目录的ACL。而在Zookeeper中一个ACL和一个ZooKeeper节点相对应。而且,父节点的ACL与子节点的ACL是相互独立的。也就是说,ACL不能被子节点所继承,父节点所拥有的权限与子节点所用的权限都没有任何关系。
Zookeeper支持可配置的认证机制。它利用一个三元组来定义客户端的访问权限:(scheme:expression, perms) 。其中:
1.scheme:定义了expression的含义。
如:(host:host1.corp.com,READ),标识了一个名为host1.corp.com的主机,有该数据节点的读权限。
2.Perms:标识了操做权限。
如:(ip:19.22.0.0/16, READ),表示IP地址以19.22开头的主机,有该数据节点的读权限。
Zookeeper的ACL也能够从三个维度来理解:一是,scheme; 二是,user; 三是,permission,一般表示为scheme:id:permissions,以下图所示。
1.world : id格式:anyone。
如:world:anyone表明任何人,zookeeper中对全部人有权限的结点就是属于world:anyone的。
2.auth : 它不须要id。
注:只要是经过authentication的user都有权限,zookeeper支持经过kerberos来进行认证, 也支持username/password形式的认证。
3.digest: id格式:username:BASE64(SHA1(password))。
它须要先经过username:password形式的authentication。
4.ip: id格式:客户机的IP地址。
设置的时候能够设置一个ip段。如:ip:192.168.1.0/16, 表示匹配前16个bit的IP段
5.super: 超级用户模式。
在这种scheme状况下,对应的id拥有超级权限,能够作任何事情
ZooKeeper权限定义以下图所示:
ZooKeeper内置的ACL模式以下图所示:
当会话创建的时候,客户端将会进行自我验证。另外,ZooKeeper Java API支持三种标准的用户权限,它们分别为:
1.ZOO_PEN_ACL_UNSAFE:对于全部的ACL来讲都是彻底开放的,任何应用程序能够在节点上执行任何操做,好比建立、列出并删除子节点。
2.ZOO_READ_ACL_UNSAFE:对于任意的应用程序来讲,仅仅具备读权限。
3.ZOO_CREATOR_ALL_ACL:授予节点建立者全部权限。须要注意的是,设置此权限以前,建立者必须已经通了服务器的认证。
下面演示一个经过digest(用户名密码的方式)为建立的节点设置ACL的例子,代码以下:
import org.apache.zookeeper.*; import org.apache.zookeeper.server.auth.DigestAuthenticationProvider; import org.apache.zookeeper.data.*; import java.util.*; public class NewDigest { public static void main(String[] args) throws Exception {//new一个acl List acls = new ArrayList(); //添加第一个id,采用用户名密码形式 Id id1 = new Id("digest",DigestAuthenticationProvider.generateDigest("admin:admin")); ACL acl1 = new ACL(ZooDefs.Perms.ALL, id1); acls.add(acl1); //添加第二个id,全部用户可读权限 Id id2 = new Id("world", "anyone"); ACL acl2 = new ACL(ZooDefs.Perms.READ, id2); acls.add(acl2); // zk用admin认证,建立/test ZNode。 ZooKeeper zk = new ZooKeeper("host1:2181,host2:2181,host3:2181",2000, null); zk.addAuthInfo("digest", "admin:admin".getBytes()); zk.create("/test", "data".getBytes(), acls, CreateMode.PERSISTENT); } } |
3.4 ZooKeeper的执行
ZooKeeper服务能够以两种模式运行。在单机模式下,只有一个ZooKeeper服务器,便于用来测试。可是他没有高可用性和恢复性的保障。在工业界,ZooKeeper以复合模式10运行在一组叫ensemble的集群上。ZooKeeper经过复制来得到高可用性,同时,只要ensemble中大部分机器运做,就能够提供服务。在2n+1个节点的ensemble中,能够承受n台机器故障。
ZooKeeper的思想很是简单:他所须要作的就是保证对Znode树的每一次修改都复制到ensemble中的大部分机器上去。若是机器中的小部分出故障了,那么至少有一台机器将会恢复到最新状态,其余的则保存这副本,直到最终达到最新状态。Zookeeper采用Zab协议,它分为两个阶段,而且可能被无限的重复。
(1)阶段1:领导者选举
在ensemble中的机器要参与一个选择特殊成员的进程,这个成员叫领导者,其余机器脚跟随者。在大部分的跟随者与他们的领导者同步了状态之后,这个阶段才算完成。
(2)阶段2:原子广播
全部的写操做请求被传送给领导者,并经过广播将更新信息告诉跟随者。当大部分跟随者执行了修改以后,领导者就提交更新操做,客户端将获得更新成功的回应。未得到一致性的协议被设计为原子的,所以不管修改失败与否,他都分两阶段提交。
若是领导者出故障了,城下的机器将会再次进行领导者选举,并在新领导被选出前继续执行任务。若是在不久后老的领导者恢复了,那么它将以跟随者的身份继续运行。领导者选举很是快,由发布的结果所知,大约是200毫秒,所以在选举是性能不会明显减慢。
全部在ensemble中的机器在更新它们内存中的Znode树以前会先将更新信息写入磁盘。读操做请求可由任何机器服务,同时,因为他们只涉及内存查找,所以很是快。
3.5 ZooKeeper一致性
在ensemble中的领导者和跟随着很是灵活,跟随者经过更新号来滞后领导者11,结果致使了只要大部分而不是全部的ensemble中的元素确认更新,就能被提交了。对于ZooKeeper来讲,一个较好的智能模式是将客户端链接到跟着领导者的ZooKeeper服务器上。客户端可能被链接到领导者上,但他不能控制它,并且在以下状况时,甚至可能不知道。参见下图:
每个Znode树的更新都会给定一个惟一的全局标识,叫zxid(表示ZooKeeper事务“ID”)。更新是被排序的,所以若是zxid的z1<z2,那么z1就比z2先执行。对于ZooKeeper来讲,这是分布式系统中排序的惟一标准。
ZooKeeper是一种高性能、可扩展的服务。ZooKeeper的读写速度很是快,而且读的速度要比写快。另外,在进行读操做的时候,ZooKeeper依然可以为旧的数据提供服务。这些都是由ZooKeeper所提供的一致性保证的,它具备以下特色:
(1)顺序一致性
任何一个客户端的更新都按他们发送的顺序排序,也就意味着若是一个客户端将Znode z的值更新为值a,那么在以后的操做中,他会将z更新为b,在客户端发现z带有值b以后,就不会再看见带有值a的z。
(2)原子性
更新不成功就失败,这意味着若是更新失败了,没有客户端会知道。☆☆
(3)单系统映像☆
不管客户端链接的是哪台服务器,他与系统看见的视图同样。这就意味着,若是一个客户端在相同的会话时链接了一台新的服务器,他将不会再看见比在以前服务器上看见的更老的系统状态,当服务器系统出故障,同时客户端尝试链接ensemble中的其余机器时,故障服务器的后面那台机器将不会接受链接,直到它链接到故障服务器。
(4)容错性☆☆☆
一旦更新成功后,那么在客户端再次更新他以前,他就固定了,将再也不被修改,这就会保证产生下面两种结果:
若是客户端成功的得到了正确的返回代码,那么说明更新已经成功。若是不可以得到返回代码(因为通讯错误、超时等缘由),那么客户端将不知道更新是否生效。
当故障恢复的时候,任何客户端可以看到的执行成功的更新操做将不会回滚。
(5)实时性☆☆
在任何客户端的系统视图上的的时间间隔是有限的,所以他在超过几十秒的时间内部会过时。这就意味着,服务器不会让客户端看一些过期的数据,而是关闭,强制客户端转到一个更新的服务器上。
解释一下:
因为性能缘由,读操做由ZooKeeper服务器的内存提供,并且不参与写操做的全局排序。这一特性可能会致使来自使用ZooKeeper外部机制交流的客户端与ZooKeeper状态的不一致。举例来讲,客户端A将Znode z的值a更新为a',A让B来读z,B读到z的值是a而不是a’。这与ZooKeeper的保证机制是相容的(不容许的状况较做“同步一致的交叉客户端视 图”)。为了不这种状况的发生,B在读取z的值以前,应该先调用z上的sync。Sync操做强制B链接上的ZooKeeper服务器与leader保 持一致这样,当B读到z的值时,他将成为A设置的值(或是以后的值)
容易混淆的是:
sync操做只能被异步调用12。这样操做的缘由是你不须要等待他的返回,由于ZooKeeper保证了任何接下去的操做将会发生在sync在服务器上执行之后,即便操做是在sync完成前被调用的。
这些已执行的保证后,ZooKeeper更高级功能的设计与实现将会变得很是容易,例如:leader选举、队列,以及可撤销锁等机制的实现。
3.6 ZooKeeper会话
ZooKeeper客户端与ensemble中的服务器列表配置一致,在启动时,他尝试与表中的一个服务器相链接。若是链接失败了,他就尝试表中的其余服务器,以此类推,知道他最终链接到其中一个,或者ZooKeeper的全部服务器都没法得到时,链接失败。
一旦与ZooKeeper服务器链接成功,服务器会建立与客户端的一个新的对话。每一个回话都有超时时段,这是应用程序在建立它时设定的。若是服务器没有在超时时段内获得请求,他可能会中断这个会话。一旦会话被中断了,他可能再也不被打开,并且任何与会话相链接的临时节点都将丢失。
不管何时会话持续空闲长达必定时间,都会由客户端发送ping请求保持活跃(犹如心跳)。时间段要足够小以监测服务器故障(由读操做超时反应),而且能再回话超市时间段内从新链接到另外一个服务器。
在ZooKeeper中有几个time参数。tick time是ZooKeeper中的基本时间长度,为ensemble里的服务器所使用,用来定义对于交互运行的调度。其余设置以tick time的名义定义,或者至少由它来约束。
建立更复杂的临时性状态的应用程序应该支持更长的会话超时,由于从新构建的代价会更昂贵。在一些状况下,咱们可让应用程序在必定会话时间内可以重启,而且避免会话过时。(这可能更适合执行维护或是升级)每一个会话都由服务器给定一个惟一的身份和密码,并且若是是在创建链接时被传递给ZooKeeper的话,只要没有过时它可以恢复会话。
这些特性能够视为一种能够避免会话过时的优化,但它并不能代替用来处理会话过时。会话过时可能出如今机器忽然故障时,或是因为任何缘由致使的应用程序安全关闭了,但在会话中断前没有重启。
3.7 ZooKeeper实例状态
Zookeeper对象的转变是经过其生命周期中的不一样状态来实现。可使用getState()方法在任什么时候候去查询他的状态:
public states getState() |
Zookeeper状态事务,如图3.5所示
图 3.5 Zookeeper状态事务
getState()方法的返回类型是states,states是枚举类型表明Zookeeper对象可能所处的不一样状态,一个Zookeeper实例可能一次只处于一个状态。一个新建的Zookeeper实例正在于Zookeeper服务器创建链接时,是处于CONNECTING状态的。一旦链接创建好之后,他就变成了Connected状态。
使用Zookeeper的客户端能够经过注册Watcher的方法来获取状态转变的消息。一旦进入了CONNNECTED状态,Watcher将得到一个KeepState值为SyncConnected的WatchedEvent。
注意Zookeeper的watcher有两个职责:
<1>了解Zookeeper的状态改变。传递给ZooKeeper对象构造函数的(默认)watcher,被用来监测状态的改变。
<2>了解Zonde的改变。监测Zonde的改变既可使用专门的实例设置到读操做上,也可使用读操做的默认watcher。
Zookeeper实例可能失去或从新链接Zookeeper服务,在CONNECTED和CONNECTING状态中切换。若是链接断开,watcher获得一个Disconnected事件。学要注意的是,这些状态的迁移是由Zookeeper实例本身发起的,若是链接断开他将自动尝试自动链接。
若是任何一个close()方法被调用,或是会话由Expired类型的KeepState提示过时时,ZooKeeper可能会转变成第三种状态CLOSED。一旦处于CLOSED状态,Zookeeper对象将再也不是活动的了(可使用states的isActive()方法进行测试),并且不能被重用。客户端必须创建一个新的Zookeeper实例才能从新链接到Zookeeper服务。
下期预告:ZooKeeper的集群安装和配置,敬请关注。本期内容及供你们参考,有什么不对的地方,但愿你们给予指点更正。若是您以为,文章写得还行,就抬起您的贵手,点一下推荐