zookeeper 知识点汇总
Zookeeper 是什么
zookeeper 是软件世界里的管理者,被用来 提供分布式环境的协调服务。zookeeper 是 yahoo 公司使用 java 语言开发的,是 Hadoop 项目中的子项目,基于 Google 的 Chubby 的开源实现,在 Hadoop,Hbase,Kafka 等技术中充当核心组件的角色。
它的设计目标就是 将那些复杂并且容易出错的分布式一致性服务加以封装,构成高效且可靠的服务,并为用户提供一系列简单易用的接口。
Zookeeper 是一个经典的 分布式数据一致性 解决方案,分布式应用程序可以基于它实现
- 数据的发布和订阅
- 负载均衡
- 命名服务
- 分布式协调与通知
- 集群管理
- 领导选举
- 分布式锁
- 分布式队列
zookeeper 一般都以 集群的方式 对外提供服务,一个集群包含多个节点,每个节点都对应一台 Zookeeper 服务器,所有的节点共同对外提供服务。具体包括以下五大特性:
- 顺序性
- 原子性
- 一致性
- 可靠性
- 实时性
Zookeeper 树状模型
Zookeeper 内部拥有树状的内存模型,与文件系统非常类似。
- 在 Zookeeper 中将这些目录与文件统称为 ZNode
- 每个 ZNode 都有对应的路径及其包含的数据
- ZNode 可由 Zookeeper 客户端来创建
- 当客户端与服务端建立连接后,服务端将为客户端创建一个 Session (会话),客户端对 ZNode 的所有操作均在这个会话中来完成
树状模型图如下所示
ZNode 包含 4 类节点
- persistent 持久节点
- persistent sequential 持久性顺序节点
- ephemeral 临时性节点
- ephemeral sequential 临时性顺序节点
Zookeeper 集群结构
Zookeeper 设计了一个轻量级的协议 Zab (ZooKeeper Atomic Broadcast, Zookeeper 广播协议)。Zab 协议分为两个阶段:
-
Leader Election 领导选举
Zookeeper 集群启动时,会选出一台节点为 Leader,而其他节点均为 Follower。当 Leader 出现故障时,会自动选举出新的 Leader 节点,并让所有节点恢复到一个正常的状态。选举结束后,会进入 原子广播阶段。
-
Atomic Broadcase 原子广播
该阶段会同步 Leader 节点与 Follower 节点之间的数据,确保 Leader 与 Follower 节点具有相同的状态。所有的写操作都会发送到 Leader 节点,并通过广播的方式同步到 Follower 节点。
一个 Zookeeper 集群通常由一组节点组成,一般情况下 3 ~ 5 个就可以组成可用的 Zookeeper 集群。
每个节点都会在内存中维护当前的服务器状态,并在每个节点之间都会保持通信,目的就是告诉其他节点“自己还活着”。我们一般会提供奇数个节点比较节省资源。此外, Zookeeper 客户端可以选择集群中任意一个节点来建立连接,而一旦客户端与某个节点之间断开联系,客户端会自动连接到集群的其他节点。
如何使用 ZooKeeper
zookeeper 官方地址:http://zookeeper.apache.org/
zookeeper 使用 java 语言开发,使用前需要先安装 jdk。下文是在 linux 环境下运行 zookeeper 的指导。
运行 Zookeeper
步骤1 修改 ZooKeeper 配置文件
Zookeeper 默认提供了一份名为 zoo_sample.cfg
的示例配置文件,需要复制一下,并将其重命名为 zoo.cfg
。配置文件的重要配置项如下
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/tmp/zookeeper
clientPort=2181
详细解释下以上配置项
tickTime
滴答时间,用于配置 Zookeeper 中最小时间单元的长度。单位为 ms。默认值是 3000 ms。initLimit
用于配置 Leader 节点等待 Follower 节点启动并完成数据同步的时间, 默认值是 10 ,也就是 10 * tickTimesyncLimit
用来配置 Leader 节点与 Follower 节点之间心跳检测的最大延时时间,默认值是 5,也就是 5 * tickTimedataDir
用来配置 Zookeeper 服务器存储快照文件的目录,不建议将其指定到 /tmp 目录下,因为该目录下的所有文件可能被自动删除。在 Zookeeper 环境中,将生成一个名为 myid 的文件,用来存放 zk 集群节点的 ID。clientPort
用于配置当前 zk 服务器对外暴露的接口
步骤 2, 启动 Zookeeper 服务器
启动 Zookeeper 服务器非常简单,只需执行 Zookeeper 提供的脚本程序即可。
bin/zkServer.sh start
执行上面脚本,默认会在后台启动 Zookeeper 服务器。
zkServer.sh
可以传入以下参数
- start
- start-foreground
- stop
- restart
- status
- upgrade
- print-cmd
步骤 3, 验证 Zookeeper 服务是否有效
bin/zkServer.sh status
还可以使用 telnet 命令来验证 ZooKeeper 服务是否有效
telnet 127.0.0.1 2181
zookeeper 当前处于 standard alone 模式下,一般情况下我们使用单机模式作为开发环境,而使用集群模式作为生产环境。
搭建 Zookeeper 集群环境
在本地搭建一个伪集群模式的 Zookeeper 集群环境(3 个节点)
修改 Zookeeper 配置文件
配置如下
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/app-data/zookeeper/data
dataLogDir=/usr/local/zookeeper-3.4.6/logs
clientPort=2181
server.0=127.0.0.1:2888:3888
server.1=127.0.0.1:2889:3889
server.2=127.0.0.1:2890:3890
与单机模式的区别就在于添加了一组 server 配置,表示集群中包含的三个节点,需要注意的是 server 配置需要满足一定的格式:
server.<id>=<ip>:<port1>:<port2>
- id 表示节点编号,表示该节点在集群中的唯一编号,取值在 0 ~ 255 之间。
- 必须在 dataDir 目录下创建一个名为 myid 的文件,其内容是该节点的编号
- ip 表示节点所在的 IP 地址,本机为 127.0.0.1 或者是 localhost
- port1 表示 Leader 节点与 Follower 节点心跳检测与数据同步时使用的接口
- port2 表示在领导选举过程中,用于投票通信的端口
启动 ZooKeeper 集群
与单机模式的启动方法相同,只需一次启动所有的 ZooKeeper 节点即可启动整个集群
验证 Zookeeper 集群环境是否有效
和 standard alone 模式一样,也可以通过 zkServer.sh 脚本与 telnet 命令来查看每个节点的状态,此时会看到 Mode:leader
或者 Mode:follower
的信息,表示该节点是 Leader 还是 Follower。
ZooKeeper 提供了一系列脚本程序,全部存放在 bin 目录下,如
- zkServer.sh 用于启动 Zookeeper 服务器
- zkCli.sh 用于连接 ZooKeeper 服务器的命令行客户端
- zkCleanup.sh 用于清理 ZooKeeper 的历史数据,包括
- 事务日志文件
- 快照数据文件
- zkEnv.sh 用于设置 ZooKeeper 的环境变量
连接 zookeeper
使用 zkCli 连接 Zookeeper
连接 ZooKeeper 只需要执行以下脚本:
bin/zkCli.sh
如果想要连接远程的 ZooKeeper, 则在 zkCli.sh 脚本中添加 -server 选项
bin/zkCli.sh -server <ip>:<port>
连接成功后,就可以输入相关的命令来操作 zookeeper 了。当输入 help 命令后,将输出 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
列出子节点
命令格式如下
ls path [watch]
ls 命令中可设置一个 watch
参数,用于指定客户端监视器,在 zooKeeper 中被称为 Watcher, Watcher 用于监控节点的状态变化,默认情况下可不带有任何 Watcher。
[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper]
默认情况下,根目录中有一个名为 zookeeper 的子节点,它作为 Zookeeper 的保留节点,我们一般不使用它。
此外,还可以使用 ls2
命令以更加详细的方式列出节点名称及其相关属性,命令格式如下
ls2 path [watch]
ls2 命令会输出当前节点的基本信息
[zk: localhost:2181(CONNECTED) 2] ls2 /
[zookeeper]
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
在输出信息中包含了大量的统计属性:
cZxid
表示创建节点时的事务 ID (每个客户端请求都会形成一个事务)ctime
表示创建节点的时间mZxid
表示最后一次修改节点时的事务 IDmtime
表示最后一次修改节点的时间pZxid
表示最后一次修改父节点时的事务 IDcversion
子节点的版本号dataVersion
节点包含数据的版本号aclVersion
表示节点的 ACL 权限版本号ephemeralOwner
临时节点的会话 IDdataLength
表示节点包含数据内容的长度numChildren
当前节点的子节点数
判断节点是否存在
[zk: localhost:2181(CONNECTED) 8] stat /foo
Node does not exist: /foo
创建节点
使用 create 命令创建节点,命令格式如下
create [-s] [-e] path data acl
其中
-
-s
用于指定该节点是否为顺序节点,即 Sequential 节点 -
-e
选项:用于指定该节点是否为临时节点,即 Ephemeral 节点 -
acl
用于权限控制, Zookeeper 内部提供了一个强大的 ACL 访问控制列表,默认情况下不做任何权限控制[zk: localhost:2181(CONNECTED) 13] create -e /foo hello Created /foo
获取节点数据
使用 get 命令获取节点数据,命令格式如下
get path [watch]
可以通过以下命令获取 /foo 节点包含的数据
[zk: localhost:2181(CONNECTED) 14] get /foo
hello
cZxid = 0xa
ctime = Wed Jan 02 15:27:44 CST 2019
mZxid = 0xa
mtime = Wed Jan 02 15:27:44 CST 2019
pZxid = 0xa
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x1680d4a324f0002
dataLength = 5
numChildren = 0
更新节点数据
使用 set 命令更新节点数据,命令格式如下
set path data [version]
在更新节点数据时,可指定 version 参数,表示节点包含数据的版本号。如果不指定 version 参数,则表示更新节点数据的最新版本。
删除节点
使用 delete 命令删除节点,命令格式如下
delete path [version]
当节点没有任何子节点时,才能删除成功,否则将给出 "node not empty" 的提示,但可以通过以下命令一次删除该节点及其所有的子节点。
rmr path
使用 Node.js Client 连接 Zookeeper
Node.js 连接 Zookeeper 中比较好用的客户端是 node-zookeeper-client
。
首先通过 NPM 来安装这个模块。
npm install node-zookeeper-client
先用一段代码来展示连接 Zookeeper, 后面再基于该代码,将 nodejs 针对 zookeeper 客户端的操作执行一遍。
var zookeeper = require('node-zookeeper-client')
var CONNECTION_STRING = 'localhost:2181'
var OPTIONS = {
sessionTimeout: 5000
}
var zk = zookeeper.createClient(CONNECTION_STRING, OPTIONS)
zk.on('connected', function() {
console.log(zk);
zk.close();
});
zk.connect();
如果 zk 可以正常输出,说明可成功连接 Zookeeper 服务器并建立了正常的会话, 下面所有的操作都在该会话中进行。Node.js 客户端仅提供了异步方式,我们不能通过同步方式来调用。
node-zookeeper-client 的官方文档地址为: https://www.npmjs.com/package/node-zookeeper-client
可执行的常见操作包括:
- 列出子节点
- 判断节点是否存在
- 创建节点
- 获取节点数据
- 更新节点数据
- 删除节点
参考
- 《架构探险—轻量级微服务架构》