zookeeper 学习总结(四)——基本使用
ZooKeeper数据模型的结构与Unix文件系统很类似,整体上可以看作是一棵树,每个节点称做一个 ZNode,每个ZNode都可以通过其路径唯一标识。
Znode 节点类型
-
持久化目录节点( PERSISTENT) 客户端与zookeeper断开连接后,该节点依旧存在
-
持久化顺序编号目录节点( PERSISTENT_SEQUENTIAL) 客户端与zookeeper断开连接后,该节点依旧存在,Zookeeper会给该节点按照顺序编号
-
临时目录节点( EPHEMERAL) 客户端与zookeeper断开连接后,该节点被删除
-
临时顺序编号目录节点( EPHEMERAL_SEQUENTIAL) 客户端与zookeeper断开连接后,该节点被删除,Zookeeper会给该节点按照顺序编号
命令行使用
通过在zk的/bin目录下的zkCli.sh进入zookeeper客户端命令行,输入help查看zookeeper客户端的指令
如上图列出了 zookeeper所有的客户端命令行,下面主要讲解常见的几个命令行
查看节点
1、查看某个节点下的所有子节点信息
ls path [watch]
2、查看当前节点更详细数据,比如根据dataVersion能看到更新次数等数据
ls2 path [watch]
#描述
czxid 创建该节点的事物ID
ctime 创建该节点的时间
mZxid 更新该节点的事物ID
mtime 更新该节点的时间
pZxid 操作当前节点的子节点列表的事物ID(这种操作包含增加子节点,删除子节点)
cversion 当前节点的子节点版本号
dataVersion 当前节点的数据版本号
aclVersion 当前节点的acl权限版本号
ephemeralowner 当前节点的如果是临时节点,该属性是临时节点的事物ID
dataLength 当前节点的d的数据长度
numchildren 当前节点的子节点个数
创建节点
create [-s] [-e] path data acl -s 表示是顺序节点 -e 标识是临时节点 path 节点路径 data 节点数据 acl 节点权限
注:临时节点在客户端结束与服务器的会话后,自动消失
查询节点的值
get path [watch]
修改节点的值
修改当前节点的数据内容 如果指定版本,需要和当前节点的数据版本一致
set path data [version]
查看节点状态
stat path
-
ls2
与stat
命令输出的基本是一样的;也就是说ls2
整合了stat
命令; -
get与stat的区别:get不仅显示状态值也显示节点的值
删除节点
1、delete 只能删除自身下面不存在其他节点的节点
delete path [version]
2、递归删除节点
rmr path [version]
watch
watcher使用场景
当主机更新节点为新的配置信息时会触发 watcher 事件,客户端 1, 客户端 2, 客户端 3会监听 watcher事件,并更新配置;
get path [watch]
触发时机:删除、更新节点的值
ls path [watch]
触发时机:创建、删除子节点
api 使用
java接口
Stat exists(String path,Watcher watcher) | 重载方法,这里给某个目录节点设置特定的 watcher,Watcher 在ZooKeeper 是一个核心功能,Watcher 可以监控目录节点的数据变化以及子目录的变化,一旦这些状态发生变化,服务器就会通知所有设置在这个目录节点上的 Watcher,从而每个客户端都很快知道它所关注的目录节点的状态发生变化而做出相应的反应 |
void delete(String path, int version) | 删除 path 对应的目录节点,version 为 -1 可以匹配任何版本,也就删除了这个目录节点所有数据 |
ListgetChildren(String path, boolean watch) | 获取指定 path 下的所有子目录节点,同样 getChildren方法也有一个重载方法可以设置特定的 watcher 监控子节点的状态 |
Stat setData(String path, byte[] data, int version) | 给 path 设置数据,可以指定这个数据的版本号,如果 version 为 -1 怎可以匹配任何版本 |
byte[] getData(String path, boolean watch, Stat stat) | 获取这个 path 对应的目录节点存储的数据,数据的版本等信息可以通过 stat 来指定,同时还可以设置是否监控这个目录节点数据的状态 |
ZooKeeper zk = new ZooKeeper(String connectString, int sessionTimeout, Watcher watcher) | 创建zookeeper连接,connectString表示连接的zookeeper服务器的地址,sessionTimeOut指定会话的的超时时间,Watcher配置监听 |
String create(String path, byte[] data, List acl,CreateMode createMode) | 创建一个给定的目录节点 path, 并给它设置数据,CreateMode 标识有四种形式的目录节点,分别是 PERSISTENT:持久化目录节点,这个目录节点存储的数据不会丢失;PERSISTENT_SEQUENTIAL:顺序自动编号的目录节点,这种目录节点会根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名;EPHEMERAL:临时目录节点,一旦创建这个节点的客户端与服务器端口也就是 session 超时,这种节点会被自动删除;EPHEMERAL_SEQUENTIAL:临时自动编号节点 |
Stat exists(String path, boolean watch) | 判断某个 path 是否存在,并设置是否监控这个目录节点,这里的watcher 是在创建 ZooKeeper 实例时指定的 watcher,exists 方法还有一个重载方法,可以指定特定的watcher |
例子
maven依赖
<dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.14</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
用例代码
import org.apache.zookeeper.*; import org.apache.zookeeper.data.Stat; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.util.List; import java.util.concurrent.CountDownLatch; public class zkApiTest { private static final String connectString = "192.168.126.99:2181"; private static final int sessionTimeout = 5000; //对执行中的线程进行管理,等待线程完成某些操作后,再对此线程做处理(起到过河拆桥、卸磨杀驴的作用) private static CountDownLatch connectedSemaphore = new CountDownLatch(1); private static ZooKeeper zk = null; @Before public void init() throws Exception { //创建zookeeper的连接 zk = new ZooKeeper(connectString,sessionTimeout,new Watcher() { @Override public void process(WatchedEvent event) { System.out.println("触发事件:" + event.getType()); System.out.println("Receive watched event : " + event); if (Event.KeeperState.SyncConnected == event.getState()) { System.out.println("连接成功:不再阻塞当前线程"); /** * 当客户端连接上了zookeeper服务器,Watcher接口会使用process()方法接收一个连接成功的事件, * 接下来调用CountDownLatch释放之前的阻塞; */ connectedSemaphore.countDown(); } } }); System.out.println("zk的连接状态:"+zk.getState()); /** * 在连接函数中创建了zookeeper的实例;然后建立与服务器的连接; * 建立连接函数会立即返回,所以我们需要等待连接建立成功后再进行其他的操作; * 我们使用connectedSemaphore.await()来阻塞当前线程,直到zookeeper准备就绪; */ connectedSemaphore.await(); System.out.println("====================zk连接成功===================="); } @Test public void testZkApi() throws Exception { //完成zk 的操作 // 1.1 创建一个节点 //Thread.sleep(5*60000);// // String path = zk.create("/node5", "hzp5".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // System.out.println(path); //判断节点是否存在 如果指定路径的节点存在返回节点的状态,如果节点不存在返回null // Stat exists = zk.exists("/node5", true); // System.out.println(exists); //获取指定路径下的所有子节点的名称 // List<String> children = zk.getChildren("/", true); // for (int i = 0; i < children.size(); i++) { // String node = children.get(i); // System.out.println(node); // } //设置节点的数据 // Stat stat1 = zk.setData("/itheima", "itheima01update".getBytes(), -1); // System.out.println(stat1); //获取节点的数据 byte[] data = zk.getData("/node2", true, new Stat()); System.out.println(new String(data)); // zk.delete("/itheima/heima02",-1); } }
特别注意:
上面的代码 connectedSemaphore.await();对于避免连接建立完成之前就发出ZooKeeper操作命令出现ConnectionLoss是有用的。
因为new出ZooKeeper实例时只是建立了与服务端之间的会话,此时TCP连接可能还未建立完成,如果这时发出ZooKeeper操作命令的确会出现连接丢失异常,虽然这种概率相对较小。
当客户端与服务端连接建立之后客户端会收到一个SyncConnected事件,此时将connectedSemaphore减到零就可以让阻塞的主线程继续运行,再来发出ZooKeeper操作命令就不会出现连接丢失的异常了。