Zookeeper 原生API操作

1.1 接口介绍描述

zookeeper的javaclient可以使我们更轻松的实现对zookeeper的各种操作,要使用java操作zookeeper,需要引入zookeeper-3.4.5.jar和zkclient-0.1.jar。zookeeper-3.4.5.jar是官方提供的JAVA API,zkclient-0.1.jar则是在原生API基础上进行扩展的开源Java客户端。


1.2 客户端描述

客户端可以通过创建一个zookeeper实例来连接zookeeper服务器,Zookeeper(Arguments)方法(一共有4个构造方法,根据参数不同),参数说明如下:

  • connectString:连接服务器列表,用“,”分割。
  • sessionTimeout:心跳检测时间周期,单位毫秒。
  • watcher:事件处理通知器。
  • canBeReadOnly:标识当前会话是否支持只读。
  • sessionId和sessionPassword:提供连接zookeeper服务器的sessionId和密码,通过这两个参数确定唯一一台客户端,目的是可以提供重复会话。

注意:需要注意的是ZK的连接是异步的,因此我们需要CountDownLatch来帮助我们确保ZK初始化完成。。


1.3 连接zookeeper代码demo

import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
public class ZookeeperBase {

	/** zookeeper地址 */
	static final String CONNECT_ADDR = "192.168.91.101:2181,192.168.91.102:2181,192.168.91.103:2181";
	/** session超时时间 */
	static final int SESSION_OUTTIME = 2000;//ms 
	/** 信号量,阻塞程序执行,用于等待zookeeper连接成功,发送成功信号 */
	static final CountDownLatch connectedSemaphore = 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){
						//如果建立连接成功,则发送信号量,让后续阻塞程序向下执行
						connectedSemaphore.countDown();
						System.out.println("zk 建立连接");
					}l
				}
			}
		});
		//进行阻塞
		connectedSemaphore.await();
		System.out.println("----------------");
		zk.close();	
	}	
}

因为zookeeper 连接异步,引入CountDownLatch 该工具类。

利用zookeeper.jar这样的就是基于原生的API方式操作ZK,因为这个原生API使用起来并不是让人很舒服,于是出现了zkclient这种方式,以至到后来基于Curator框架,让人使用ZK更加方便。有一句话,Guava is to JAVA what Curator is to Zookeeper。


1.4 创建节点

同步创建
create(java.lang.String path
		, byte[] data
		, java.util.List<org.apache.zookeeper.data.ACL> acl
		, org.apache.zookeeper.CreateMode createMode)
  • path:创建节点路径,需保证父节点已存在
  • bdata:节点数据(字符数组,不支持序列化方式)
  • acl:权限列表
  • 提供默认的权限
  • OPEN_ACL_UNSAFE:完全开放
  • CREATOR_ALL_ACL:创建该znode的连接拥有所有权限
  • READ_ACL_UNSAFE:所有的客户端都可读
  • 自定义权限
ACL aclIp = new ACL(ZooDefs.Perms.READ,new Id("ip","127.0.0.1"));
ACL aclDigest = new ACL(ZooDefs.Perms.READ| ZooDefs.Perms.WRITE,
                                        new Id("digest", DigestAuthenticationProvider.generateDigest("id:pass")));
  • session设置权限 
    zk.addAuthInfo(“digest”, “id:pass”.getBytes());
  • createMode:节点类型
  • PERSISTENT:持久化节点
  • PERSISTENT_SEQUENTIAL:持久化有序节点
  • EPHEMERAL:临时节点(连接断开自动删除。一次会话)
  • EPHEMERAL_SEQUENTIAL:临时有序节点(连接断开自动删除)
代码示例
zk.create("/testRoot"
		  , "testRoot".getBytes()
		  , Ids.OPEN_ACL_UNSAFE
		  , CreateMode.PERSISTENT);

//创建子节点
zk.create("/testRoot/children", "children data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

注意:

  • 不允许递归创建节点(原生API中如果父节点不存在,则子节点不能创建)
  • 节点内容,要求是字符数组(不支持序列化,如果要使用序列化,使用第三方框架,例如kryo等)

异步创建
create(java.lang.String path
       , byte[] data
       , java.util.List<org.apache.zookeeper.data.ACL> acl
       , org.apache.zookeeper.CreateMode createMode
       , org.apache.zookeeper.AsyncCallback.StringCallback cb     	   	      ,    
       , java.lang.Object ctx)
  • StringCallback cb:回调接口,执行创建操作后,结果以及数据发送到此接口的实现类中
  • Object ctx:自定义回调数据,在回调实现类可以获取此数据

1.5 删除节点

两种方式,一种同步,一种异步

void    delete(String path, int version):同步的删目录方法
void    delete(String path, int version, AsyncCallback.VoidCallback cb, Object ctx):异步的删目录方法
案例代码
zk.delete("/testRoot", -1, new AsyncCallback.VoidCallback() {
	@Override
	public void processResult(int rc, String path, Object ctx) {
		System.out.println(rc);
		System.out.println(path);
		System.out.println(ctx);
	}
}, "a");

version = -1 全清空。跳过检查


1.6 获取的节点信息

//获取节点信息
byte[] data = zk.getData("/testRoot", false, null);
System.out.println(new String(data));

获取子节点

/**
  * 参数:
  * path:父节点路径
  * watch:true或者false,注册一个watch事件
  */
 List<String> strChildList = zooKeeper.getChildren("/testNode", false);
 for (String s : strChildList) {
     System.out.println(s);
 }
 //获取节点信息时,把父节点路径添加上,使用getData()方法
 // getChildren() 只能获取直接子节点,不能获取孙子节点

1.7 修改节点信息

zk.setData("/testRoot", "modify data root".getBytes(), -1);
byte[] data = zk.getData("/testRoot", false, null);
System.out.println(new String(data));
//判断节点是否存在
//System.out.println(zk.exists("/testRoot/children", false));

zookeeper 支持ipv4 ipv6_zookeeper

1.8 zookeeper实现分布式锁简单原理

分布式锁主要用于在分布式环境中保护跨进程、跨主机、跨网络的共享资源实现互斥访问,以达到保证数据的一致性

算法思路: 利用名称唯一性,加锁操作时,只需要所有客户端一起创建/test/Lock节点,只有一个创建成功,成功者获得锁。解锁时,只需删除/test/Lock节点,其余客户端再次进入竞争创建节点,直到所有客户端都获得锁。(创建临时节点的方式)

其他:数据存储到内存中,get请求非常快