zookeeper(三)java操作zookeeper
首先要使用java操作zookeeper,zookeeper的javaclient是我们更轻松地去对zookeeper进行各种操作,我们引入zookeeper-3.3.4.jar和zkclient-0.1.jar即可。
zookeeper-3.3.4.jar为官方提供的javaAPI,zkclient-0.1.jar则为在原生api基础上进行扩展的开源JAVA客户端。
(1)创建会话方法:
客户端可以通过一个zookeeper实例来连接zookeeper服务器。
Zookeeper(Arguments)方法(一共有4个构造方法,根据参数不同)
参数说明如下:
connectString:连接服务器列表,用"逗号"分割。
sessionTimeout:心跳检测时间周期(毫秒)。
wather:事件处理通知器。
canBeReadOnly:标识当前会话是否支持只读。
sessionId和sessionPassword:提供连接zookeeper的sessionId和密码,通过这两个确定唯一一台客户端,目的是客户提供重复会话。
注意:zookeeper客户端和服务器端会话的建立是一个异步过程,也就是说在程序中,我们程序方法在处理完成客户端初始化后立即返回(也就是说程序往下执行代码,这样,大多数情况下我们并没有真正构建好一个可用会话,在会话的生命周期处于"CONNECTING"时才算真正建立完毕,所以我们需要使用多线程中所学习的一个小工具类)
(2)创建节点(znode)方法:create
提供了两种创建节点的方法:同步和异步创建节点方式
同步方式:
参数1:节点路径(名称)/nodename
(不允许递归创建节点,也就是说在父节点不存在的情况下,不允许创建子节点)
参数2:节点内容:要求类型是字节数组
(也就是说,不支持序列化方式,如果需要实现序列化,可使用java相关序列化框架,如Hessioan,Kryo框架)
参数3:节点权限:使用Ids.OPEN_ACL_UNSAFE开发权限即可。
(这个参数一般在权限没有太高要求的场景下,没必要关注)
参数4:节点类型:创建节点的类型,CreateMode.*,提供四种节点类型:
PERSISTENT:持久节点
PERSISTENT_SEQUENTIAL:持久顺序节点
EPHEMERAL:临时节点
EPHEMERAL_SEQUENTIAL:临时顺序节点
异步方式:(在同步参数基础上增加两个参数)
参数5:注册一个异步回调函数,要实现AsynCallBack .StringCallBack借口,重写processResult(int rc,String path,Object ctx,String name)方法,当节点创建完毕后执行此方法。
rc:为服务端响应码 0 表示调用成功,-4 表示端口连接、-110 表示指定节点存在,-112表示会话已经过期
path:接口调用时传入API的数据节点的路径参数
ctx:为调用接口传入API的ctx的值
name:实际在服务端创建节点的名称
参数6:传递给回调函数的参数,一般为上下文(Context)信息。
(3)删除节点:delete方法
(api提供了两个接口:同步删除和异步删除方式)
同步方式:
参数1:节点名称 /deletePath
参数2:版本号,即表明本次删除操作是针对该数据的某个版本进行的操作。
异步方式:(和create方法一致)
参数3:一个异步回调函数
参数4:用于传递上下文信息的对象。
注意:在zookeeper中,只允许删除子节点信息,也就是说如果当前节点不是子节点则无法删除,或必须先删除其下所有子节点。
(4)getChildren读取数据方法:包括子节点列表的获取和子节点数据的获取
参数1,path:获取指定节点下的数据(获取子节点列表)
参数2,watcher:注册的watcher,一旦在本次子节点获取后,子节点列表发生变更的话,那么就会向客户端发送通知。该参数允许为null。
参数3,wath:表明是否需要注册一个watcher;如果为true,则会使用到zookeeper客户端上文中提到的那个默认watcher。如果为false,则表明不需要注册watcher。
参数4,cb:回调函数
参数5,ctx:上下文信息对象。
参数6,stat:指定数据节点的节点状态信息
注意:当我们获取指定节点的子节点列表后,还需要订阅这个子节点列表的变化通知,这时候就可以通过注册一个watcher来实现,当子节点被添加火删除时,服务器端就会触发一个“NodeChildrenChanged”类型的事件通知,需要注意的是服务器端发送给客户端的事件通知中,是不包含最新的节点列表的,客户端必须主动从新进行获取,通常在客户端收到这个事件通知后,就可以再次主动获取最新的子节点列表了。也就是说,zookeeper服务端在向客户端发送watcher“NodeChildrenChanged”事件通知的时候,仅仅只发了一个通知,不会把节点变化情况发给客户端,需要客户端自己重新获取,另外Watcher通知是一次性的,即触发后失效,因此客户需要反复注册watcher才行。
(5)getData方法:获取指定节点的数据内容
参数1,path:路径
参数2,watcher:注册的watcher对象。一旦之后节点内容有变更,则会像客户端发送通知,该参数允许为null。
参数3,stat:指定节点的状态信息
参数4,watch:是否使用watcher,如果为true则使用默认上文中的watcher,false则不使用watcher。
参数5,cb:回调函数
参数6,ctx:用于传递的上下文信息对象。
注意:该方法和getChildren方法基本相同,主要是注册的watcher有所不同,客户端在获取一个阶段数据内容时,是可以进行watcher注册的,一旦节点发生变更,则服务器端会发送给客户端一个NodeDataChanged的事件通知
(6)setData方法:修改指定节点的数据内容
参数1,path:路径。
参数2,data:数据内容。
参数3,版本号(-1覆盖之前所有的版本)
参数4,cb:回调函数
参数5:ctx:用于传递的上下文对象。
(7)exists方法:检测节点是否存在
参数1,path:路径
参数2,watcher:注册的watcher对象。一旦节点内容有变更,则会像客户端发送通知,该参数允许为null。(用于三类事件监听:节点的创建,删除,更新)
参数3,watch:是否使用watcher,如果为true则使用默认上文中的watcher,false则不使用watcher。
参数4,cb:回调函数。
参数5,ctx:用于传递的下文信息对象。
注意:exists方法意义在于无论节点是否存在,都可以进行注册watcher,能够对节点的创建,删除,和修改进行监听,但是其子节点发生的各种变化,都不会通知客户端。
java操作zookeeper的示例代码:
1 import java.util.concurrent.CountDownLatch; 2 import org.apache.zookeeper.CreateMode; 3 import org.apache.zookeeper.WatchedEvent; 4 import org.apache.zookeeper.Watcher; 5 import org.apache.zookeeper.Watcher.Event.EventType; 6 import org.apache.zookeeper.ZooKeeper; 7 import org.apache.zookeeper.Watcher.Event.KeeperState; 8 import org.apache.zookeeper.ZooDefs.Ids; 9 10 /** 11 * Zookeeper base学习笔记 12 */ 13 public class ZookeeperBase { 14 15 /** zookeeper地址 */ 16 static final String CONNECT_ADDR = "127.0.0.1:2181"; 17 /** session超时时间 */ 18 static final int SESSION_OUTTIME = 2000;//ms 19 /** 信号量,阻塞程序执行,用于等待zookeeper连接成功,发送成功信号 */ 20 static final CountDownLatch connectedSemaphore = new CountDownLatch(1); 21 22 public static void main(String[] args) throws Exception{ 23 24 ZooKeeper zk = new ZooKeeper(CONNECT_ADDR, SESSION_OUTTIME, new Watcher(){ 25 @Override 26 public void process(WatchedEvent event) { 27 //获取事件的状态 28 KeeperState keeperState = event.getState(); 29 EventType eventType = event.getType(); 30 //如果是建立连接 31 if(KeeperState.SyncConnected == keeperState){ 32 if(EventType.None == eventType){ 33 //如果建立连接成功,则发送信号量,让后续阻塞程序向下执行 34 connectedSemaphore.countDown(); 35 System.out.println("zk 建立连接"); 36 } 37 } 38 } 39 }); 40 41 //进行阻塞 42 connectedSemaphore.await(); 43 44 System.out.println(".."); 45 //创建父节点 46 zk.create("/testRoot", "testRoot".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 47 48 //创建子节点 49 zk.create("/testRoot/children", "children data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 50 zk.create("/testRoot/children2", "children2 data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 51 52 //获取节点洗信息 53 byte[] data = zk.getData("/testRoot", false, null); 54 System.out.println(new String(data)); 55 System.out.println(zk.getChildren("/testRoot", false)); 56 57 //修改节点的值 58 zk.setData("/testRoot", "modify data root".getBytes(), -1); 59 byte[] data2 = zk.getData("/testRoot", false, null); 60 System.out.println(new String(data2)); 61 62 //判断节点是否存在 63 System.out.println(zk.exists("/testRoot/children", false)); 64 65 //删除节点 66 zk.delete("/testRoot/children2", -1); 67 System.out.println(zk.exists("/testRoot/children2", false)); 68 69 zk.close(); 70 71 72 73 } 74 75 }