ZooKeeper API使用 I 创建节点

客户端可以通过 ZooKeeper 的 API 来创建一个数据节点,有如下两个接口:

String create(final String path, byte data[], List<ACL> acl, CreateMode createMode)
void create(final String path, byte date[], List<ACL> acl, CreateMode createMode, StringCallback cb, Object ctx)

这两个接口分别以同步异步方式创建节点,API 方法的参数说明如表:

参数名 说明
path 需要创建的数据节点的节点路径。比如,/zk-book/foo。
data[] 一个字节数组,是节点创建后的初始内容。
acl 节点的 ACL 策略。
createMode 节点类型,是一个枚举类型,通常有4种可选的节点类型:持久(PERSISTENT)持久顺序(PERSISTENT_SEQUENTIAL)临时(EPHEMERAL)临时顺序(EPHEMERAL_SEQUENTIAL)。关于ZNode的节点特性,将在后面做详解介绍。
cb 注册一个异步回调函数。开发人员需要实现StringCallBack接口,主要是对下面这个方法的重写:void processResult(int rc, String path, Object ctx, String name);当服务端节点创建完毕后,ZooKeeper 客户端就会自动调用这个方法,这样就可以处理相关的业务逻辑了。
ctx 用于传递一个对象,可以在毁掉方法执行的时候使用,通常是一个上下文(Context)信息。

需要注意几点,无论是同步还是异步接口,ZooKeeper 都不支持递归创建,即无法再父节点不存在的情况下创建一个子节点。另外,如果一个节点已经存在了,那么创建同名节点的时候,会抛出NodeExistException异常

目前,ZooKeeper 的节点内容只支持字节数组(byte[])类型。也就是说,ZooKeeper 不负责为节点内容进行序列化,开发人员需要自己使用序列化工具将节点内容进行序列化和反序列化。对于字符串,可以简单地使用“String”.getBytes()来生成一个字节数组;对于其他复杂对象,可以使用 Hessian 或是 Kryo 等专门的序列化工具来进行序列化。

关于权限控制,如果你的应用场景没有太高的权限要求,那么可以不关注这个参数,只需要在 acl 参数中传入参数Ids.OPEN_ACL_UNSAFE,这就表明之后对这个节点的任何操作都不受权限控制。关于 ZooKeeper 的权限控制,将在后面做纤细介绍。

使用同步API创建一个节点

package com.lpf.zookeeper.ZooKeeper.create;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;

import java.util.concurrent.CountDownLatch;

// ZooKeeper API创建节点,使用同步(sync)接口
public class ZooKeeper_Create_API_Sync_Usage implements Watcher {
    private static CountDownLatch connectedSemaphore = new CountDownLatch(1);

    public static void main(String[] args) {
        try {
            ZooKeeper zookeeper = new ZooKeeper("localhost:2181", 5000, new ZooKeeper_Create_API_Sync_Usage());
            connectedSemaphore.await();

            String path1 = zookeeper.create("/zk-test-ephemeral-", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
            System.out.println("Success create znode:" + path1);

            String path2 = zookeeper.create("/zk-test-ephemeral-", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            System.out.println("Success create znode:" + path2);

            Thread.sleep(Integer.MAX_VALUE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void process(WatchedEvent event) {
        if (KeeperState.SyncConnected == event.getState()) {
            connectedSemaphore.countDown();
        }
    }
}

运行程序,输出结果如下:

Success create znode:/zk-test-ephemeral-
Success create znode:/zk-test-ephemeral-0000000003

在上面这个程序片段中,使用了同步的节点创建接口:String create(final String path,byte data[],List<ACL> acl,CreateMode createMode)。在接口使用中,我们分别创建了两种类型的节点:临时节点和临时顺序节点。从返回的结果可以看出,如果创建了临时节点,那么API的返回值就是当时传入的path参数;如果创建了临时顺序节点,那么ZooKeeper 会自动在节点后加上一个数字,并且在 API 接口的返回值中返回该数据节点的一个完整的节点路径。

使用异步API创建一个节点

package com.lpf.zookeeper.ZooKeeper.create;

import org.apache.zookeeper.*;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;

import java.util.concurrent.CountDownLatch;

// ZooKeeper API创建节点,使用异步(async)接口
public class ZooKeeper_Create_API_ASync_Usage implements Watcher {
    private static CountDownLatch connectedSemaphore = new CountDownLatch(1);

    public static void main(String[] args) {
        try {
            ZooKeeper zookeeper = new ZooKeeper("localhost:2181", 5000, new ZooKeeper_Create_API_ASync_Usage());
            connectedSemaphore.await();

            zookeeper.create("/zk-test-ephemeral-", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL,
                    new IStringCallback(), "I am contest.");

            zookeeper.create("/zk-test-ephemeral-", "".getBytes(), Ids.OPEN_ACL_UNSAFE,
                    CreateMode.EPHEMERAL, new IStringCallback(), "I am context.");

            zookeeper.create("/zk-test-ephemeral-", "".getBytes(), Ids.OPEN_ACL_UNSAFE,
                    CreateMode.EPHEMERAL_SEQUENTIAL, new IStringCallback(), "I am context.");

            Thread.sleep(Integer.MAX_VALUE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void process(WatchedEvent event) {
        if (KeeperState.SyncConnected == event.getState()) {
            connectedSemaphore.countDown();
        }
    }
}

class IStringCallback implements AsyncCallback.StringCallback {

    public void processResult(int rc, String path, Object ctx, String name) {
        System.out.println("Create path result:【" + rc + "," + path + "," + ","
                + ctx + ", real path name:" + name + "】");
    }
}

运行程序,输出结果如下:

Create path result:【0,/zk-test-ephemeral-,,I am contest., real path name:/zk-test-ephemeral-】
Create path result:【-110,/zk-test-ephemeral-,,I am context., real path name:null】
Create path result:【0,/zk-test-ephemeral-,,I am context., real path name:/zk-test-ephemeral-0000000007

从这个程序片段中可以看出,使用异步方式创建接口也很简单。用户仅仅需要实现 AsyncCallback.StringCallback() 接口即可。AsyncCallback 包含了 StatCallbackDataCalbackACLCallbackChildrenCallbackChildren2CallbackStringCallbackVoidCallback 七种不同的回调接口,用户可以在不同的异步接口中实现不同的接口。

和同步接口最大的区别在于,节点的创建过程(包括网络通信和服务端的节点创建过程)是异步的。并且,在同步接口调用过程中,我们需要关注接口抛出异常的可能;但是在异步接口中,接口本身是不会抛出异常的、所有的异常都会在回调函数中通过Result Code(响应吗)来实现。

下面来重点看下回调方法:void processResult(int rc, String path, Object ctx, String name)。这个方法的几个参数主要如下表。

参数名 说明
rc Result Code,服务端响应码。客户端可以从这个响应码中识别出API调用的结果,常见的响应码如下:0(OK):接口调用成功-4(ConnectionLoss):客户端和服务端连接已断开-110(NodeExists):指定节点已存在-112(SessionExpired):会话已过期
path 接口调用时传入API的数据节点路径参数值
ctx 接口调用时传入API的ctx参数值
name 实际在服务端创建的节点名。在上述代码中,第三次创建节点时,由于创建的节点类型是顺序节点,因此在服务端没有真正创建好顺序节点之前,客户端无法知道节点的完整节点路径。于是,在回调方法中,服务端会返回这个数据节点的完整节点路径。

猜你喜欢

转载自blog.csdn.net/liupeifeng3514/article/details/82108958
今日推荐