[转]ZooKeeper的学习与应用java
http://blog.csdn.net/rengq126/article/details/7393227node
1. ZooKeeper的学习与应用程序员
1.1. 概述算法
ZooKeeper是Apache在不少云计算项目中的一个,与Hadoop密切相关,这种状况致使我一开始认为ZooKeeper的搭建须要Hadoop项目做为支持,可是最后发现彻底不须要,它是能够单独运行的一个项目。shell
在网上看到了一个很不错的关于ZooKeeper的介绍: 顾名思义动物园管理员,他是拿来管大象(Hadoop) 、 蜜蜂(Hive) 、 小猪(Pig) 的管理员, Apache Hbase和 Apache Solr 以及LinkedIn sensei 等项目中都采用到了 Zookeeper。ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,ZooKeeper是以Fast Paxos算法为基础,实现同步服务,配置维护和命名服务等分布式应用。数据库
从介绍能够看出,ZooKeeper更倾向于对大型应用的协同维护管理工做。IBM则给出了IBM对ZooKeeper的认知: Zookeeper 分布式服务框架是 Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中常常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。express
总之,我认为它的核心词就是一个单词,协调。apache
1.2. ZooKeeper的特征缓存
在Hadoop权威指南中看到了关于ZooKeeper的一些核心特征,阅读以后感受总结的甚是精辟,在这里引用并总结。服务器
1.2.1. 简易
ZooKeeper的最重要核心就是一个精简文件系统,提供一些简单的操做以及附加的抽象(例如排序和通知)。
1.2.2. 易表达
ZooKeeper的原型是一个丰富的集合,它们是一些已建好的块,能够用来构建大型的协做数据结构和协议,例如:分布式队列、分布式锁以及一组对等体的选举。
1.2.3. 高可用性
ZooKeeper运行在一些集群上,被设计成可用性较高的,所以应用程序能够依赖它。ZooKeeper能够帮助你的系统避免单点故障,从而创建一个可靠的应用程序。
1.2.4. 松散耦合
ZooKeeper的交互支持参与者之间并不了解对方。例如:ZooKeeper能够被当作一种公共的机制,使得进程彼此不知道对方的存在也能够相互发现而且交互,对等方可能甚至不是同步的。
这一特色我感受最能体如今集群的部署启动过程当中。像Hadoop当把配置文件写好以后,而后运行启动脚本,则251,241,242中做为集群的虚拟机是同步启动的,也就是DataNode,NameNode,TaskTracker,以及JobTracker的启动并运行时在一次启动过程当中启动的,就是运行一次启动脚本文件,则都启动起来。可是ZooKeeper的启动过程却不是这样的。我在251,241,242部署了ZooKeeper集群,并进行启动,则启动的过程是这样的:首先ssh到251而后启动,这时候251的集群节点启动起来,可是控制台一直报错,大概的含义就是没有检测到其余两个结点。接着分别ssh到241,242,分别启动集群中的剩下的结点,当241启动起来时,回到251查看,发现报错的信息减小,意思是只差一个结点。当251,241,242三台服务器的结点所有启动起来,则三台的服务器的控制台打印出正常的信息。
1.2.5. ZooKeeper是一个库
ZooKeeper提供了一个开源的、共享的执行存储,以及通用协做的方法,分担了每一个程序员写通用协议的负担。随着时间的推移,人们能够增长和改进这个库来知足本身的需求。
1.3. Zookeeper基本知识
在这一小结,我介绍关于ZooKeeper的一些基本理论知识,以便对ZooKeeper有一个基本感性的认识吧,因为学习的时间不长,有些的认识多是比较片面的,以后若是有了更深层次的认识,会补充于以后的月总结中。
1.3.1. 层次化的名字空间
ZooKeeper的整个名字空间的结构是层次化的,和通常的Linux文件系统结构很是类似,一颗很大的树。这也就是ZooKeeper的数据结构状况。名字空间的层次由斜杠/来进行分割,在名称空间里面的每个结点的名字空间惟一由这个结点的路径来肯定。
图3.1 ZooKeeper的层次化名字空间
每个节点拥有自身的一些信息,包括:数据、数据长度、建立时间、修改时间等等。从这样一类既含有数据,又做为路径表标示的节点的特色中,能够看出,ZooKeeper的节点既能够被看作是一个文件,又能够被看作是一个目录,它同时具备两者的特色。为了便于表达,从此咱们将使用Znode来表示所讨论的ZooKeeper节点。
1.3.2. Znode
Znode维护着数据、ACL(access control list,访问控制列表)、时间戳等交换版本号等数据结构,它经过对这些数据的管理来让缓存生效而且令协调更新。每当Znode中的数据更新后它所维护的版本号将增长,这很是相似于数据库中计数器时间戳的操做方式。
另外Znode还具备原子性操做的特色:命名空间中,每个Znode的数据将被原子地读写。读操做将读取与Znode相关的全部数据,写操做将替换掉全部的数据。除此以外,每个节点都有一个访问控制列表,这个访问控制列表规定了用户操做的权限。
ZooKeeper中一样存在临时节点。这些节点与session同时存在,当session生命周期结束,这些临时节点也将被删除。临时节点在某些场合也发挥着很是重要的做用。
1.3.3. Watch机制
Watch机制就和单词自己的意思同样,看。看什么?具体来说就是某一个或者一些Znode的变化。官方给出的定义:一个Watch事件是一个一次性的触发器,当被设置了Watch的数据发生了改变的时候,则服务器将这个改变发送给设置了Watch的客户端,以便通知它们。
Watch机制主要有如下三个特色:
1 一次性的触发器(one-time trigger)
当数据改变的时候,那么一个Watch事件会产生而且被发送到客户端中。可是客户端只会收到一次这样的通知,若是之后这个数据再次发生改变的时候,以前设置Watch的客户端将不会再次收到改变的通知,由于Watch机制规定了它是一个一次性的触发器。
2 发送给客户端
这个代表了Watch的通知事件是从服务器发送给客户端的,是异步的,这就代表不一样的客户端收到的Watch的时间可能不一样,可是ZooKeeper有保证:当一个客户端在看到Watch事件以前是不会看到结点数据的变化的。例如:A=3,此时在上面设置了一次Watch,若是A忽然变成4了,那么客户端会先收到Watch事件的通知,而后才会看到A=4。
3被设置Watch的数据
这代表了一个结点能够变换的不一样方式。一个Znode变化方式有两种,结点自己数据的变化以及结点孩子的变化。所以Watch也能够设置为这个Znode的结点数据,固然也能够设置为Znode结点孩子。
1.3.4. ACL访问控制列表
这是另一个和Linux操做系统很是类似的地方,ZooKeeper使用ACL来控制对旗下Znode结点们的访问。ACL的实现和Linux文件系统的访问权限十分相似:它经过设置权限为来代表是否容许对一个结点的相关内容的改变。
可是与传统Linux机制不太相同,一个结点的数据没有相似“拥有者,组用户,其余用户”的概念,在ZooKeeper中,ACL经过设置ID以及与其关联的权限来完成访问控制的。ACL的权限组成语法是:
(scheme:expression, perms)
前者代表设置的ID,逗号后面表示的是ID相关的权限,例如:
(ip:172.16.16.1, READ)
指明了IP地址为如上的用户的权限为只读。
如下列举如下ACL所具备的权限
CREATE:代表你能够建立一个Znode的子结点。
READ:你能够获得这个结点的数据以及列举该结点的子结点状况。
WRITE:设置一个结点的数据。
DELETE:能够删除一个结点
ADMIN:对一个结点设置权限。
1.4. ZooKeeper的部署以及简单使用
要想使用ZooKeeper,首先就要把它部署在服务器上跑起来,就想Apache,Tomcat,FtpServer等服务器同样。ZooKeeper的部署方式主要有三种,单机模式、伪集群模式、集群模式。其实剩下的两种模式都是集群模式的特殊状况。
1.4.1. 基本的环境变量配置
Java大型的项目中,环境变量的配置很重要,若是没有很好的配置环境变量的话,甚至项目连启动都是难事。
export ZOOKEEPER_HOME=/home/zookeeper-3.3.3
export PATH=$PATH:$ZOOKEEPER_HOME/bin:$ZOOKEEPER_HOME/conf
1.4.2. ZooKeeper的单机模式部署
ZooKeeper的单机模式一般是用来快速测试客户端应用程序的,在实际过程当中不多是单机模式。单机模式的配置也比较简单。
l 编写配置文件zoo.cfg
zookeeper-3.3.3/conf文件夹下面就是要编写配置文件的位置了。在文件夹下面新建一个文件zoo.cfg。ZooKeeper的运行默认是读取zoo.cfg文件里面的内容的。如下是一个最简单的配置文件的样例:
tickTime=2000
dataDir=/var/zookeeper
clientPort=2181
在这个文件中,咱们须要指定 dataDir 的值,它指向了一个目录,这个目录在开始的时候须要为空。下面是每一个参数的含义:
tickTime :基本事件单元,以毫秒为单位。这个时间是做为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每一个 tickTime 时间就会发送一个心跳。
dataDir :存储内存中数据库快照的位置,顾名思义就是 Zookeeper 保存数据的目录,默认状况下,Zookeeper 将写数据的日志文件也保存在这个目录里。
clientPort :这个端口就是客户端链接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。
使用单机模式时用户须要注意:这种配置方式下没有 ZooKeeper 副本,因此若是 ZooKeeper 服务器出现故障, ZooKeeper 服务将会中止。
l 执行运行脚本
在zookeeper-3.3.3/bin文件夹下面运行zkServer.sh便可,运行完毕以后则ZooKeeper服务变启动起来。
./zkServer.sh start
脚本默认调用zoo.cfg里面的配置,所以程序正常启动。
1.4.3. ZooKeeper的集群模式部署
ZooKeeper的集群模式下,多个Zookeeper服务器在工做前会选举出一个Leader,在接下来的工做中这个被选举出来的Leader死了,而剩下的Zookeeper服务器会知道这个Leader死掉了,在活着的Zookeeper集群中会继续选出一个Leader,选举出Leader的目的是为了能够在分布式的环境中保证数据的一致性。如图所示:
图3.2 ZooKeeper集群模式图
l 确认集群服务器的数量
因为ZooKeeper集群中,会有一个Leader负责管理和协调其余集群服务器,所以服务器的数量一般都是单数,例如3,5,7...等,这样2n+1的数量的服务器就能够容许最多n台服务器的失效。
l 编写配置文件
配置文件须要在每台服务器中都要编写,如下是一个配置文件的样本:
# Filename zoo.cfg
tickTime=2000
dataDir=/var/zookeeper/
clientPort=2181
initLimit=5
syncLimit=2
server.1=202.115.36.251:2888:3888
server.2=202.115.36.241:2888:3888
server.3=202.115.36.242:2888:3888
initLimit:这个配置项是用来配置 Zookeeper 接受客户端(这里所说的客户端不是用户链接 Zookeeper 服务器的客户端,而是 Zookeeper 服务器集群中链接到 Leader 的 Follower 服务器)初始化链接时最长能忍受多少个心跳时间间隔数。当已经超过 10 个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器尚未收到客户端的返回信息,那么代表这个客户端链接失败。总的时间长度就是 5*2000=10 秒。
syncLimit:这个配置项标识 Leader 与 Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是 2*2000=4 秒
server.A=B:C:D:其中 A 是一个数字,表示这个是第几号服务器;B 是这个服务器的 ip 地址;C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口;D 表示的是万一集群中的 Leader 服务器挂了,须要一个端口来从新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通讯的端口。若是是伪集群的配置方式,因为 B 都是同样,因此不一样的 Zookeeper 实例通讯端口号不能同样,因此要给它们分配不一样的端口号。
l 建立myid文件
除了修改 zoo.cfg 配置文件,集群模式下还要配置一个文件 myid,这个文件在 dataDir 目录下,这个文件里面就只有一个数据就是 A 的值,Zookeeper 启动时会读取这个文件,拿到里面的数据与 zoo.cfg 里面的配置信息比较从而判断究竟是那个 server。
l 执行运行脚本
和单机模式下的运行方式基本相同,值得注意的地方就是要分别在不一样服务器上执行一次,例如分别在251,241,242上运行:
./zkServer.sh start
这样才能使得整个集群启动起来。
1.4.4. ZooKeeper的集群伪分布
其实在企业中式不会存在的,另外为了测试一个客户端程序也没有必要存在,只有在物质条件比较匮乏的条件下才会存在的模式。
集群伪分布模式就是在单机下模拟集群的ZooKeeper服务,在一台机器上面有多个ZooKeeper的JVM同时运行。
l 确认集群伪服务器的数量
2n+1,和以前的集群分布相同。
l 编写配置文件
在/conf文件夹新建三个配置文件,zoo1.cfg,zoo2.cfg以及zoo3.cfg。配置文件分别以下编写:
Zoo1.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/root/hadoop-0.20.2/zookeeper-3.3.1/d_1
clientPort=2181
server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889
Zoo2.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/root/hadoop-0.20.2/zookeeper-3.3.1/d_2
clientPort=2182
server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889
Zoo3.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/root/hadoop-0.20.2/zookeeper-3.3.1/d_3
clientPort=2183
server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889
因为三个服务都在同一台电脑上,所以这里要保证地址的惟一性,所以要特别注意IP地址和端口号不要互相冲突,以避免影响程序的正确执行。
l 建立myid文件
这个同集群模式部署,在各自的文件夹下面建立。
l 执行运行脚本
因为全部的配置文件都在/conf文件夹下面,所以要执行三次,并且要加文件名的参数,否则会默认执行zoo.cfg这个文件,以下:
./zkServer.sh start zoo1.cfg
./zkServer.sh start zoo2.cfg
./zkServer.sh start zoo3.cfg
执行完毕后,将完成ZooKeeper的集群伪分布的启动。
1.4.5. 经过ZooKeeper命令行工具访问ZooKeeper
ZooKeeper命令行工具相似于Linux的shell环境,不过功能确定不及shell啦,可是使用它咱们能够简单的对ZooKeeper进行访问,数据建立,数据修改等操做。
当启动 ZooKeeper 服务成功以后,输入下述命令,链接到 ZooKeeper 服务:
zkCli.sh –server 202.115.36.251:2181
链接成功后,系统会输出 ZooKeeper 的相关环境以及配置信息,并在屏幕输出“ Welcome to ZooKeeper ”等信息。
命令行工具的一些简单操做以下:
1 )使用 ls 命令来查看当前 ZooKeeper 中所包含的内容:
[zk: 202.115.36.251:2181(CONNECTED) 1] ls /
2 )建立一个新的 znode ,使用 create /zk myData 。这个命令建立了一个新的 znode 节点“ zk ”以及与它关联的字符串:
[zk: 202.115.36.251:2181(CONNECTED) 2] create /zk "myData"
3 )咱们运行 get 命令来确认 znode 是否包含咱们所建立的字符串:
[zk: 202.115.36.251:2181(CONNECTED) 3] get /zk
4 )下面咱们经过 set 命令来对 zk 所关联的字符串进行设置:
[zk: 202.115.36.251:2181(CONNECTED) 4] set /zk "zsl"
5 )下面咱们将刚才建立的 znode 删除:
[zk: 202.115.36.251:2181(CONNECTED) 5] delete /zk
1.4.6. 使用API来访问ZooKeeper
API访问ZooKeeper才是客户端主要的使用手段,经过在客户端编写丰富多彩的程序,来达到对ZooKeeper的利用。这里给出一个简单的例子:(深刻的还没能力给出啊,例子是从网上找的很清晰明了)
1. import java.io.IOException;
2.
3. import org.apache.zookeeper.CreateMode;
4. import org.apache.zookeeper.KeeperException;
5. import org.apache.zookeeper.Watcher;
6. import org.apache.zookeeper.ZooDefs.Ids;
7. import org.apache.zookeeper.ZooKeeper;
8.
9. public class demo {
10. // 会话超时时间,设置为与系统默认时间一致
11. private static final int SESSION_TIMEOUT=30000;
12.
13. // 建立 ZooKeeper 实例
14. ZooKeeper zk;
15.
16. // 建立 Watcher 实例
17. Watcher wh=new Watcher(){
18. public void process(org.apache.zookeeper.WatchedEvent event)
19. {
20. System.out.println(event.toString());
21. }
22. };
23.
24. // 初始化 ZooKeeper 实例
25. private void createZKInstance() throws IOException
26. {
27. zk=new ZooKeeper("localhost:2181",demo.SESSION_TIMEOUT,this.wh);
28.
29. }
30.
31. private void ZKOperations() throws IOException,InterruptedException,KeeperException
32. {
33. System.out.println("\n1. 建立 ZooKeeper 节点 (znode : zoo2, 数据: myData2 ,权限: OPEN_ACL_UNSAFE ,节点类型: Persistent");
34. zk.create("/zoo2","myData2".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
35.
36. System.out.println("\n2. 查看是否建立成功: ");
37. System.out.println(new String(zk.getData("/zoo2",false,null)));
38.
39. System.out.println("\n3. 修改节点数据 ");
40. zk.setData("/zoo2", "shenlan211314".getBytes(), -1);
41.
42. System.out.println("\n4. 查看是否修改为功: ");
43. System.out.println(new String(zk.getData("/zoo2", false, null)));
44.
45. System.out.println("\n5. 删除节点 ");
46. zk.delete("/zoo2", -1);
47.
48. System.out.println("\n6. 查看节点是否被删除: ");
49. System.out.println(" 节点状态: ["+zk.exists("/zoo2", false)+"]");
50. }
51.
52. private void ZKClose() throws InterruptedException
53. {
54. zk.close();
55. }
56.
57. public static void main(String[] args) throws IOException,InterruptedException,KeeperException {
58. demo dm=new demo();
59. dm.createZKInstance( );
60. dm.ZKOperations();
61. dm.ZKClose();
62. }
63.}
此类包含两个主要的 ZooKeeper 函数,分别为 createZKInstance ()和 ZKOperations ()。其中 createZKInstance ()函数负责对 ZooKeeper 实例 zk 进行初始化。 ZooKeeper 类有两个构造函数,咱们这里使用 “ ZooKeeper ( String connectString, , int sessionTimeout, , Watcher watcher )”对其进行初始化。所以,咱们须要提供初始化所需的,链接字符串信息,会话超时时间,以及一个 watcher 实例。 17 行到 23 行代码,是程序所构造的一个 watcher 实例,它可以输出所发生的事件。
ZKOperations ()函数是咱们所定义的对节点的一系列操做。它包括:建立 ZooKeeper 节点( 33 行到 34 行代码)、查看节点( 36 行到 37 行代码)、修改节点数据( 39 行到 40 行代码)、查看修改后节点数据( 42 行到 43 行代码)、删除节点( 45 行到 46 行代码)、查看节点是否存在( 48 行到 49 行代码)。另外,须要注意的是:在建立节点的时候,须要提供节点的名称、数据、权限以及节点类型。此外,使用 exists 函数时,若是节点不存在将返回一
个 null 值。
1.5. 小结
对于ZooKeeper的认识目前处在比较浅显的状态,了解到了基本的服务的部署以及大概ZooKeeper的工做原理。不少东西都是只懂得皮毛,如今可以深深地感觉到“只有结合具体的应用才能使你对一个东西有较深的了解”这句话的深入含义了。