1.1. 概述
ZooKeeper是Apache在很多云计算项目中
的
一个,与
Hadoop密切相关,这种情况导致我一开始认为
ZooKeeper的
搭建需要
Hadoop项目作为支持,但是最后发现完全不需要,它是可以单独运行
的
一个项目。
在网上看
到
了一个很不错
的
关于
ZooKeeper的
介绍: 顾名思义动物园管理员,他是拿来管大象
(Hadoop) 、 蜜蜂
(Hive) 、 小猪
(Pig) 的
管理员,
Apache Hbase和
Apache Solr 以及
LinkedIn sensei 等项目中都采用
到
了
Zookeeper。
ZooKeeper是一个分布式
的
,开放源码
的
分布式应用程序协调服务,
ZooKeeper是以
Fast Paxos算法为基础,实现同步服务,配置维护和命名服务等分布式应用。
从介绍可以看出,ZooKeeper
更倾向于
对
大型应用
的
协同维护管理工作。
IBM则给出了
IBM对
ZooKeeper的
认知:
Zookeeper 分布式服务框架是
Apache Hadoop 的
一个子项目,它主要是用来解决分布式应用中经常遇
到
的
一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项
的
管理等。
总之,我认为它的
核心词就是一个单词,协调。
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的
数据结构情况。名字空间
的
层次由斜杠
/来进行分割,在名称空间里面
的
每一个结点
的
名字空间唯一由这个结点
的
路径来确定。
每一个节点拥有自身
的
一些信息,包括:数据、数据长度、创建时间、修改时间等等。从这样一类既含有数据,又作为路径表标示
的
节点
的
特点
中,
可以看出,
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的
目
的
是为了可以在分布式
的
环境中保证数据
的
一致性。
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 值。