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));
1.8 zookeeper实现分布式锁简单原理
分布式锁主要用于在分布式环境中保护跨进程、跨主机、跨网络的共享资源实现互斥访问,以达到保证数据的一致性
算法思路: 利用名称唯一性,加锁操作时,只需要所有客户端一起创建/test/Lock节点,只有一个创建成功,成功者获得锁。解锁时,只需删除/test/Lock节点,其余客户端再次进入竞争创建节点,直到所有客户端都获得锁。(创建临时节点的方式)
其他:数据存储到内存中,get请求非常快