这大概是我第一次以教程的形式写博文。但是准确地说,这更应该是一次学习笔记。希望在自己学习的过程中,能够帮助他人。这篇博文的内容是我在学习ZooKeeper的同时撰写的,因此并不是很深入,适合和我一样希望学习ZooKeeper的同学。另外,如果你在文章中发现了错误,欢迎在评论中指正。
下载和安装
ZooKeeper的下载和安装还是很简单的,首先到Apache网站上找到Zookeeper的镜像源,然后在镜像源中下载最新版本即可。在这里给大家推荐北京理工大学镜像源。下载完毕后解压:
tar xf zookeeper-3.4.11.tar.gz
解压完毕后,将解压出的文件添加到/usr/local/lib目录下:
sudo mv zookeeper-3.4.11 /usr/local/lib
至此,安装过程就搞定了。可以说是非常的简单了。
注意:ZooKeeper的运行需要安装JDK,因此请确保已经安装并配置JDK
配置ZooKeeper集群
首先打开ZooKeeper的配置文件,文件为 conf/zoo_sample.cfg。文件中可配置的项目并不是很多,而且都给出了注释,大体功能如下:
# 每个计时单位(应该是心跳包)的间隔(毫秒)
tickTime=2000
# 初始化同步的超时时间(以上述的tick为单位,下同)
initLimit=10
# 每次同步的超时时间(从发出请求到得到反馈)
syncLimit=5
# 储存数据的文件夹(一定要修改这里,因为/tmp下的文件会被自动删除)
dataDir=/tmp/zookeeper
# 客户端连接端口
clientPort=2181
# 最大客户端连接数
maxClientCnxns=60
以上是主要配置项。其中必须要修改的就是 dataDir。修改完毕后,需要添加集群中各个服务器的IP地址。假设我们的集群里共有3台主机,它们的IP分别是 192.168.1.2,192.168.1.3,192.168.1.5,则配置格式如下:
server.1=192.168.1.2:2888:3888
server.2=192.168.1.5:2888:3888
server.3=192.168.1.3:2888:3888
其中,等号左边为主机ID,等号右边分别为主机IP、数据同步端口以及选择leader的端口。需要注意的是,集群内所有主机的上述配置必须相同,不然会找不到主机。配置完毕后,将文件另存为zoo.cfg,只有另存配置才能生效。因此,我的zoo.cfg完整内容如下(为了简洁,我已经手动删除注释信息):
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/home/dapan/Data/zkdata
clientPort=2181
server.1=192.168.1.2:2888:3888
server.2=192.168.1.5:2888:3888
server.3=192.168.1.3:2888:3888
可以通过FTP等方式将上述配置文件拷贝到集群内所有机器上,从而快速完成配置。但是仅仅这样是不够的,因为每台主机还不知道自己是谁。所以接下来需要在dataDir下新建一个名为myid的文件,每台主机的myid都不同:
cd /home/dapan/Data/zkdata
echo 1 > myid
在剩余两台主机上重复上述操作,只不过把内容改为2和3.修改完成后,主机将根据自己的id,匹配配置文件中的主机。到现在为止,ZooKeeper的配置就完成了。
在控制台操作ZooKeeper
ZooKeeper的命令相对来说是很简单的。我们首先在3台主机上启动ZooKeeper:
cd /usr/local/lib/zookeeper-3.4.11/bin
./zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /usr/local/lib/zookeeper-3.4.11/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
服务端启动后,在任意一台主机上启动客户端:
./zkCli.sh
接下来就可以使用ZooKeeper提供的API进行操作了。需要注意的是,ZooKeeper的数据结构是树状的,类似于Linux的文件系统,但又不完全一样。例如,ZooKeeper中的每个数据节点(临时节点除外)均可保存数据并创建子节点。而且,ZooKeeper中不存在cd的概念,所有数据的访问必须从根节点开始。
首先尝试一下枚举数据(第一行是命令,其余是输出,下同):
[zk: localhost:2181(CONNECTED) 1] ls /
[zookeeper, app2]
这里列出了根节点的全部子节点。接下来我们使用create来创建一个新的节点mydata:
[zk: localhost:2181(CONNECTED) 2] create /mydata "Hello ZooKeeper!"
Created /mydata
创建节点的格式如下:create 节点名 节点数据。这里需要注意的是,无论是枚举、创建还是后续的查看和删除,一定是从根节点开始的。创建完毕后,我们可以使用get命令来读取节点中的内容:
[zk: localhost:2181(CONNECTED) 3] get /mydata
Hello ZooKeeper!
cZxid = 0x200000004
ctime = Fri Feb 23 14:22:48 CST 2018
mZxid = 0x200000004
mtime = Fri Feb 23 14:22:48 CST 2018
pZxid = 0x200000004
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 16
numChildren = 0
简单解释一下数据的含义:第一行自然是数据的内容,cZxid和ctime分别是创建时的id和创建时间,mZxid和mtime则是修改时的id和修改时间。由于这个节点的数据尚未修改过,因此创建信息等于修改信息。pZxid是持久化时的记录Id,dataVersion表示节点数据的更新次数,cversion表示其子节点的更新次数,aclVersion表示节点ACL(授权信息)的更新次数。如果该节点为ephemeral(临时)节点, ephemeralOwner值表示与该节点绑定的session id,如果该节点不是ephemeral节点, ephemeralOwner值为0。dataLength表示数据长度,numChildren表示子节点数目。
接下来可以使用set命令设置节点的值,格式为 set 节点名称 节点新数据:
[zk: localhost:2181(CONNECTED) 4] set /mydata "Goodbye ZooKeeper!"
cZxid = 0x200000004
ctime = Fri Feb 23 14:22:48 CST 2018
mZxid = 0x200000005
mtime = Fri Feb 23 14:31:33 CST 2018
pZxid = 0x200000004
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 18
numChildren = 0
可以看到,设置完毕后,mZxid和mtime都发生了改变。接下来使用delete命令删除节点,格式为 delete 节点名称。注意delete只能删除没有子节点的节点,如果需要删除有子节点的节点,则需要使用rmr命令。用法和delete命令是一样的。
备注:特殊类型节点
在上面的介绍中,我提到了ephemeral节点。ephemeral节点为临时节点,具有如下特点:
- 当创建节点的客户端(注意是客户端)断开后,节点将被删除
- ephemeral节点不能拥有子节点
ephemeral节点的创建方式很简单,在create的时候加上参数 -e 即可:
[zk: localhost:2181(CONNECTED) 8] create -e /tmp "Ephemeral Test"
Created /tmp
此时我们尝试在/tmp下新建子节点,发现无论是普通子节点,还是ephemeral子节点,均无法创建:
[zk: localhost:2181(CONNECTED) 12] create /tmp/sub "Sub"
Ephemerals cannot have children: /tmp/sub
[zk: localhost:2181(CONNECTED) 13] create -e /tmp/sub "Sub"
Ephemerals cannot have children: /tmp/sub
此外,还有一种序列化子节点,其特点是在创建时,自动在节点名称后面加上序列化编号,从而避免重名:
[zk: localhost:2181(CONNECTED) 14] create -s /node "Node"
Created /node0000000006
[zk: localhost:2181(CONNECTED) 15] create -s /node "Node"
Created /node0000000007
因此,ZooKeeper总共有4种类型的节点:普通节点,临时节点(-e),序列化节点(-s),序列化临时节点(-s -e)。