Zookeeper应用的开发主要通过java客户端API去连接和Zookeeper集群。
Zookeeper提供java客户端API:
- Zookeeper官方的java客户端API
- 第三方的java客户端API,比如Curator()
Zookeeper官方客户端API提供了基本操作:创建会话、创建节点、读取节点、更新数据、删除节点和检查节点是否存在等。
官方API的不足之处:(Zookeeper官方API功能比较简单,在实际开发过程中不推荐使用)
- Zookeeper的WatcherJ监测是一次性的,每次触发之后需要重新注册。会话超时之后没有实现重连机制。
- 异常处理繁琐,Zookeeper提供了很多异常,对于开发人员来说可能根本不知道如何处理这些抛出来的异常
- 仅提供简单的byte[]数组类型的接口,没有提供Java POJO级别的序列化数据处理接口
- 创建节点时如果抛出异常,需要自行检查节点是否存在
- 无法实现级联删除
Zookeeper原生客户端使用
引入zookeeper client依赖(与服务端保持一致,否则会有很多兼容性问题)
<!-- Zookeeper client -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.8.0</version>
</dependency>
Zookeeper原生客户端主要使用org.apache.zookeeper.ZooKeeper这个类来使用ZooKeeper服务。
ZooKeeper常用构造器
Zookeeper(connectString,sessionTimeout,watcher)
- connectString:使用逗号分隔的列表,每个Zookeeper节点是一个host.port对,host是机器名或者IP地址,port是ZooKeeper节点对客户端提供服务的端口号。客户端会任意选取connectString中的一个节点建立连接。
- sessionTimeout:session timeout 时间
- watcher:接收来自Zookeeper集群的事件。
使用zookeeper原生API,连接zookeeper集群
public class ZkClientDemo {
private static final String CONNECT_STR="localhost:2181";
private final static String CLUSTER_CONNECT_STR="192.168.65.156:2181,192.168.65.190:2181,192.168.65.200:2181";
public static void main(String[] args) throws Exception {
final CountDownLatch countDownLatch=new CountDownLatch(1);
ZooKeeper zooKeeper = new ZooKeeper(CLUSTER_CONNECT_STR,
4000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if(Event.KeeperState.SyncConnected==event.getState()
&& event.getType()== Event.EventType.None){
//如果收到了服务端的响应事件,连接成功
countDownLatch.countDown();
System.out.println("连接建立");
}
}
});
System.out.printf("连接中");
countDownLatch.await();
//CONNECTED
System.out.println(zooKeeper.getState());
//创建持久节点
zooKeeper.create("/user","fox".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
Zookeeper主要方法
- create(path,data,acl,createMode);创建一个给定路径的znode,并在znode保存data[]的数据,createMode指定znode的类型。
- delete(path,version):如果给定path上的znode的版本和给定的version匹配,删除znode。
- exists(path,watch):判断给定 path 上的 znode 是否存在,并在 znode 设置一个 watch。
- getData(path, watch):返回给定 path 上的 znode 数据,并在 znode 设置一个 watch。
- setData(path, data, version):如果给定 path 上的 znode 的版本和给定的 version 匹配,设置 znode 数据。
- getChildren(path, watch):返回给定 path 上的 znode 的孩子 znode 名字,并在 znode 设置一个 watch。
- sync(path):把客户端 session 连接节点和 leader 节点进行同步。
方法特点:
- 所有获取 znode 数据的 API 都可以设置一个 watch 用来监控 znode 的变化。
- 所有更新 znode 数据的 API 都有两个版本: 无条件更新版本和条件更新版本。如果 version 为 -1,更新为无条件更新。否则只有给定的 version 和 znode 当前的 version 一样,才会进行更新,这样的更新是条件更新。
- 所有的方法都有同步和异步两个版本。同步版本的方法发送请求给 ZooKeeper 并等待服务器的响 应。异步版本把请求放入客户端的请求队列,然后马上返回。异步版本通过 callback 来接受来 自服务端的响应。
同步创建节点:
@Test
public void createTest() throws KeeperException, InterruptedException {
String path = zooKeeper.create(ZK_NODE, "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
log.info("created path: {}",path);
}
异步创建节点:
@Test
public void createAsycTest() throws InterruptedException {
zooKeeper.create(ZK_NODE, "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT,
(rc, path, ctx, name) -> log.info("rc {},path {},ctx {},name {}",rc,path,ctx,name),"context");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
}
修改节点数据
@Test
public void setTest() throws KeeperException, InterruptedException {
Stat stat = new Stat();
byte[] data = zooKeeper.getData(ZK_NODE, false, stat);
log.info("修改前: {}",new String(data));
zooKeeper.setData(ZK_NODE, "changed!".getBytes(), stat.getVersion());
byte[] dataAfter = zooKeeper.getData(ZK_NODE, false, stat);
log.info("修改后: {}",new String(dataAfter));
}