Apache Zookeeper
Apache Zookeeper Website: https://zookeeper.apache.org
Zookeeper简介
Zookeeper是一个分布式协调调度框架,主要用来解决分布式集群中应用系统的一致性问题。
- Zookeeper本质上是一个分布式的小文件存储系统。提供基于类似于文件系统的目录树方式的数据存储,并且可以对树中的节点进行有效管理;
- Zookeeper提供给客户端监控存储在ZK内部数据的功能,从而可以达到基于数据的集群管理。诸如:统一命名服务(Dubbo)、分布式配置管理(Solr的配置集中管理)、分布式消息队列(sub/pub)、分布式锁、分布式协调等功能。
ZK的架构构成
Leader
- Zookeeper集群工作的核心角色;
- 集群内部各个服务器的调度者;
- 事务请求(写操作)的唯一调度和处理者,保证集群事务处理的顺序性;对于create, setData, Delete等有写操作的请求,则需要统一转发给Leader处理,Leader需要决定编号、执行操作,这个过程称为一个事务。
Follower
- 处理客户端非事务(读操作)请求;
- 转发事务请求给Leader;
- 参与集群Leader选举投票2n - 1 台可以做集群投票。
Observer
- 观察者角色,观察Zookeeper集群的最新状态变化并将这些状态同步过来,其对于非事务请求可以进行独立处理,对于事务请求,则会转发给Leader服务器进行处理;
- 不会参与任何形式的投票只提供非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力。增加了集群并发的读请求。
Zookeeper特点
- Zookeeper:一个领导者(Leader),多个跟随者(Follower)组成的集群;
- Leader负责进行投票的发起和决议,更新系统状态;
- Follower用于接收客户请求并向客户端返回结果,在选举Leader过程中参与投票;
- 集群中只有半数以上节点存活,Zookeeper集群就能正常服务;
- 全局数据一致:每个server保存一份相同的数据副本,Client无论连接到哪个server,数据都是一致的;
- 更新请求顺序进行;
- 数据更新原子性,一次数据更新要么成功,要么失败。
ZK环境搭建
ZK的搭建方式
ZK安装方式有三种,单机模式和集群模式以及伪集群模式。
- 单机模式:Zookeeper只允许在一台服务器上,适合测试环境;
- 伪集群模式:就是在一台服务器上运行多个zk实例;
- 集群模式:zk运行于一个集群上,适合生产环境,这个计算集群被称为一个“集合体”
ZK单机模式
Getting Started Guide
从官网下载网址下载最新版本3.7.0,具体部署步骤详见Getting Started Guide。
常用命令
-
启动命令
bin/zkServer.sh start
-
查看启动状态
bin/zkServer.sh status
-
停止命令
bin/zkServer.sh stop
ZK数据结构与监听机制
Zookeeper数据模型Znode
在Zookeeper中,数据信息被保存在一个个数据节点上,这些节点被称为ZNode
。ZNode是ZK中最小的数据单元,在ZNode下面又可以再挂ZNode,这样一层层下去就形成了一个层次化命名空间ZNode树,我们称为ZNode Tree
,它采用了类似文件系统的层级树状结构进行管理。见下图示例:
在zk中,每一个数据节点都是一个ZNode,ZNode的节点路径标识方式和Unix文件系统路径非常相似,都是由一系列使用斜杠(/)进行分割的路径表示,开发人员可以向这个节点写入数据,也可以在这个节点下面创建子节点。
ZNode类型
ZK节点类型可以分为三大类:
-
持久性节点(Persistent):节点被创建后会一直存在服务器,直到删除操作主动清除;
-
临时性节点(Ephemeral):它的生命周期和客户端会话绑在一起,客户端会话结束,节点会被删除掉。与持久性节点不同的是,临时节点不能创建子节点;
-
顺序性节点(Sequential):实质就是在创建节点的时候,会在节点名后面加上一个数字后缀,来表示其顺序。
在开发中创建节点的时候通过组合可以生成以下四种节点类型:持久节点
、持久顺序节点
、临时节点
、临时顺序节点
。
事务ID
在ZK中,事务是指能够改变ZK服务器状态的操作,包括数据节点创建、删除、内容更新等操作。对于每一个事务请求,ZK都会为其分配一个全局唯一的事务ID,用ZXID来表示,通常是一个64位的数字。每一个ZXID对应一次更新操作,从这些ZXID中可以间接识别出ZK处理这些更新操作请求的全局顺序。
ZNode的状态信息
[zk: localhost:2181(CONNECTED) 5] get -s /zk-persist
123
cZxid = 0x8
ctime = Mon Jan 03 17:09:50 CST 2022
mZxid = 0x8
mtime = Mon Jan 03 17:09:50 CST 2022
pZxid = 0x8
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
field | description |
---|---|
czxid | 创建znode的zxid |
mzxid | 最近一次修改znode的zxid(创建、删除、set直系子节点、set自身节点都会计数) |
pzxid | 最近一次修改子节点的zxid(创建、删除直系子节点都会计数,set子节点不会计数) |
ctime | 创建znode的时间,单位毫秒 |
mtime | 最近一次修改znode的时间,单位毫秒 |
version | 修改znode的次数 |
cversion | 修改子节点的次数(创建、删除直系子节点都会计数,set子节点不会计数) |
aversion | 该znode的ACL修改次数 |
ephemeralOwner | 临时znode节点的session id,如果不是临时节点,值为0 |
dataLength | znode携带的数据长度,单位字节 |
numChildren | 直系子节点的数量(不会递归计算孙节点) |
Watcher机制
zk使用Watcher机制实现分布式数据的发布/订阅功能。zk允许客户端向服务端注册一个Watcher监听,当服务端的一些指定事件触发了这个Watcher,那么zk就会向指定的客户端发送一个事件通知来实现分布式的通知功能。整个Watcher注册与通知过程如图所示:
ZK的Watcher机制主要包括:客户端线程、客户端WatcherManager、Zookeeper服务器三部分。
具体工作流程为:
- 客户端在向zk服务器注册的同时,会将Watcher对象存储在客户端的WatchManager当中;
- 当ZK服务器触发Watcher事件后,会向客户端发送通知;
- 客户端线程从WatcherManager中取出对应的Watcher对象来执行回调逻辑。
ZK的基本使用
zk命令行操作
通过zkClient进入zk客户端命令行
./zkcli.sh # 连接本地的zk服务器
./zkcli.sh -server ip:port # 连接指定的服务器
帮助命令
[zk: localhost:2181(CONNECTED) 3] help
ZooKeeper -server host:port -client-configuration properties-file cmd args
addWatch [-m mode] path # optional mode is one of [PERSISTENT, PERSISTENT_RECURSIVE] - default is PERSISTENT_RECURSIVE
addauth scheme auth
close
config [-c] [-w] [-s]
connect host:port
create [-s] [-e] [-c] [-t ttl] path [data] [acl]
delete [-v version] path
deleteall path [-b batch size]
delquota [-n|-b|-N|-B] path
get [-s] [-w] path
getAcl [-s] path
getAllChildrenNumber path
getEphemerals path
history
listquota path
ls [-s] [-w] [-R] path
printwatches on|off
quit
创建节点
使用create
命令创建一个zk节点,如:
create [-s] [-e] path data
-s
代表创建一个顺序节点;-e
代表创建一个临时节点;- 若不指定,则创建一个持久节点。
-
创建顺序节点
[zk: localhost:2181(CONNECTED) 5] create -s /zk-test 123 Created /zk-test0000000000
执行完后,就在根节点下创建了一个叫做/zk-test的节点,该节点内容就是123,同时可以看到创建的zk-test节点后面添加了一串数字以示区别。
-
创建临时节点
[zk: localhost:2181(CONNECTED) 7] create -e /zk-temp 123 Created /zk-temp [zk: localhost:2181(CONNECTED) 8] ls / [zk-temp, zk-test0000000000, zookeeper]
临时节点在客户端会话结束后,就会自动删除,下面使用
quit
命令退出客户端[zk: localhost:2181(CONNECTED) 9] quit WATCHER:: WatchedEvent state:Closed type:None path:null 2022-01-03 17:06:16,947 [myid:] - INFO [main:ZooKeeper@1232] - Session: 0x10014703c2b0001 closed 2022-01-03 17:06:16,948 [myid:] - INFO [main-EventThread:ClientCnxn$EventThread@570] - EventThread shut down for session: 0x10014703c2b0001 2022-01-03 17:06:16,949 [myid:] - ERROR [main:ServiceUtils@42] - Exiting JVM with code 0
再次使用客户端连接服务端,并使用
ls /
命令查看根目录的节点[zk: localhost:2181(CONNECTED) 0] ls / [zk-test0000000000, zookeeper]
可以看到根目录下已经不存在zk-temp临时节点了。
-
创建永久节点
[zk: localhost:2181(CONNECTED) 1] create /zk-persist 123 Created /zk-persist [zk: localhost:2181(CONNECTED) 2] ls / [zk-persist, zk-test0000000000, zookeeper]
可以看到永久节点不用与顺序节点,不会自动在后面添加一串数据。
读取节点
与读取相关的命令有:
ls
命令:可以列出zk指定节点下的所有子节点,但只能查看指定节点下的第一级的所有子节点;get
命令:可以获取zk指定节点的数据内容和属性信息。
若获取根节点下面的所有子节点,使用ls /
命令即可
[zk: localhost:2181(CONNECTED) 2] ls /
[zk-persist, zk-test0000000000, zookeeper]
若想获取节点的数据内容和属性,可使用get path
命令
[zk: localhost:2181(CONNECTED) 3] get /zk-persist
123
# -s 表示包含ZNode状态信息
[zk: localhost:2181(CONNECTED) 5] get -s /zk-persist
123
cZxid = 0x8
ctime = Mon Jan 03 17:09:50 CST 2022
mZxid = 0x8
mtime = Mon Jan 03 17:09:50 CST 2022
pZxid = 0x8
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
更新节点
使用set
命令,可以更新指定节点的数据内容,用户如下:
$ set [-s] [-v version] path data
其中data
就是要更新的新内容,version
表示数据版本。在zk中,节点的数据是有版本概念的,这个参数用于指定本次更新操作时基于ZNode的哪一个数据版本进行的。
[zk: localhost:2181(CONNECTED) 6] set /zk-persist 666
[zk: localhost:2181(CONNECTED) 7] get -s /zk-persist
666
cZxid = 0x8
ctime = Mon Jan 03 17:09:50 CST 2022
mZxid = 0x9
mtime = Mon Jan 03 17:24:56 CST 2022
pZxid = 0x8
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
删除节点
使用delete
命令可以删除zk上的指定节点,用法如下:
delete [-v version] path
NOTE: 若删除节点存在子节点时,那么无法直接删除该节点,必须先删除子节点,再删除父节点。