初窥Zookeeper原生API

        其实对Zookeeper原生API封装的框架有两个,一个是zkClient,一个是Curator。但是我个人觉得呢,封装的框架是给我们提供方便以及更强大的功能,这当然是非常棒的了,但是,如果我们能先学习好原生API,到学框架时,就能理解得更深了,也知道它是怎么做到比原生更加方便更加强大的功能。

       至于原生API的详解我就不贴上来了,因为网上一大堆比我厉害的人,哈哈。下面我会将我知道的需要注意的点写一下,然后把我自己调用原生API的代码贴出来给大家瞄瞄。

1、创建的临时节点:本次会话有效,当本次会话连接断开了就被删除掉。

可以利用临时节点做个分布式锁。先get判断再create,而不是直接create是否抛异常来判断能否创建。Zookeeper的数据是存放在内存中的,所以get的性能非常的好。

2、不允许递归创建节点:

例如要创建/testRoot/test1,如果没有/testRoot节点,就这样创建,是会报错的

而Curator框架是可以递归创建节点的。

3、一般操作都提供两套API,一个是同步,一个是异步,要使用异步操作要注册一个异步回调函数,要实现AsynCallBack.xxx 接口。

4、delete操作,一个参数是节点的path,第二个是数据版本号dataVersion,写-1就是跳过版本检查。如果不是-1,会删除对应的版本号节点。

5、获取当前节点的子节点,返回的字符串列表中,字符串是子节点的相对路径,而不是绝对路径,如果想得到子节点的数据,记得是根据绝对路径获取。而且,如果想获取当前节点的子节点下的子节点,是不能直接获取到的。

6、创建不支持递归,删除也不支持递归。如果被删除的节点下有子节点,是会报错的。KeeperErrorCode = Directory not empty for xxx

Demo代码:代码中也会有比较详细的注解

package com.demo.zookeeper.base;

import java.util.List;
import java.util.concurrent.CountDownLatch;

import org.apache.zookeeper.AsyncCallback.StringCallback;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

public class ZookeeperBase {
	/**zookeeper地址*/
	private static final String CONNECT_ADDR="192.168.1.111:2181,192.168.1.112:2181,192.168.1.113:2181";
	/**session超时时间*/
	private static final int SESSION_OUTTIME = 2000;//单位为ms
	/**信号量,阻塞主线程执行,用于等待Zookeeper连接成功,发送成功信号*/
	private static final CountDownLatch semaphore = new CountDownLatch(1);
	
	public static void main(String[] args) throws Exception {
		ZooKeeper zk  = new ZooKeeper(CONNECT_ADDR, SESSION_OUTTIME, new Watcher(){

			@Override
			public void process(WatchedEvent event) {
				//获取事件的状态
				KeeperState keeperState =event.getState();
				//节点事件
				EventType eventType = event.getType();
				if(KeeperState.SyncConnected == keeperState){//连接成功
					if(EventType.None == eventType){ //第一次连接EventType的状态都为none
						//如果建立连接成功,则发送成功的信号量,让后续程序继续执行
						semaphore.countDown();
						System.out.println("zk建立连接");
					}
				}
			}
			
		});
		//进行阻塞
		semaphore.await();
		
		/**
		 * 创建父节点  
		 * data:字节数组,表示不支持序列化
		 * Ids.OPEN_ACL_UNSAFE 表示不设置访问控制
		 * CreateMode.EPHEMERAL 短暂,PERSISTENT 持久 ,如果后面带上SEQUENTIAL表示要顺序节点
		 */
		zk.create("/testRoot", "testRoot".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
		/**
		 * 创建子节点,试试异步方式
		 */
		//zk.create("/testRoot/test1", "test1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
		zk.create("/testRoot/test1", "test1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, new StringCallback(){
			
			/**
			 * rc:服务端响应码 0表示调用成功,-4表示端口连接,-110表示节点存在,-12表示会话已经过期
			 * path:接口调用时传入API的数据节点的路径参数
			 * ctx:为调用接口时传入API的ctx值
			 * name:实际在服务端创建节点的名称
			 */
			@Override
			public void processResult(int rc, String path, Object ctx, String name) {
				System.out.println("rc:"+rc);
				System.out.println("path:"+path);
				System.out.println("ctx:"+ctx);
				System.out.println("name:"+name);
			}
			
		}, "666");//666对应里面的ctx
		
		//获取节点信息
		byte[] data = zk.getData("/testRoot", false, null);
		System.out.println("父节点信息:"+new String(data));
		/**
		 * 获取子节点信息,注意:path填的是父节点的path,返回的应该是子节点的path(相对的)列表
		 */
		List<String> pathList = zk.getChildren("/testRoot", false);
		System.out.println(pathList);
		
		/**
		 * 修改节点的值  
		 * 第一个参数:节点的path
		 * 第二个参数:节点的新值
		 * 第三个参数:版本号  -1表示不理睬版本号
		 */
		zk.setData("/testRoot", "newRoot".getBytes(), -1);
		System.out.println(zk.getData("/testRoot", false, null));
		
		/**
		 * 判断节点是否存在
		 * 返回的是Stat:里面有节点的所有信息
		 * 顺便提一下watch:每个方法其实有两套,一个是boolean watch  一个是Watcher watcher
		 * 使用第一种:Zookeeper在创建的时候就绑定watcher,使用第二种是使用匿名类
		 */
		System.out.println("/testRoot是否存在:"+zk.exists("/testRoot", false));
		
		/**
		 * 删除节点
		 * 第一个参数:path
		 * 第二个参数:版本号 -1表示不理睬
		 * 注意:如果节点下面有子节点,删除是会报错的:KeeperErrorCode = Directory not empty for xxx
		 */
		//删除父节点时,先获取子节点,然后删除子节点,最后再删除父节点
		List<String> sonPaths = zk.getChildren("/testRoot", false);
		sonPaths.forEach((path)->{
			try {
				zk.delete("/testRoot/"+path, -1);
			} catch (Exception e) {
				e.printStackTrace();
			} 
		});
		zk.delete("/testRoot", -1);
		System.out.println("/testRoot是否存在:"+zk.exists("/testPath", false));
		
		//最后断开连接
		zk.close();
	}
}

猜你喜欢

转载自blog.csdn.net/Howinfun/article/details/82788064