在《zookeeper介绍及环境搭建》和《zookeeper客户端的使用》两篇文章中,我介绍了zookeeper实验环境的搭建、zookeeper的数据结构和zookeeper的一些操做命令。本篇文章我将对zookeeper java api进行详细的介绍。相关代码java
zookeeper.jar中包含了zookeeper提供的java api,为了在项目中引入zookeeper.jar,在工程的pom.xml文件中加入下面的依赖:node
<dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.6</version> </dependency>
构造ZooKeeper类对象的过程就是与ZK服务器创建链接的过程。git
ZooKeeper zooKeeper = new ZooKeeper("192.168.1.108:2181", 5000, watcher);
ZooKeeper类的构造函数一共有三个参数:第一个参数是服务器的地址,第二个参数是session超时时间,第三个参数是org.apache.zookeeper.Watcher类型的对象。zookeeper api与服务器创建链接是异步的,上面的调用会立刻从ZooKeeper构造函数返回,当与服务器创建好链接以后会调用Watcher中的process方法进行处理。process方法会接受一个WatchedEvent类型的参数,用于代表发生了什么事件。github
public void process(WatchedEvent watchedEvent) { if (watchedEvent.getState() == Event.KeeperState.SyncConnected) { //判断是否已链接 if(watchedEvent.getType() == Event.EventType.None && null == watchedEvent.getPath()) { // 最初与zk服务器创建好链接 } else if(watchedEvent.getType() == Event.EventType.NodeChildrenChanged) { // 子节点变化事件 } // ...还能够继续监听其它事件类型 } System.out.println(watchedEvent.getState()); }
WatchedEvent包含两方面重要信息:apache
watchedEvent.getState()
方法获取与zk服务器链接的状态信息,状态信息取值主要包括SyncConnected、Disconnected、ConnectedReadOnly和AuthFailed等等。watchedEvent.getState()
方法只是获取与zk服务器链接的状态信息,但在同一个链接状态下,还会发生不少事件的类型。例如在zk中,咱们能够watch一个节点的数据内容,当这个节点的数据被改变时,咱们能够获取到这个事件。相似的还有子节点列表变化事件等等。watchedEvent.getType()
方法获取具体的事件类型。事件类型的取值包括None、NodeCreated、NodeDeleted、NodeDataChanged和NodeChildrenChanged。下面要介绍的每种api操做均可以分为两种类型——同步和异步。同步操做通常会有返回值,而且会抛出相应的异常。异步操做没有返回值,也不会抛出异常。此外异步方法参数在同步方法参数的基础上,会增长Callback和context两个参数。
如用同步方式建立一个节点的的代码以下:segmentfault
private void createNodeSync() throws KeeperException, InterruptedException { String path = "/poype_node"; String nodePath = zooKeeper.create(path, "123".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); System.out.println(nodePath); }
这里的zooKeeper
就是经过ZooKeeper构造函数构造的对象,能够调用它的create()方法建立一个节点。同步版的create()方法一共有4个参数,第一个参数是要建立的节点路径。第二个参数是建立节点的数据值,参数类型是字节数组。第三个参数是这个节点的访问权限,咱们这里指定该节点能够被任何人访问(关于节点的访问权限,我将在下一篇文章进行详细介绍)。第四个参数是建立节点的类型,在上一篇文章中,我提到过create命令能够有-s和-e两个参数,其中-s是顺序节点,-e是临时节点。这里的CreateMode就是这两个参数的组合,它有下面四种取值:PERSISTENT、PERSISTENT_SEQUENTIAL、EPHEMERAL和EPHEMERAL_SEQUENTIAL。
异步模式建立一个节点的代码以下,注意异步模式方法没有返回值,而且不会抛出任何异常:api
private void createNodeAsync() { String path = "/poype_node2"; zooKeeper.create(path, "123".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, new AsyncCallback.StringCallback() { public void processResult(int resultCode, String path, Object ctx, String name) { System.out.println(resultCode); System.out.println(path); System.out.println(ctx); System.out.println(name); } }, "建立"); }
除了同步create方法中的四个参数之外,异步模式的create方法还增长了callback和context两个参数。StringCallback接口中的processResult方法会在节点建立好以后被调用,它有四个参数。第一个是int类型的resultCode,做为建立节点的结果码,当成功建立节点时,resultCode的值为0。第二个参数是建立节点的路径。第三个参数是context,当一个StringCallback类型对象做为多个create方法的参数时,这个参数就颇有用了。第四个参数是建立节点的名字,其实与path参数相同。数组
同步获取一个节点数据值的代码以下:服务器
private void getDataSync() throws KeeperException, InterruptedException { Stat stat = new Stat(); // getData的返回值是该节点的数据值,节点的状态信息会赋值给stat对象 byte[] data = zooKeeper.getData("/node_1",true, stat); System.out.println(new String(data)); System.out.println(stat); }
zooKeeper.getData方法的返回值就是节点中存储的数据值,它有三个参数,第一个参数是节点的路径,用于表示要获取哪一个节点中的数据。第三个参数stat用于存储节点的状态信息,在调用getData方法前,会先构造一个空的Stat类型对象做为参数传给getData方法,当getData方法调用返回后,节点的状态信息会被填充到stat对象中。
第二个参数是一个bool类型的watch,这个参数比较重要。当watch为true时,表示咱们想要监控这个节点的数据变化。当节点的数据发生变化时,咱们就能够拿到zk服务器推送给咱们的通知。在process方法中会有相似下面的代码:session
public void process(WatchedEvent watchedEvent) { if (watchedEvent.getState() == Event.KeeperState.SyncConnected) { //与zk服务器处于链接状态 if(watchedEvent.getType() == Event.EventType.None && null == watchedEvent.getPath()) { createNodeAsync(); } else if(watchedEvent.getType() == Event.EventType.NodeChildrenChanged) { // 节点的子节点列表发生变化 } else if(watchedEvent.getType() == Event.EventType.NodeDataChanged) { // 节点的数据内容发生变化 } } }
当节点的数据内容发生变化时,咱们就会接收到NodeDataChanged这个事件。值得注意的是,zooKeeper设置的监听只生效一次,若是在接收到NodeDataChanged事件后还想继续对该节点的数据内容改变进行监听,须要在事件处理逻辑中从新调用getData方法并将watch参数设置为true。
异步获取一个节点数据值的代码以下:
private void getDataAsync() { zooKeeper.getData("/node_1", true, new AsyncCallback.DataCallback() { public void processResult(int resultCode, String path, Object ctx, byte[] data, Stat stat) { System.out.println(resultCode); System.out.println(path); System.out.println(ctx); System.out.println(new String(data)); System.out.println(stat); } }, "异步获取节点的数据值"); }
异步getData方法中的最后一个参数是context,我这里将其设置为"异步获取节点的数据值"。
同步获取一个节点的子节点列表:
private void getChildrenSync() throws KeeperException, InterruptedException { List<String> childrenNode = zooKeeper.getChildren("/",true); for(String child : childrenNode) { System.out.println(child); } }
方法getChildren的第二个参数一样为watch,当节点的子节点列表发生变化时,zk服务器会向咱们推送类型为NodeChildrenChanged的事件。
异步获取一个节点的子节点列表:
private void getChildrenAsync() { zooKeeper.getChildren("/", true, new AsyncCallback.Children2Callback() { public void processResult(int resultCode, String path, Object ctx, List<String> children, Stat stat) { System.out.println(resultCode); System.out.println(path); System.out.println(ctx); for(String child : children) { System.out.println(child); } System.out.println(stat); } }, "获取/下面的子节点"); }
同步查看一个节点是否存在:
private void existSync() throws KeeperException, InterruptedException { Stat stat = zooKeeper.exists("/poype_node2", true); System.out.println(stat); }
exists方法的watch参数比较特别,若是将其指定为true,那么表明你对该节点的建立事件、节点删除事件和该节点的数据内容改变事件都感兴趣,因此会同时响应三种事件类型。请看process方法中对事件的处理:
public void process(WatchedEvent watchedEvent) { if (watchedEvent.getState() == Event.KeeperState.SyncConnected) { if(watchedEvent.getType() == Event.EventType.None && null == watchedEvent.getPath()) { existAsync(); } else if(watchedEvent.getType() == Event.EventType.NodeCreated) { System.out.println("监控到了该节点被建立"); existAsync(); } else if(watchedEvent.getType() == Event.EventType.NodeDataChanged) { System.out.println("监控到了该节点的数据内容发生变化"); existAsync(); } else if(watchedEvent.getType() == Event.EventType.NodeDeleted) { System.out.println("监控到了该节点被删除"); existAsync(); } } }
异步查看一个节点是否存在:
private void existAsync() { zooKeeper.exists("/poype_node2", true, new AsyncCallback.StatCallback() { public void processResult(int resultCode, String path, Object ctx, Stat stat) { System.out.println(resultCode); System.out.println(path); System.out.println(ctx); System.out.println(stat); } }, "异步查看一个节点是否存在"); }
同步修改节点的数据内容:
private void setDataSync() throws KeeperException, InterruptedException { Stat stat = zooKeeper.setData("/poype_node2", "poype5211314".getBytes(), 1); System.out.println(stat); }
setData方法有三个参数,前两个参数分别是节点的路径和要修改的数据值,最后一个参数是version字段。在上一篇文章我提到过,在节点的状态信息中包含dataVersion字段,是该节点的数据内容版本号。在调用setData方法修改节点数据内容时,只有当version参数的值与节点状态信息中的dataVersion值相等时,数据修改才能成功,不然会抛出BadVersion异常。这是为了防止丢失数据的更新,在ZooKeeper提供的API中,全部的写操做(例如后面要提到的delete)都有version参数。
异步修改节点的数据内容:
private void setDataAsync() { zooKeeper.setData("/poype_node2", "poype5211314".getBytes(), 3, new AsyncCallback.StatCallback() { public void processResult(int resultCode, String path, Object ctx, Stat stat) { System.out.println(resultCode); System.out.println(path); System.out.println(ctx); System.out.println(stat.getVersion()); // 获取数据节点版本号 } }, "异步设置一个节点的数据"); }
同步方式删除一个节点:
private void deleteSync() throws KeeperException, InterruptedException { zooKeeper.delete("/node_1", 12); }
delete方法的第二个参数也是version,含义与setData方法中的version参数相似。
异步方式删除一个节点:
private void deleteAsync() { zooKeeper.delete("/poype_node", 3, new AsyncCallback.VoidCallback() { public void processResult(int resultCode, String path, Object ctx) { System.out.println(resultCode); System.out.println(path); System.out.println(ctx); } }, "异步删除一个节点"); }
本文介绍了ZooKeeper Java API的基本使用方式,在下一篇文章中,我将详细介绍ZooKeeper中节点权限的概念和使用方法。