Zookeeper(三):常用JAVA API
-
Zookeeper提供了Java API方便我们来操作zk服务。
-
通过org.apache.zookeeper.Zookeeper类创建连接zk服务器的示例对象
-
-
当连接创建成功后,通过Zookeeper实例提供的接口来和服务器进行交互。
Zookeeper类
-
org.apache.zookeeper.Zookeeper类表示连接,该类有几个构造方法,不过一般最常用的是下面两个构造方法:
-
ZooKeeper(connectString,session-Timeout,watcher)
-
其实这个也是调下面的方法,只是canBeRead-Only是false
-
-
ZooKeeper(connectString,sessionTimeout,watcher,canBeRead-Only);
-
connectString参数为zk集群服务器的连接url,当给定路径的时候,表示所有的操作都是基于该路径进行操作的(路径只可以添加到最后)。例如: “hh:2181,hh:2182,hh:2183/app”;
-
sessionTimeout为会话过期时间,一般设置为tickTime的3-4倍;
-
watcher是监视器,用于触发相应事件,可以为空;
-
canBeReadOnly是给定是否是只读连接,默认为false。
-
-
示例
1.导入依赖
<!--注入zookeeper--> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.6.0</version> </dependency>
2.连接和创建节点
package com.rzp.service; import org.apache.zookeeper.*; import java.io.IOException; public class DemoConnection { public static void main(String[] args) throws Exception { //连接地址,写了多个以后,连接时会随机选择一个去连接 String url = "192.168.98.129:2181,192.168.98.130:2182,192.168.98.130:2183"; //创建连接,注意sessionTimeout(第二个参数)不能设置太小。。不然一下子就关闭了,后面全都执行不下去。。 //第三个参数是Watcher类的对象,后文再说明 ZooKeeper client = new ZooKeeper(url, 48000, new Watcher() { @Override public void process(WatchedEvent event) {} }); System.out.println("getSessionId=="+client.getSessionId()); //在根目录下创建连接 /* 参数说明 1.path 节点,要创建的节点类型 2.data 节点数据,该节点存储的数据 3.acl 权限控制,一般使用下面这个就可以了,没有权限控制 4.CreateMode 节点类型,使用CreateMode下的枚举类就可以了 返回值就是创建后的节点名称 */ String result = client.create("/root", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); System.out.println(result); //在root文件夹下面创建永久顺序子节点child result = client.create("/root/child","child".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT_SEQUENTIAL); System.out.println("添加child节点="+result); //创建临时节点 result = client.create("/tmp","tmp".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL); System.out.println("添加tmp节点="+result); //创建临时顺序节点 result = client.create("/tmp","tmp".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL); System.out.println("添加tmp顺序节点="+result); Thread.sleep(5000); //关闭连接 client.close(); } }
测试结果:
-
可以看到节点创建成功了(笔者先创建过/root了,如果第一次走会有4个输出)。
备注
url后面可以直接增加文件夹,相当于直接连接到app节点,所有操作会在app下操作。
String url = "192.168.98.129:2181/app";
3.删除节点
@Test public void test1() throws Exception{ //创建连接 String url = "192.168.98.129:2181,192.168.98.130:2182,192.168.98.130:2183"; ZooKeeper client = new ZooKeeper(url, 48000, null); //删除节点 /* path:要删除的节点 version:要删除节点的版本号,如果无视版本号删除就写-1 */ client.delete("/root/child0000000000",-1); //关闭连接 client.close(); }
查看删除是否成功
[zk: localhost:2181(CONNECTED) 1] ls /root [child0000000000] [zk: localhost:2181(CONNECTED) 2] ls /root []
4.获取节点信息
@Test public void testGetSet() throws Exception{ //创建连接 String url = "192.168.98.129:2181,192.168.98.130:2182,192.168.98.130:2183"; ZooKeeper client = new ZooKeeper(url, 48000, null); // client.create("/root/child","child".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT); /*参数说明 path:要查看的节点信息 watch:true会使用创建连接给的watch监控,false则不用,也可以直接给一个new watch stat:输入一个stat类的对象,stat类是节点的状态JavaBean,获取完成以后会把节点信息封装到这个stat里面 */ Stat stat = new Stat(); byte[] data = client.getData("/root/child", false, stat); System.out.println(new String(data)); System.out.println(stat.getCzxid()); //关闭连接 client.close(); }
5.修改节点信息
@Test public void testSet() throws Exception{ //创建连接 String url = "192.168.98.129:2181,192.168.98.130:2182,192.168.98.130:2183"; ZooKeeper client = new ZooKeeper(url, 48000, null); /*参数说明 path:要修改的节点 data:填入节点的数据 version:版本,和delete机制一样 */ Stat stat = client.setData("/root/child", "new-data".getBytes(), -1); System.out.println(stat.getCzxid()); byte[] data = client.getData("/root/child", false, stat); System.out.println(new String(data)); System.out.println(stat.getCzxid()); //关闭连接 client.close(); }
6.其他方法
addAuthInfo: 添加权限验证scheme信息
exists: 判断节点是否存在
getACL: 获取节点的权利列表
setACL: 设置节点的权限列表
getChildren: 获取子节点名称列表
getState: 获取当前连接的States信息
Stat类
-
stat类就是节点的状态JavaBean,可以封装节点的信息。
Watch
-
Watch机制和shell中的类似,就是开启一个对某节点的监视器以后,这个节点如果有特定的行为,zk会返回给客户端一条信息。
-
监视器在调用exists、getData、getChildren方法时可以开启。
-
开启以后一旦被触发,就会执行我们重写的process方法。
-
process方法默认会给一个输入参数WatchedEvent,这里面封装了zk返回来的信息
-
@Test public void testWatch() throws Exception{ //创建连接 String url = "192.168.98.129:2181,192.168.98.130:2182,192.168.98.130:2183"; ZooKeeper client = new ZooKeeper(url, 48000, new Watcher() { //监视器可以在创建连接的时候定义。 //重写process方法,我们可以让监视器被触发时做一些事情 @Override public void process(WatchedEvent event) { //WatchedEvent--zk返回的信息就是封装在这个对象里面,我们可以通过这个对象的方法获取到信息 //event.getType()--事件类型,见后文 //event.getPath()--事件发生结点 System.out.println("创建连接的时候给定的监视器:" + event.getType().name() + ";" + event.getPath()); } }); //在调用exists、getData、getChildren方法,可以开启创建链接时定义的监视器。 client.exists("/watcher",true); //也可以定义一个新的监视器 client.exists("/watcher", new Watcher() { @Override public void process(WatchedEvent event) { System.out.println("exists自定义监视器"+event.getType().name()+","+event.getPath()); } }); client.exists("/root",true); client.exists("/root/child",true); client.getChildren("/root",true); Thread.sleep(600000); //关闭连接 client.close(); }
-
event.getType()--事件有这些类型:
-
比如:使用exists方法开启的监视器,会在被监视的结点创建时触发,返回来的时间类型是NodeCreated。
-
-
同一个结点,监视器开启后,zk服务器仅会发送一次通知信息给客户端。
-
上图中的child只是第一代子节点----watch机制只监控node节点本身以及node节点的第一代子节点
测试
回到上面给的示例程序,启动以后因为线程休眠,在线程休眠的过程中,我们在shell窗口执行:
[zk: localhost:2181(CONNECTED) 22] create /root/child "" Created /root/child [zk: localhost:2181(CONNECTED) 23] delete /root/child [zk: localhost:2181(CONNECTED) 24] delete /root
可以看到输出为:
第一个输出是链接到zk时的输出。
第二条和第三条是/root/child的创建时的反馈,执行了下面代码中的2、3(前面测试代码中摘取出来的)
这时候我们删除child,就没有输出了,验证了同一个结点,开启监视器后只会反馈一次。
最后一条输出时下面代码中的1。
1 client.exists("/root",true); 2 client.exists("/root/child",true); 3 client.getChildren("/root",true);
案例
-
递归监控路径下的所有结点
package com.rzp.service; import java.io.IOException; import java.util.List; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import java.io.IOException; public class DemoWatcher implements Watcher{ private ZooKeeper client = null; private String rootPath = "/app"; public DemoWatcher() throws IOException { this.client = new ZooKeeper("192.168.98.129:2181,192.168.98.130:2182,192.168.98.130:2183", 48000, new Watcher() { @Override public void process(WatchedEvent event) { System.out.println("默认监视器:" + event.getType().name()); } }); } public void run() { registerWatcher(this.rootPath, this, true); // 添加监视器 while(true) { try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 给path路径添加监控信息,循环添加 * * @param path * @param watcher * @param recursive */ private void registerWatcher(String path, Watcher watcher, boolean recursive) { try { this.client.exists(path, watcher); this.client.getData(path, watcher, null); List<String> child = this.client.getChildren(path, watcher); //递归监控 if (recursive && child != null) { for (String childPath : child) { registerWatcher(path + "/" + childPath, watcher, recursive); } } } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void process(WatchedEvent event) { try { switch (event.getType()) { case NodeChildrenChanged: System.out.println(event.getPath() + "节点子节点列表发生改变"); this.registerWatcher(event.getPath(), this, true); // 重新添加一遍监视器 break; case NodeCreated: System.out.println(event.getPath() + "节点被创建"); this.registerWatcher(event.getPath(), this, false); break; case NodeDataChanged: System.out.println(event.getPath() + "节点内容被修改"); this.registerWatcher(event.getPath(), this, false); break; case NodeDeleted: System.out.println(event.getPath() + "节点被删除"); this.client.exists(event.getPath(), this); // 添加监视器 break; case None: System.out.println("触发none event"); break; } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) throws IOException { new DemoWatcher().run(); } }