zookeeper客户端
一. 客户端启动进去zookeeper安装目录,在bin目录下有客户端的启动脚本./bin/zkCli.sh这说明客户端启动成功了!二. zk的节点类型zookeeper节点结构是一个树形结构,一个节点有两个维度:临时/永久、有序/无序。一共四种节点类型:PERSISTENT:持久化节点PERSISTENT_SEQUENTLAT:持久化排序节点EPHEMERAL:临时节点EPHEMERAL_SEQ
一. 客户端启动
进去zookeeper安装目录,在bin目录下有客户端的启动脚本
./bin/zkCli.sh
这说明客户端启动成功了!
二. zk的节点类型
zookeeper节点结构是一个树形结构,一个节点有两个维度:临时/永久、有序/无序。一共四种节点类型:
-
PERSISTENT:持久化节点
-
PERSISTENT_SEQUENTLAT:持久化排序节点
-
EPHEMERAL:临时节点
-
EPHEMERAL_SEQUENTLAL:临时排序节点
永久节点:在节点创建之后就会被持久化,只有主动调用delete方法的时候才可以删除节点;
临时节点:节点创建后自动创建超时连接或者失去连接的时候,节点会被删除,临时节点不能存在子节点;
排序节点:创建节点名称后会自动添加序号,例如:节点名称为node-
,将会自动添加node-1
,顺序的话就是node-2
三. 操作命令
在控制台输入 help
或者tab键
,就可以看到zookeeper客户端支持的命令了:
下面介绍一写常用的命令,用来操作zookeeper集群
3.1. ls和get命令
ls命令:查看当前节点信息,有三个参数:-w
(监听子节点变化)、-s
(详细信息)和-R
(递归现实节点)
[zk: localhost:2181(CONNECTED) 8] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 9] ls -s /
[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 | 每次修改zk状态都会产生一个zk事务ID,事务ID是zk中所有修改的总次序。每次修改都有唯一的zxid,zxid有先后顺序。 |
ctime | znode被创建的毫秒数(1970年开始) |
mZxid | znode最后更新的事务zxid |
mtime | znode最后修改的毫秒数(1970年开始) |
pZxid | znode最后更新的子节点zxid |
cversion | znode子节点变化号,znode子节点修改次数 |
dataVersion | znode数据变化号 |
aclVersion | znode访问访问控制表的变化号 |
ephemeralOwner | 如果是临时节点,这个是znode拥有者的session id。如果不是临时节点则是0 |
dataLength | znode的数据长度 |
numChildren | znode的子节点数量 |
get命令可以获取节点内容:
有两个参数:-s(获取节点数据以及节点信息)、-w(监听节点数据变化)
[zk: localhost:2181(CONNECTED) 13] create /root "11"
Created /root
[zk: localhost:2181(CONNECTED) 15] get /root
11
3.2. create命令
创建节点命令格式:create [-s] [-e] [-c] [-t ttl] path [data] [acl]
-s
参数:顺序节点
-e
参数:临时节点
acl
参数:用来控制权限
创建节点如果不赋值的话,节点的值就是null。
[zk: localhost:2181(CONNECTED) 20] create /root
Created /root
[zk: localhost:2181(CONNECTED) 21] get /root
null
[zk: localhost:2181(CONNECTED) 22] create /node "hello world"
Created /node
[zk: localhost:2181(CONNECTED) 23] get /node
hello world
[zk: localhost:2181(CONNECTED) 24]
创建带序号的节点(永久节点+序号):
[zk: localhost:2181(CONNECTED) 39] create /root # 需要先创建普通节点
Created /root
[zk: localhost:2181(CONNECTED) 40] create -s /root/node- "aaaaa" # 创建带序号的节点
Created /root/node-0000000000
[zk: localhost:2181(CONNECTED) 41] create -s /root/node- "bbbbb"
Created /root/node-0000000001
[zk: localhost:2181(CONNECTED) 42] create -s /root/node- "ccccc"
Created /root/node-0000000002
[zk: localhost:2181(CONNECTED) 43]
创建临时节点(带序号/不带序号):
[zk: localhost:2181(CONNECTED) 58] create /root
Created /root
[zk: localhost:2181(CONNECTED) 61] create -e /root/node "11" # 临时无序节点不能多次设置值
Created /root/node
[zk: localhost:2181(CONNECTED) 62] create -e /root/node "22"
Node already exists: /root/node
[zk: localhost:2181(CONNECTED) 63] create -e -s /root/node "22" # 临时有序节点
Created /root/node0000000002
[zk: localhost:2181(CONNECTED) 64] create -e -s /root/node "22"
Created /root/node0000000003
[zk: localhost:2181(CONNECTED) 65] create -e -s /root/node "22"
Created /root/node0000000004
[zk: localhost:2181(CONNECTED) 66] create -e -s /root/node "33"
Created /root/node0000000005
[zk: localhost:2181(CONNECTED) 67] ls /root # 查看所有节点
[node, node0000000002, node0000000003, node0000000004, node0000000005]
3.3. delete和deleteall命令
删除有两个命令:delete
和deleteall
[zk: localhost:2181(CONNECTED) 8] ls /root
[node-0000000006, node-0000000007, node-0000000008]
[zk: localhost:2181(CONNECTED) 10] delete /root/node-0000000006
[zk: localhost:2181(CONNECTED) 11] deleteall /root
[zk: localhost:2181(CONNECTED) 12] ls /root
Node does not exist: /root
3.4. set命令
修改节点数据值,有两个参数:-s(设置并显示节点状态)、-v(使用CAS设置数据,可以使用stat从dataVersion找到版本)
[zk: localhost:2181(CONNECTED) 13] create /root "11"
Created /root
[zk: localhost:2181(CONNECTED) 15] get /root
11
[zk: localhost:2181(CONNECTED) 16] set /root "22"
[zk: localhost:2181(CONNECTED) 17] get /root
22
3.5. 其他命令
查看节点状态信息:stat
[zk: localhost:2181(CONNECTED) 18] stat /root
cZxid = 0x3b
ctime = Tue Sep 21 21:47:04 CST 2021
mZxid = 0x3c
mtime = Tue Sep 21 21:47:26 CST 2021
pZxid = 0x3b
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 2
numChildren = 0
查看操作历史:history
[zk: localhost:2181(CONNECTED) 19] history
9 - delete /root/node-
10 - delete /root/node-0000000006
11 - deleteall /root
12 - ls /root
13 - create /root "11"
14 - ls /root
15 - get /root
16 - set /root "22"
17 - get /root
18 - stat /root
19 - history
推出操作:quit
[zk: localhost:2181(CONNECTED) 31] quit
WATCHER::
WatchedEvent state:Closed type:None path:null
2021-09-21 23:31:15,799 [myid:] - INFO [main:ZooKeeper@1422] - Session: 0x1000abff6690001 closed
2021-09-21 23:31:15,799 [myid:] - INFO [main-EventThread:ClientCnxn$EventThread@524] - EventThread shut down for session: 0x1000abff6690001
四. 权限控制
zk类似于unix文件系统,节点类比文件,客户端可以删除节点,创建节点,修改节点。zk可以使用ACL(access control list)访问控制列表来对节点的权限进行控制。
4.1. ACL概述
ACL权限控制包含三个方面:
-
权限模式(scheme):授权的策略
-
权限对象(id):权限的对象
-
权限(permission):授予的权限
ACL授权注意点:
-
zk的权限控制是基于znode节点的,需要对每个节点设置权限
-
每个znode支持设置多种权限控制方案和多个权限
-
子节点不会继承父节点权限,客户端无法访问某一个节点,但是可以访问他的子节点
权限模式的种类:
-
world:授权对象是anyone,表示登录到服务器的所有客户端都能对该节点执行某种权限
-
ip:对客户端进行IP认证
-
auth:用已经认证后的用户进行认证
-
digest:账号密码认证
权限类型:
-
read:简写r,读取节点及显示子节点列表的权限
-
write:简写w,写权限
-
create:简写c,创建权限
-
delete:简写d,删除权限
-
admin:简写a,设置ACL的权限
4.2. 命令操作
查看节点ACL权限信息:
[zk: localhost:2181(CONNECTED) 1] create /root "admin"
Created /root
[zk: localhost:2181(CONNECTED) 2] getAcl /root
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 3]
可以看到默认节点的ACL是:world:anyone:cdrwa
设置节点的ACL权限:
设置world权限:取消节点读取权限,当通过get获取数据时会发生异常
[zk: localhost:2181(CONNECTED) 1] create /root "admin"
Created /root
[zk: localhost:2181(CONNECTED) 2] getAcl /root
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 4] setAcl /root world:anyone:cdwa
[zk: localhost:2181(CONNECTED) 5] get /root
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /root
[zk: localhost:2181(CONNECTED) 6]
设置IP权限:设置之后只能由这个IP访问
[zk: localhost:2181(CONNECTED) 6] create /node "1"
Created /node
[zk: localhost:2181(CONNECTED) 7] setAcl /node ip:192.168.0.117:adc
[zk: localhost:2181(CONNECTED) 8] get /node
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /node
设置digest权限:基于账号密码的授权模式,这里的密码只能使用加密之后的密码
# 先创建一个账号密码
➜ ~ echo -n root:123456 | openssl dgst -binary -sha1 | openssl base64
u53OoA8hprX59uwFsvQBS3QuI00=
[zk: localhost:2181(CONNECTED) 3] create /lala "1"
Created /lala
[zk: localhost:2181(CONNECTED) 4] setAcl /lala digest:root:u53OoA8hprX59uwFsvQBS3QuI00=:adcwr
[zk: localhost:2181(CONNECTED) 5] getAcl /lala
Insufficient permission : /lala
[zk: localhost:2181(CONNECTED) 6] addauth digest root:123456
[zk: localhost:2181(CONNECTED) 7] get /lala
1
[zk: localhost:2181(CONNECTED) 8] getAcl /lala
'digest,'root:u53OoA8hprX59uwFsvQBS3QuI00=
: cdrwa
[zk: localhost:2181(CONNECTED) 9]
设置auth授权模式:
[zk: localhost:2181(CONNECTED) 0] create /aaa "1"
Created /aaa
[zk: localhost:2181(CONNECTED) 1] addauth digest admin:123456
[zk: localhost:2181(CONNECTED) 2] setAcl /aaa auth:admin:adcwr
[zk: localhost:2181(CONNECTED) 3] get /aaa
1
[zk: localhost:2181(CONNECTED) 4] getAcl /aaa
'digest,'admin:0uek/hZ/V9fgiM35b0Z2226acMQ=
: cdrwa
[zk: localhost:2181(CONNECTED) 5]
退出之后重新登录:
[zk: localhost:2181(CONNECTED) 0] get /aaa
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /aaa
[zk: localhost:2181(CONNECTED) 1] addauth digest admin:123456
[zk: localhost:2181(CONNECTED) 2] get /aaa
1
[zk: localhost:2181(CONNECTED) 3]
删除节点ACL权限:
取消的话,这是默认权限就可以了。
[zk: localhost:2181(CONNECTED) 7] get /lala
1
[zk: localhost:2181(CONNECTED) 8] getAcl /lala
'digest,'root:u53OoA8hprX59uwFsvQBS3QuI00=
: cdrwa
[zk: localhost:2181(CONNECTED) 9] setAcl /lala world:anyone:cdwar
[zk: localhost:2181(CONNECTED) 10] get /lala
1
[zk: localhost:2181(CONNECTED) 11]
五. 监听机制
zookeeper提供了数据的发布/订阅功能,多个订阅者可以同时监听某一特定数据节点,当数据节点自身发生变化,会主动通知所有订阅者。当前通知时异步通知,客户端不必再watcher注册后轮询阻塞,从而减轻了客户端压力,可以看做是观察者模型的实现。
5.1. 特点
-
一次性:监听是一次性的,一旦触发就会移除,再次使用时需要重新注册
-
顺序回调:回调是顺序串行执行
-
轻量级:结构上
-
时效性:监听只有在当前session失效时才会无效,如果在seesion有效期内快速重新连接成功,仍可以接收到通知
5.2. 监听流程
-
当客户端启动之后会创建两个线程,一个负责网络连接通信(
connet
),另一个负责监听(listener
)。 -
通过
connect
线程将注册的监听事件发生给zookeeper
-
在
zookeeper
的注册监听器列表中将注册的监听事件添加到列表中 -
zookeeper
监听到有数据或路径变化,就会将这个消息发送给listener
线程 -
listener
线程内部调用了process()
方法。
5.3. 客户端演示
一次性监听,触发后会被删除,无法再次触发。
命令 | 描述 |
---|---|
ls -w path | 监听子节点的变化(增、删)[监听目录] |
get -w path | 监听节点数据的变化 |
stat -w path | 监听节点属性的变化 |
5.3.1. 监听节点变化
# 写数据节点
[zk: localhost:2181(CONNECTED) 5] create /root
Created /root
[zk: localhost:2181(CONNECTED) 14] set /root "77"
# 在另一个监听节点
[zk: localhost:2181(CONNECTED) 8] get -w /root
null
[zk: localhost:2181(CONNECTED) 9]
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/root
5.3.2. 监听子节点变化
# 写数据节点
[zk: localhost:2181(CONNECTED) 16] create /root
Created /root
[zk: localhost:2181(CONNECTED) 17] create /root/node1 "123"
Created /root/node1
# 在另一个节点监听
[zk: localhost:2181(CONNECTED) 10] ls -w /root
[node1, node2]
[zk: localhost:2181(CONNECTED) 11]
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/root
5.3.3. 永久监听
在Zookeeper 3.6.0版本之后,客户端可以在节点上创建永久监听,永久监听在被触发后不会被删除。
六. 客户端API操作
下面演示一个Java对zk的CRUD操作,并简单介绍一下API参数。
6.1. 准备工作
搭建一个简单的maven项目,添加下面依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.7.0</version>
<exclusions>
<!-- 去掉日志避免和springboot中的产生冲突 -->
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
增加配置项:
zookeeper.address=192.168.0.117:2181
zookeeper.timeout=4000
增加获取连接的Bean:
@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "zookeeper")
public class ZkConfig {
private String address;
private Integer timeout;
@Bean(name = "zookeeperConn")
public ZooKeeper zookeeperClient() {
ZooKeeper zooKeeper = null;
try {
CountDownLatch countDownLatch = new CountDownLatch(1);
zooKeeper = new ZooKeeper(address, timeout, (event) -> {
if (Watcher.Event.KeeperState.SyncConnected == event.getState()) {
countDownLatch.countDown();
}
});
countDownLatch.await();
log.info("初始化zk连接:{}", zooKeeper.getState());
} catch (IOException | InterruptedException e) {
log.info("获取ZK连接失败: {}", e.getMessage());
}
return zooKeeper;
}
}
这样准备工作就做好了,下面开始CRUD大法!
6.2. 创建节点
创建方法多种大体分页两类:同步创建和异步创建。
// 同步创建
String create(String path, byte[] data, List<ACL> acl, CreateMode createMode);
String create(String path, byte[] data, List<ACL> acl, CreateMode createMode, Stat stat);
String create(String path, byte[] data, List<ACL> acl, CreateMode createMode, Stat stat, long ttl);
// 异步创建
void create(String path, byte[] data, List<ACL> acl, CreateMode createMode, AsyncCallback.Create2Callback cb, Object ctx);
void create(String path, byte[] data, List<ACL> acl, CreateMode createMode, AsyncCallback.StringCallback cb, Object ctx);
void create(String path, byte[] data, List<ACL> acl, CreateMode createMode, AsyncCallback.Create2Callback cb, Object ctx, long ttl);
这里需要看一下CreateMode这个枚举类,定义了节点类型:
public enum CreateMode {
PERSISTENT(0, false, false, false, false),
PERSISTENT_SEQUENTIAL(2, false, true, false, false),
EPHEMERAL(1, true, false, false, false),
EPHEMERAL_SEQUENTIAL(3, true, true, false, false),
CONTAINER(4, false, false, true, false),
PERSISTENT_WITH_TTL(5, false, false, false, true),
PERSISTENT_SEQUENTIAL_WITH_TTL(6, false, true, false, true);
}
这里一共有七种类型:
-
PERSISTENT
:持久节点(也有叫永久节点的),不会随着会话的结束而自动删除 -
PERSISTENT_SEQUENTIAL
:带有递增序号的持久节点,不会随着会话的结束而自动删除。 -
EPHEMERAL
:临时节点,会随着会话的结束而自动删除 -
EPHEMERAL_SEQUENTIAL
:带有递增序号的临时节点,会随着会话的结束而自动删除。 -
CONTAINER
:容器节点,用于Leader、Lock等特殊用途,当容器节点不存在任何子节点时,容器将成为服务器在将来某个时候删除的候选节点。 -
PERSISTENT_WITH_TTL
:带TTL(time-to-live,存活时间)的持久节点,节点在TTL时间之内没有得到更新并且没有子节点,就会被自动删除。 -
PERSISTENT_SEQUENTIAL_WITH_TTL
:带TTL(time-to-live,存活时间)和递增序号的持久节点,节点在TTL时间之内没有得到更新并且没有子节点,就会被自动删除。
创建节点时候需要注意:
-
如果指令路径和版本的节点已经存在,则会抛出一个
KeeperException
异常 -
临时节点不能有子节点,如果给临时节点创建子节点会抛
KeeperException
异常。 -
临时节点的生命周期与客户端会话绑定。一旦客户端会话失效(客户端与 Zookeeper连接断开不一定会话失效),那么这个客户端创建的所有临时节点都会被移除。
-
byte[] data
允许的最大数据量为1MB(1,048,576 bytes),如果超过,会抛KeeperExecption
。
@GetMapping("create")
public String create(@RequestParam("path") String path, @RequestParam("data") String data) {
try {
return "创建成功";
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
return "创建失败";
}
}
其他的操作看zk的api接口就可以了!
6.6. 监听器
监听主要是针对节点而言,前面在判断节点是否存在、修改数据时都可以设置监听器,但是他们是一次性的,如果我们希望长久有效,则可以使用下面的addWatch
@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "zookeeper")
public class ZkConfig {
private String address;
private Integer timeout;
@Bean(name = "zookeeperConn")
public ZooKeeper zookeeperClient() {
ZooKeeper zooKeeper = null;
try {
CountDownLatch countDownLatch = new CountDownLatch(1);
zooKeeper = new ZooKeeper(address, timeout, (event) -> {
if (Watcher.Event.KeeperState.SyncConnected == event.getState()) {
countDownLatch.countDown();
}
});
countDownLatch.await();
// 增加部分
zooKeeper.addWatch("/six",
event -> log.info("监听到节点变化: {}", event.getState()),
AddWatchMode.PERSISTENT_RECURSIVE);
log.info("初始化zk连接:{}", zooKeeper.getState());
} catch (IOException | InterruptedException | KeeperException e) {
log.info("获取ZK连接失败: {}", e.getMessage());
}
return zooKeeper;
}
}
监听模式有两中种:
-
AddWatchMode.PERSISTENT
: 表示只关心当前节点的删除、数据变更,创建,一级子节点的创建、删除;无法感知子节点的子节点创建、删除,无法感知子节点的数据变更 -
AddWatchMode.PERSISTENT_RECURSIVE
: 相当于递归监听,改节点及其子节点的所有变更都监听
另外在zk还可以在exists中增加监听器,但是是一次性的:
public Stat exists(final String path, Watcher watcher)
七. 写数据的流程
第一步:客户端发出写入数据请求给集群中的任意一个server,如果当前server是follower,follower会将写请求转发给leader;
第二步:leader会将数据广播follower中,leader采用两个阶段提交方式,先发送Propose(事务)广播给follower,follower接收到Propose消息,写入日志成功之后,返回ack消息给leader;
第三步:当leader收到半数以上ack消息,返回成功返回给客户端,并广播commit请求给follower
第四步:zk会进一步通知客户端写成功了。
八. 客户端可视化工具
下面推荐几个常用的zookeeper客户端可视化工具:
客户端 | 地址 |
---|---|
PrettyZoo | https://github.com/vran-dev/PrettyZoo |
zk-view-tool | https://github.com/yangyuscript/zk-view-tool |
zookeeper-visualizer | https://github.com/yuanhongxiang/zookeeper-visualizer |
idea插件 | zookeeper tools |
推荐PrettyZoo,JavaFx使用的,挺好看的,颜值党,哈哈!
更多推荐
所有评论(0)