| 
                             
| 
     
                                    | 编辑推荐: |   
                                    | 本文主要讲解了ZooKeeper是什么,它的角色及架构,ZooKeeper数据模型Znode,ZooKeeper服务中操作,Zookeeper下载安装与配置和命令相关。 来自于博客园,,由火龙果软件Anna编辑、推荐。
 |  |  ZooKeeper介绍 ZooKeeper是一种为分布式应用所设计的高可用、高性能且一致的开源协调服务,是Google的Chubby一个开源的实现。 提供功能: 命名服务 配置管理 集群管理 分布式锁 队列管理 特性: 顺序一致性:从同一个客户端发起的事务请求,最终将会严格按照其发起顺序被应用到ZooKeeper中。 原子性:所有事务请求的结果在集群中所有机器上的应用情况是一致的,也就是说要么整个集群所有集群都成功应用了某一个事务,要么都没有应用,一定不会出现集群中部分机器应用了该事务,而另外一部分没有应用的情况。 单一视图:无论客户端连接的是哪个ZooKeeper服务器,其看到的服务端数据模型都是一致的。 可靠性:一旦服务端成功地应用了一个事务,并完成对客户端的响应,那么该事务所引起的服务端状态变更将会被一直保留下来,除非有另一个事务又对其进行了变更。 实时性:通常人们看到实时性的第一反应是,一旦一个事务被成功应用,那么客户端能够立即从服务端上读取到这个事务变更后的最新数据状态。这里需要注意的是,ZooKeeper仅仅保证在一定的时间段内,客户端最终一定能够从服务端上读取到最新的数据状态。 Zookeeper的角色 领导者(leader):负责进行投票的发起和决议,更新系统状态,一个ZooKeeper集群同一时刻只会有一个Leader,其他都是Follower或Observer。 学习者(learner):包括跟随者(follower)和观察者(observer)。 follower用于接受客户端请求并向客户端返回结果,在选主过程中参与投票。 Observer可以接受客户端连接,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度。 客户端(client),请求发起方。 注意:ZooKeeper默认只有Leader和Follower两种角色,没有Observer角色。为了使用Observer模式,在任何想变成Observer的节点的配置文件中加入:peerType=observer,并在所有server的配置文件中,配置成observer模式的server的那行配置追加:observer,例如:server.1:localhost:2888:3888:observer Follower和Observer区别:Follower和Observer都能提供读服务,不能提供写服务。两者唯一的区别在于,Observer机器不参与Leader选举过程,也不参与写操作的『过半写成功』策略,因此Observer可以在不影响写性能的情况下提升集群的读性能。 Zookeeper架构 
 zookeeper是一个由多个service(节点)组成的集群,一个leader,多个follower,每个server保存一份数据部分,全局数据一致,分布式读写,客户端更新请求由leader转发实施。 更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行,数据更新原子性,一次数据更新要么成功,要么失败,全局唯一数据试图,client无论连接到哪个server,数据视图是一致的。 zookeeper的核心是原子广播,这个机制保证了各个server之间的同步,实现这个机制的协议叫做Zab协议.Zab协议有两种模式,他们分别是恢复模式和广播模式。 (1)当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导着被选举出来,且大多数server都完成了和leader的状态同步后,恢复模式就结束了.状态同步保证了leader和server具有相同的系统状态。 (2)一旦leader已经和多数的follower进行了状态同步后,他就可以开始广播消息了,即进入广播状态.这时候当一个server加入zookeeper服务中,它会在恢复模式下启动,发下leader,并和leader进行状态同步,待到同步结束,它也参与广播消息。 ZooKeeper数据模型Znode (1) 节点 分布式的时候,一般『节点』指的是组成集群的每一台机器。而ZooKeeper中的数据节点是指数据模型中的数据单元,称为ZNode。 (2) ZooKeeper节点属性 通过前面的介绍,我们可以了解到,一个节点自身拥有表示其状态的许多重要属性,如下图所示。 Znode节点属性结构: 
 (3) 数据模型结构 ZooKeeper将所有节点数据存储在内存中,数据模型是一棵树(ZNode Tree),由斜杠(/)进行分割的路径,就是一个ZNode,如/hbase/master,其中hbase和master都是ZNode。每个ZNode上都会保存自己的数据内容,同时会保存一系列属性信息。ZooKeeper拥有一个层次的命名空间,这个和标准的文件系统非常相似,如下图3.1 
                            所示。 ZooKeeper数据模型与文件系统目录树: 
 
 (4) 引用方式 Zonde通过路径引用,如同Unix中的文件路径。路径必须是绝对的,因此他们必须由斜杠字符来开头。除此以外,他们必须是唯一的,也就是说每一个路径只有一个表示,因此这些路径不能改变。在ZooKeeper中,路径由Unicode字符串组成,并且有一些限制。字符串"/zookeeper"用以保存管理信息,比如关键配额信息。 (5) Znode结构 ZooKeeper命名空间中的Znode,兼具文件和目录两种特点。既像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分。图中的每个节点称为一个Znode。 
                            每个Znode由3部分组成: ① stat:此为状态信息, 描述该Znode的版本, 权限等信息 ② data:与该Znode关联的数据 ③ children:该Znode下的子节点 ZooKeeper虽然可以关联一些数据,但并没有被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,比如分布式应用中的配置文件信息、状态信息、汇集位置等等。这些数据的共同特性就是它们都是很小的数据,通常以KB为大小单位。ZooKeeper的服务器和客户端都被设计为严格检查并限制每个Znode的数据大小至多1M,但常规使用中应该远小于此值。 (6) 数据访问 ZooKeeper中的每个节点存储的数据要被原子性的操作。也就是说读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。另外,每一个节点都拥有自己的ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的操作。 (7) 节点类型 ZooKeeper中的节点有两种,分别为临时节点和永久节点。节点的类型在创建时即被确定,并且不能改变。 ① 临时节点:该节点的生命周期依赖于创建它们的会话。一旦会话(Session)结束,临时节点将被自动删除,当然可以也可以手动删除。虽然每个临时的Znode都会绑定到一个客户端会话,但他们对所有的客户端还是可见的。另外,ZooKeeper的临时节点不允许拥有子节点。 ② 永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。 (8) 顺序节点 当创建Znode的时候,用户可以请求在ZooKeeper的路径结尾添加一个递增的计数。这个计数对于此节点的父节点来说是唯一的,它的格式为"%10d"(10位数字,没有数值的数位用0补充,例如"0000000001")。当计数值大于232-1时,计数器将溢出。 (9) 观察 客户端可以在节点上设置watch,我们称之为监视器。当节点状态发生改变时(Znode的增、删、改)将会触发watch所对应的操作。当watch被触发时,ZooKeeper将会向客户端发送且仅发送一条通知,因为watch只能被触发一次,这样可以减少网络流量。 ZooKeeper服务中操作 在ZooKeeper中有9个基本操作,如下图所示: ZooKeeper类方法描述 
 更新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):getData和exists负责设置数据watch ② 孩子watch(child watches):getChildren负责设置孩子watch 我们可以通过操作返回的数据来设置不同的watch: ① getData和exists:返回关于节点的数据信息 ② getChildren:返回孩子列表 因此 ① 一个成功的setData操作将触发Znode的数据watch ② 一个成功的create操作将触发Znode的数据watch以及孩子watch ③ 一个成功的delete操作将触发Znode的数据watch以及孩子watch (3) watch注册与处触发 图 6.1 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下载安装与配置 Zookeeper下载安装 从ZooKeeper官网下载 下载地址 解压配置 
                             
                              | tar -xf /usr/local/src/zookeeper- 
                                3.4.9.tar.gz -C /usr/local/src/ ln -sv /usr/local/src/zookeeper- 3.4.9/ /usr/local/zookeeper
 cd /usr/local/zookeeper/
 |      3.配置ZooKeeper vim zoo.cfg 
                             
                              | # zoo.cfg文件中内容如下 tickTime=2000
 dataDir=/var/lib/zookeeper
 clientPort=2181
 |    tickTime 单位为微秒,用于session注册和客户端和ZooKeeper服务的心跳周期。session超时时长最小为 
                            tickTime的两倍。 dataDir ZooKeeper的状态存储位置,看名字就知道书数据目录。在你的系统中检查这个目录是否存在,如果不存在手动创建,并且给予可写权限。 clientPort 客户端连接的端口。不同的服务器可以设置不同的监听端口,默认是2181。 4.启动ZooKeeper 
                             
                              | # 这里命令写的长是为了便于知道ZooKeeper是如何使用配置文件的。 /usr/local/zookeeper/bin/zkServer.sh start /usr/local/zookeeper/conf/zoo.cfg
 
 # 查看ZooKeeper是否运行
 ps –ef | grep zookeeper
 # 也可以使用jps ,可以看到java进程中有QuorumPeerMain列出来。
 
 # 查看ZooKeeper的状态
 zkServer.sh status
 
 # 常用的ZooKeeper用法,这个属于Linux基础的部分,就不过多说明了
 ./zkServer.sh {start|start-foreground|stop|restart|status|upgrade|print-cmd}
 |  5.使用zkCli连接ZooKeeper 
                             
                              | /usr/local/zookeeper/bin/zkCli.sh 
                                 -server localhost:218 |  集群配置 1.创建配置文件 
                             
                              | cd /usr/local/zookeeper touch zoo1.cfg zoo2.cfg zoo3.cfg
 |  
  注意 端口不要冲突,dataDir不要相同。 2.配置数据目录与数据存放目录内容 
                             
                              | mkdir {zoo1,zoo2,zoo3} echo 1 > zoo1/myid
 echo 2 > zoo2/myid
 echo 3 > zoo3/myid
 |  注意:这里的myid文件中一定要对应上面配置文件中server.[id]的数字,不然ZooKeeper启动会出错。 3.启动Zookeeper 
                             
                              | zkServer.sh start 
                                 /usr/local/zookeeper/conf/zoo1.cfg zkServer.sh start  /usr/local/zookeeper/conf/zoo2.cfg
 zkServer.sh start  /usr/local/zookeeper/conf/zoo3.cfg
 |  4.查看效果 使用ps -ef | grep zoo可以看到有三个zookeeper启动起来了。 连接ZooKeeper 
                             
                              | # 192.168.8.250是ZooKeeper服务器的地址 zkCli.sh -server 192.168.8.250:2181, 192.168.8.250: 
                                2182, 192.168.8.250:2183
 |  Zookeeper命令篇 连接远程Server:zkCli.sh –server <ip>:<port> 比如连接到本地Zoopker服务: ./zkCli.sh -server localhost:2181 查看节点数据:ls <path>,比如ls / 则查看根目录节点数据 查看某个服务Service的提供者 ls 服务名/providers 查看节点数据并能看到更新次数等数据:ls2 <path>,输出字段含义如下: cZxid:创建节点的事务id ctime:创建节点的时间 mZxid:修改节点的事务id mtime:修改节点的时间 pZxid:子节点列表最后一次修改的事务id。删除或添加子节点,不包含修改子节点的数据 cversion:子节点的版本号,删除或添加子节点,版本号会自增 dataVersion:节点数据版本号,数据写入操作,版本号会递增 aclVersion:节点ACL权限版本,权限写入操作,版本号会递增 ephemeralOwner:临时节点创建时的事务id,如果节点是永久节点,则它的值为0 dataLength:节点数据长度(单位:byte),中文占3个byte numChildren:子节点数量 创建节点:create <path> <data> 获取节点,包含数据和更新次数等数据:get <path> 修改节点:set <path> <data> 删除节点:delete <path>,如果有子节点存在则删除失败 |