Apache ZooKeeper 是一个面向分布式应用程序的高性能协调服务器, 至于它的具体介绍像能提供命名和配置管理、同步和组服务等, 请自个 Google. 虽然对 ZooKeeper 早有耳闻, 也只最近因为项目中有 Kafka 内部用到了 ZooKeeper, 所以才促使我着手去了解一下 ZooKeeper 为何物. ZooKeeper 脱胎于著名的项目 Hadoop, 它也像 Hazelcast 那样未采用严格意义的 Master-Slave 的集群方式, 而是动态选出 Leader, 这避免了单点故障的问题.
既然是集群, 实际应用是会在不同的机器上启动服务, 现如今有了 docker 很容易用它来测试多主机环境, 用 docker search zookeeper
就知道了. 本实例中暂不涉及 docker, 而是在同一个系统中用多个端口来启动三个 ZooKeeper 实例进行测试. 下面以 Mac/Linux 为例:
下载安装 ZooKeeper
到 https://zookeeper.apache.org/releases.html#download 下载, 写下此文时的最新稳定版是 zookeeper-3.4.9.tar.gz, 下载后解压到某处, 为方便起见把解压后的 zookeeper-3.4.9/bin 加到系统环境变量 $PATH 中, 以后方便任何时候运行 zkServer.sh, zkCli.sh 这样的命令.
准备目录和文件
建立三个目录 server1, server2 和 server3, 并在每个目录中创建子目录 data, dataLog, logs; 以 server1 为例, 它的目录结构如下:
─── server1
├── data
├── dataLog
└── zookeeper.out
最后在以上的每一个 serverx/data 目录中创建文件 myid, 里面写入一个数字, 分别是 1, 2, 和 3.
以 Mac/Linux 可用下面的命令来完成目录和 myid 文件中的创建
1 2 |
mkdir -p {server1,server2,server3}/{data,dataLog,logs} for i in {1..3}; do echo "$i" > server$i/data/myid; done |
配置文件
配置文件可参考 zookeeper-3.4.9/conf 下的 zoo_sample.cfg. 分别在 server1, server2, server3 建立文件 zoo.cfg
, 内容如下
1 2 3 4 5 6 7 8 9 |
tickTime=2000 initLimit=5 syncLimit=2 dataDir=data dataLogDir=dataLog clientPort=2181 server.1=127.0.0.1:2888:3888 server.2=127.0.0.1:2889:3889 server.3=127.0.0.1:2890:3890 |
不同进程的 clientPort 必须不同, 比如这里 server1 是 2181, server2 是 2182, server3 是 2183.
server.X 这个数字就是对应的 data/myid 中的数字. server.X 后的第一个端口用来集群成员的信息交换, 第二个端口是在 leader 挂掉时进行新 leader 选举用的.
启动服务
依次进到 server1, server2 和 server3 目录下执行 zkServer.sh start ./zoo.cfg
,
➜ zookeeper for d in
ls
; do cd $d; zkServer.sh start ./zoo.cfg; cd ..; done
ZooKeeper JMX enabled by default
Using config: ./zoo.cfg
Starting zookeeper ... STARTED
ZooKeeper JMX enabled by default
Using config: ./zoo.cfg
Starting zookeeper ... STARTED
ZooKeeper JMX enabled by default
Using config: ./zoo.cfg
Starting zookeeper ... STARTED
如果 start
后不带 ./zoo.cfg
则会使用默认的配置文件 ZOOKEEPER_HOME/conf/zoo.cfg 文件.
这样 ZooKeeper 的三个节点都启动来了, 查看一下端口, 下面列出了 ZooKeeper 相关的端口
tcp46 0 0 *.2183 *.* LISTEN
tcp46 0 0 *.2182 *.* LISTEN
tcp46 0 0 *.2181 *.* LISTEN
tcp4 0 0 127.0.0.1.2889 127.0.0.1.50886 ESTABLISHED
tcp4 0 0 127.0.0.1.50886 127.0.0.1.2889 ESTABLISHED
tcp4 0 0 127.0.0.1.2889 127.0.0.1.50885 ESTABLISHED
tcp4 0 0 127.0.0.1.50885 127.0.0.1.2889 ESTABLISHED
tcp4 0 0 127.0.0.1.2889 *.* LISTEN
tcp4 0 0 127.0.0.1.3889 127.0.0.1.50869 ESTABLISHED
tcp4 0 0 127.0.0.1.50869 127.0.0.1.3889 ESTABLISHED
tcp4 0 0 127.0.0.1.3888 127.0.0.1.50868 ESTABLISHED
tcp4 0 0 127.0.0.1.50868 127.0.0.1.3888 ESTABLISHED
tcp4 0 0 127.0.0.1.3890 *.* LISTEN
tcp4 0 0 127.0.0.1.3889 *.* LISTEN
tcp4 0 0 127.0.0.1.3888 *.* LISTEN
从上面结果可以发现端口 2888, 2889, 2890 之中只有 2889 端口正在监听, 也就是说 server2 是现在的 leader. 可以停掉某一个 server 看谁会当上新的 leader 了. ZooKeeper 读数据时只从所连接的服务器获取, 写数据时需通过 leader 同步到所有节点上后事物才算完成.
现在如果有兴趣的话也可以看看在 serverX 目录中 或 data, dataLog 中产生了什么文件.
用 ZooKeeper 命令行客户端测试
zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183
通常我们是指定一串服务器, 虽然指定单个服务器(如 zkCli.sh -server 127.0.0.1:2181) 也能连接整个 ZooKeeper 集群, 但是所连接的服务器出问题就完蛋了. 而指定一串服务器的好处是, 在当前所连接的服务器出故障时自动连接列表中别的可用服务器.
zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183
Connecting to 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183
......................
2016-10-04 00:41:23,804 [myid:] - INFO [main-SendThread(127.0.0.1:2182):ClientCnxn$SendThread@876] - Socket connection established to 127.0.0.1/127.0.0.1:2182, initiating session
[zk: 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183(CONNECTING) 0] 2016-10-04 00:41:26,821 [myid:] - INFO [main-SendThread(127.0.0.1:2182):ClientCnxn$SendThread@1299] - Session establishment complete on server 127.0.0.1/127.0.0.1:2182, sessionid = 0x2578e2b859b0000, negotiated timeout = 30000
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
[zk: 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183(CONNECTED) 0]
现在进到了 ZooKeeper 客户端的控制台下, 输入任何不识别的命令都会显示出帮助
ZooKeeper -server host:port cmd args
stat path [watch]
set path data [version]
ls path [watch]
delquota [-n|-b] path
ls2 path [watch]
setAcl path acl
setquota -n|-b val path
history
redo cmdno
printwatches on|off
delete path [version]
sync path
listquota path
rmr path
get path [watch]
create [-s] [-e] path data acl
addauth scheme auth
quit
getAcl path
close
connect host:port
十分有必要找一张图让我们初步了解一下 ZooKeeper 维护数据所采用的类似于文件系统的结构
ZooKeeper 的每个子目录都称为 znode, znode 是有版本的, 分临时或永久节点. 只有 EPHEMERAL 类型的目录不能再有子目录. znode 可自动编号, 可被监控.
现在执行下 ls /
命令只能看到 [zookeeper] 这一个节点
[zk: 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183(CONNECTED) 7] ls /
[zookeeper]
为了演示, 我们现用两个 ZooKeeper 客户端连接到不同的端口能感受 ZooKeeper 中数据的同步, 分别
zkCli.sh -server 127.0.0.1:2181
zkCli.sh -server 127.0.0.1:2182
然后在一端创建 znode, 看另一端是否能得到新的数据
默认时 ls /
只有 [zookeeper] 节点, 左边用命令
create /mynode helloworld
然后在右端执行
ls /
get /mynode
就能得到前面添加的数据, 值为 "helloworld"
前面 zkCli.sh 能干的事情用 ZooKeeper 的 Java API 也都能做, 看 Class ZooKeeper Java Doc API.
ZooKeeper 集群式管理一个目录(znode) 结构, 在每一层目录上可以设置数据, znode 可被监控, 这对我们就很有用了. 我们可以用 ZooKeeper 用作集中式的配置管理. 相信很多时候并不需要我们写代码去应用 ZooKeeper, 而只是需要像 Kafka 那样配置代替编码.
链接:
本文链接 https://yanbin.blog/zookeeper-fast-get-started/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。