zookeeper客户端使用

1. Java客户端api

1.1 创建会话
ZooKeeper(String connectString, int sessionTimeout, Watcher watcher);
ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly);
ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd);
ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd, boolean canBeReadOnly);
参数名说明
connectString指zookeeper服务器列表,由英文逗号分开的host:port字符串组成,每一个都代表一台zookeeper机器。另外,也可以在connectString中设置客户端连接上zookeeper后的根目录,方法是在host:port字符串最后加上这个根目录,这样就指定了该客户端连接上zookeeper服务器之后,所有对zookeeper的操作,都会基于这个根目录。这个目录也叫Chroot,即客户端隔离命名空间
sessinoTimeout指会话的超时时间,是一个以“毫秒”为单位的整型值。在zookeeper中有会话的概念,在一个会话周期内,zookeeper客户端和服务器之间会通过心跳检测机制来维持会话的有效性,一旦在sessionTimeout时间内没有进行有效的心跳检测,会话就会失效。
watcherzookeeper允许客户端在构造方法中传入一个接口watcher的实现类对象来作为默认的watcher事件通知处理器。
canBeReadOnly这是一个boolean类型参数,用于标识当前会话是否支持“read-only”模式。默认情况下,在zookeeper集群中,一个机器如果和集群中过半及以上机器时区了网络连接,那么这个机器将不再处理客户端请求(包括读请求)。但是在某些使用场景下,当zookeeper服务器发生此类故障的时候,我们还是希望zookeeper服务器能够提供读服务,这就是zookeeper的“read-only”模式。
sessionId和sessionPasswd分别代表会话ID和会话秘钥。这两个参数能够唯一确定一个会话,同时,客户端使用这两个参数可以实现客户端会话复用,从而达到恢复会话的效果。
  • zookeeper的会话创建是一个异步的过程,这里需要开发人员自行控制将其同步化。具体的方式是在其事件监听程序中监听WatchedEvent.SyncConnected和EventType.Node类型的事件;
1.2 创建节点
String create(final String path, byte data[], List<ACL> acl, CreateMode createMode);
void create(final String path, byte data[], List<ACL> acl, CreateMode createMode,  StringCallback cb, Object ctx)
参数名说明
path需要创建的数据节点的节点路径
data[]一个字节数组,是节点创建后的初始内容
acl节点的ACL策略
createMode节点类型,是一个枚举类型,通常有4中可选的节点类型:<br/>a. 持久(PERSISTENT);<br />b. 持久顺序(PERSISTENT_SEQUENTIAL);<br />c. 临时(EPHEMERAL);<br />d. 临时顺序(EPHEMERAL_SEQUENTIAL)
cb注册一个异步回调函数,开发人员需要实现StringCallback接口,主要是对下面这个方法进行重写:<br />public void processResult(int rc, String path, Object ctx, String name);<br />当服务器节点创建完毕后,zookeeper客户端就会自动调用这个方法,这样就可以处理相关的业务逻辑了
ctx用于传递一个对象,可以在回调方法执行的时候使用,通常是方一个上下文(Context)信息
  • 无论是同步接口还是异步接口,zookeeper都不支持递归创建,即无法在父节点不存在的情况下创建一个子节点;
  • 如果一个节点已经存在了,那么创建同名节点的时候,会抛出NodeExistsException异常;
  • 关于权限控制,如果你的应用场景没有太高的权限要求,那么可以不关注这个参数,只需要在acl参数中传入参数Ids.OPEN_ACL_UNSAFE,这就表明之后对这个节点的任何操作都不受权限控制;
参数名说明
rcResult Code,服务端响应码。客户端可以从这个响应码中识别出API调用的结果,常见的响应码如下:<br />a. 0(OK):接口调用成功<br />b. -4(ConnectionLoss):客户端与服务端连接已断开<br />c. -100(NodeExists):指定节点已存在<br />d. -112(SessionExpired):会话已过期
path接口调用时传入API的数据节点的节点路径参数值
ctx接口调用时传入API的ctx参数值
name实际在服务端创建的节点名(包括完整路径)
1.3 删除节点
void delete(final String path, int version);
void delete(final String path, int version, VoidCallback cb, Object ctx);
参数名说明
path指定数据节点的节点路径,即API调用的目的是
version指定节点的数据版本,即表明本次删除操作是针对该数据版本进行的
cb注册一个异步回调函数
ctx用于传递上下文信息的对象
  • 在zookeeper中,只允许删除叶子节点。也就是说,如果一个节点存在至少一个子节点的话,那么该节点将无法被直接删除,必须删除掉其所有子节点。
1.4 读取子节点
List<String> getChildren(final String path, Watcher watcher);
List<String> getChildren(String path, boolean watch);
void getChildren(final String path, Watcher watcher, ChildrenCallback cb, Object ctx);
void getChildren(String path, boolean watch, ChildrenCallback cb, Object ctx);
List<String> getChildren(final String path, Watcher watcher, Stat stat);
List<String> getChildren(String path, boolean watch, Stat stat);
void getChildren(final String path, Watcher watcher, Children2Callback cb, Object ctx);
void getChildren(String path, boolean watch, Children2Callback cb, Object ctx);
参数名说明
path指定数据节点的节点路径,即API调用的目的是获取该节点的
watcher注册的watcher。一旦在本次子节点获取之后,子节点列表发生变更的话,那么就会向客户端发送通知。该参数允许传入null
watch表明是否需要注册一个watcher。在创建节点时我们提到过一个默认watcher的概念,这里就是指定是否使用该默认watcher。
cb注册一个异步回调函数
ctx用于传递上下文信息的对象
stat指定数据节点的节点状态信息。用法是在接口中传入一个旧的stat变量,该stat变量会在方法执行过程中,被来自服务端响应的新stat对象替换
  • 如果zookeeper客户端在获取到指定节点的子节点列表后,还需要订阅这个子节点列表的变化通知,那么就可以通过注册一个watcher来实现。当有子节点被添加或是删除时,服务端就会向客户端发送一个NodeChildrenChanged(EventType.NodeChildrenChanged)类型的事件通知。需要注意的是,在服务端发送给客户端的事件通知中,是不包含最新的节点列表的,客户端必须主动重新进行获取。通常客户端在接收到这个事件通知后,就可以再次获取最新的子节点列表了;
  • stat对象中记录了一个节点的基本属性信息,例如节点创建时的事务ID、最后一次修改的事务ID和节点数据内容的长度等。有时候,我们不仅需要获取节点最新的子节点列表,还要获取这个节点最新的节点状态信息。对于这种情况,我们可以将一个旧的stat变量传入API接口,该stat变量会在方法执行过程中,被来自服务器响应的新stat对象替换;
  • 调用getChildren获取到的节点列表,都是数据节点的相对节点路径;
  • 关于watcher,zookeeper服务端在向客户端发送watcher“NodeChildrenChanged”时间通知的时候,仅仅只会发送出一个通知,而不会把节点的变化情况发送给客户端,需要客户端自己重新获取;
  • 由于watcher通知是一次性的,即一旦触发一次通知后,该watcher就失效了,因此客户端需要反复注册watcher;
1.5 读取数据
byte[] getData(final String path, Watcher watcher, Stat stat);
byte[] getData(String path, boolean watch, Stat stat);
void getData(String path, boolean watch, DataCallback cb, Object ctx);
void getData(String path, boolean watch, DataCallback cb, Object ctx);
参数名说明
path指定数据节点的节点路径,即API调用的目的是获取该节点的数据
watcher注册的watcher。一旦之后节点内容有变更,就会向客户端发送通知。该参数允许传入null
stat指定数据节点的节点状态信息。用法是在接口中传入一个旧的stat变量,该stat变量会在方法执行过程中,被来自服务器响应的新stat对象替换
watch表明是否需要注册一个watcher,这里的watcher指的是在创建该节点时指定的默认watcher
cb注册一个异步回调函数
ctx用于传递上下文信息的对象
  • 客户端在获取一个节点的数据内容的时候,是可以进行watcher注册的,这样一来,一旦该节点的状态发生变更,那么zookeeper服务端就会向客户端发送一个NodeDataChanged(EventType.NodeDataChanged)的事件通知;
  • 节点数据内容或者节点版本的变化都被看做是zookeeper节点的变化。
1.6 更新数据
Stat setData(final String path, byte data[], int version);
void setData(final String path, byte data[], int version, StatCallback cb, Object ctx);
参数名说明
path指定数据节点的节点路径,即API调用的目的是更新该节点
data[]一个字节数组,即需要使用该数据内容来覆盖节点现在的数据内容
version指定节点的数据版本,即表明本次更新操作是针对该数据版本进行的
cb注册一个异步回调函数
ctx用于传递上下文信息的对象
  • 在调用更新操作的时候,就可以添加version这个参数,该参数可以对应于CAS原理中的“预期值”,表明是针对该数据版本进行更新的;
  • 在zookeeper中,数据版本都是从0开始计数的,所以严格的讲,“-1”并不是一个合法的数据版本,它仅仅是一个标识符,如果客户端传入的版本参数是“-1”,就是告诉zookeeper服务器,客户端需要基于数据的最新版本进行更新操作。如果对zookeeper数据节点的更新操作没有原子性要求,那么就可以使用“-1”。
1.7 检测节点是否存在
Stat exists(final String path, Watcher watcher);
Stat exists(String path, boolean watch);
void exists(final String path, Watcher watcher, StatCallback cb, Object ctx);
void exists(String path, boolean watch, StatCallback cb, Object ctx);
参数名说明
path指定数据节点的节点路径,即API调用的目的是检测该
watcher注册的watcher,用于监听一下三类事件:<br />a. 节点被创建<br />b. 节点被删除<br />c. 节点被更新
watch指定是否复用zookeeper中默认的watcher
cb注册一个异步回调函数
ctx用于传递上下文信息的对象
  • 无论指定节点是否存在,通过调用exists()接口都可以注册watcher;
  • exists()接口中注册的watcher,能够对节点创建、节点删除和节点数据更新事件进行监听;
  • 对于指定节点的子节点的各种变化,都不会通知客户端。
1.8 权限控制
void addAuthInfo(String scheme, byte auth[]);
参数名说明
scheme权限控制模式,分为world、auth、digest、ip
auth具体的权限信息
  • 对于节点的权限,需要注意的是,当一个客户端为一个节点添加权限信息的时候,该权限信息是添加到了该节点的叶子节点上,操作这些节点需要权限信息,但如果操作该父节点,是不需要权限的。

2. 开源客户端-ZkClient

2.1 创建会话
public ZkClient(String serverstring);
public ZkClient(String zkServers, int connectionTimeout);
public ZkClient(String zkServers, int sessionTimeout, int connectionTimeout);
public ZkClient(String zkServers, int sessionTimeout, int connectionTimeout, ZkSerializer zkSerializer);
public ZkClient(IZkConnection connection);
public ZkClient(IZkConnection connection, int connectionTimeout);
public ZkClient(IZkConnection zkConnection, int connectionTimeout, ZkSerializer zkSerializer);
参数名说明
zkServers指zookeeper服务器列表,由英文状态逗号分开的host:port字符串组成,每一个都代表一台zookeeper机器
sessionTimeout会话超时时间,单位为毫秒,默认是30000ms
connectionTimeout连接创建超时时间,单位为毫秒。此参数表明如果在这个时间段内还是无法和zookeeper建立连接,那么就放弃连接,直接抛出异常
connectionIZkConnection接口的实现类
zkSerializer自定义序列化器
  • 这里ZkClient在创建客户端连接时,将创建的过程进行了同步化;
  • 对于序列化方式,ZkClient中定义了ZkSerializer来传入一个序列化实现,如Hessian和Kryo,默认使用Java自带的序列化方式进行对象的序列化;
2.2 创建节点
String create(final String path, Object data, final CreateMode mode);
void createEphemeral(final String path);
void createEphemeral(final String path, final Object data);
void createPersistent(String path);
void createPersistent(String path, boolean createParents);
void createPersistent(String path, Object data);
String createPersistentSequential(String path, Object data);
String createEphemeralSequential(final String path, final Object data);
参数名说明
path指定数据节点的节点路径,即API调用的目的是创建
data节点的初始数据内容,可以传如null
mode节点类型,是一个枚举类型,通常有4中可选的节点类型
acl节点的ACL策略
callback注册一个异步回调函数
context用于传递一个对象,可以在执行回调函数的时候使用
createParents指定是否创建父节点
  • 由于ZkClient支持了自定义序列化器,因此可以传入复杂对象作为参数;
  • 对于节点的类型,这里是通过提供不同的方法来进行指定的;
  • 对于zookeeper原生API,其无法在没有父节点的情况下创建一个子节点,这里ZkClient则可以通过createParents参数来控制是否递归地创建父节点。
2.3 删除节点
boolean delete(final String path);
boolean deleteRecursive(String path);
参数名说明
path数据节点的完整
  • 对于删除节点,如果该节点存在子节点,那么是无法删除该节点的,而必须先删除其所有的子节点。这里ZkClient提供了deleteRecursive()方法递归的删除一个节点及其子节点。
2.4 读取子节点数据
List<String> getChildren(String path);
List<String> getChildren(final String path, final boolean watch)
List<String> subscribeChildChanges(String path, IZkChildListener listener);
2.4.1 IZkChildListener参数说明
void handleChildChange(String parentPath, List<String> currentChilds);
参数名参数值
parentPath子节点变更通知对应的父节点的节点
currentChilds子节点的相对路径列表,如果没有子节点,那么会传入null
2.4.2 IZkChildListener事件说明
事件类型说明
新增子节点指定节点nodeA新增子节点。此时在handleChildChange()方法中,parentPath收到的是nodeA的全路径,currentChilds是最新的子节点列表
减少子节点指定节点nodeA减少子节点。此时在handleChildChange()方法中,parentPath收到的是nodeA的全路径,currentChilds是最新的子节点列表,可能是null
删除节点nodeA指定节点nodeA被删除。此时在handleChildChange()方法中,parentPath收到的是nodeA的全路径,currentChilds是null
  • ZkClient中没有像zookeeper客户端API一样提供子节点的时间监听程序,而是通过注册的方式提供了IZkChildListener的监听;
  • 客户端可以对一个不存在的节点进行子节点变更的监听;
  • 一旦客户端对一个节点注册了子节点列表变更监听之后,那么当该节点的子节点列表发生变更的时候,服务端都会通知客户端,将最新的子节点列表发送给客户端;
  • 该节点本身的创建和删除也会通知到客户端;
  • 与zookeeper原生提供的watcher不同的是,ZkClient的Listener不是一次性的,客户端只需要注册一次就会一直生效。
2.5 读取节点数据
<T extends Object> T readData(String path);
<T extends Object> T readData(String path, boolean returnNullIfPathNotExists);
<T extends Object> T readData(String path, Stat stat);
参数名说明
returnNullIfPathNotExists默认情况下,在调用该API的手,如果指定的节点不存在,那么会抛出异常:KeeperException.NoNodeException。如果设置了这个参数,那么如果节点不存在,就会直接返回null,而不会
stat指定数据节点的节点状态信息。用法是在接口中传入一个旧的stat变量,该stat变量会在方法执行过程中,被来自服务器响应的新stat对象替换
  • 通过调用该接口,就可以获取指定节点的数据内容,方法的返回值,在ZkClient内部已经被反序列化成指定对象;
2.5.1 注册节点数据变更监听器
void subscribeDataChanges(String path, IZkDataListener listener)
2.5.2 节点变更监听器
public interface IZkDataListener {
    public void handleDataChange(String dataPath, Object data) throws Exception;
    public void handleDataDeleted(String dataPath) throws Exception;
}
参数名说明
dataPath事件通知对应的节点
data最新的数据内容
2.5.3 节点变更事件
事件类别说明
节点数据变化指定节点nodeA的数据内容(content)或是数据版本(version)发生变更,都会出发这个事件。此时在handleDataChange()方法中,dataPath收到的是nodeA的全路径,data是最新
删除节点nodeA指定节点nodeA被删除。此时在handleDataChange()方法中,dataPath收到的是nodeA的全路径
2.6 更新数据
void writeData(String path, Object object);
void writeData(final String path, Object datat, final int expectedVersion);
参数名说明
path数据节点的完整
data数据内容,可以是null
expectedVersion预期的数据版本
2.7 检测节点是否存在
boolean exists(final String path);
boolean exists(final String path, final boolean watch);

3. 开源客户端-Curator

3.1 创建会话
static CuratorFramework newClient(String connectString, RetryPolicy retryPolicy);
static CuratorFramework newClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs, RetryPolicy retryPolicy);
参数名说明
connectString指zookeeper服务器列表,由英文逗号分开的host:port字符串组成,每一个都代表一台zookeeper机器
retryPolicy重试策略。默认主要有四种实现,分别是ExponentialBackoffRetry、RetryNTimes、RetryOneTime、RetryUntilElapsed
sesstionTimeoutMs会话超时时间,单位为毫秒。默认是60000ms
connectionTimeoutMs连接创建超时时间,单位为毫秒,默认是15000
  • 在重试策略上,Curator通过一个接口RetryPolicy来让用户实现自定义的重试策略
public interface RetryPolicy {
    public boolean allowRetry(int retryCount, long elapsedTimeMs, RetrySleeper sleeper);
}
参数名说明
retryCount已经重试的次数。如果是第一次重试,该参数
elapsedTimeMs从第一次重试开始已经花费的时间,单位为毫秒
sleeper用于sleep指定时间。Curator建议不要使用Thread.sleep()来进行sleep操作
  • 对于ExponentialBackoffRetry,随着重试次数的增加,每次重试睡眠的时间间隔会逐步增大,不过会在baseSleepTimeMs和maxSleepMs之间波动;

  • 使用Curator典型的创建会话的方式如下:

CuratorFramework CuratorFrameworkFactory.builder()
        .connectString("domain1.book.zookeeper:2181")
        .sessionTimeoutMs(5000)
        .retryPolicy(new ExponentialBackoffRetry(1000, 3))
        .namespace("base")
        .build();
3.2 创建节点
// 创建一个节点,初始内容为空
CuratorFramework.create().forPath(path);
// 创建一个节点,附带初始内容
CuratorFramework.create().forPath(path, "init".getBytes());
// 创建一个临时节点,初始内容为空
CuratorFramework.create().withMode(CreateMode.EPHEMERAL).forPath(path);
// 创建一个临时节点,并自动递归创建父节点
CuratorFramework.create().creatingParentssIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path);
  • 关于上面第四个API,这里可以指定是否递归的创建父节点,因为默认情况下,如果父节点不存在是不允许创建子节点的;
  • zookeeper规定,所有非叶子节点必须为持久节点,因而如果使用了creatingParentssIfNeeded(),并且指定节点类型为CreateMode.EPHEMERAL,那么只有path参数对应的叶节点为临时节点,其父节点均为持久节点。
3.3 删除节点
// 删除一个节点,该接口只能删除叶节点
CuratorFramework.delete().forPath(path);
// 删除一个节点,并递归地删除其子节点
CuratorFramework.delete().deletingChildrenIfNeeded().forPath(path);
// 删除一个节点,强制指定版本进行删除
CuratorFramework.delete().withVersion(version).forPath(path);
// 删除一个节点,强制保证删除
CuratorFramework.delete().guaranteed().forPath(path);
  • 对于guaranteed()方法,如果初次尝试删除没成功,其会在后台创建一个线程持续进行删除操作,直到节点删除成功;
3.4 读取数据
// 读取一个节点的数据内容,该方法返回值的类型是byte[]
CuratorFramework.getData().forPath(path);
// 读取一个节点的数据内容,同时获取该节点的stat
CuratorFramework.getData().storingStatIn(stat).forPath(path);
3.5 更新数据
// 更新一个节点的数据内容,调用该接口后,将返回一个stat对象
CuratorFramework.setData().forPath(path);
// 更新一个节点的数据内容,强制指定版本进行更新
CuratorFramework.setData().withVersion(version).forPath(path);
3.6 异步接口
public interface BackgroundCallback{
    public void processResult(CuratorFramework client, CuratorEvent event) 
        throws Exception;
}
  • 对于Curator前面的增删改查操作,如果没指定inBackground(BackgroundCallback),那么默认就是同步的,如果指定了则调用过程是异步的,并且在操作完成后会调用上述回调接口。该接口中主要有一个CuratorEvent用于指定回调的数据,主要包含事件类型和响应码。事件类型和响应码主要有以下几种:
public enum CuratorEventType
{
    /**
     * Corresponds to {@link CuratorFramework#create()}
     */
    CREATE,

    /**
     * Corresponds to {@link CuratorFramework#delete()}
     */
    DELETE,

    /**
     * Corresponds to {@link CuratorFramework#checkExists()}
     */
    EXISTS,

    /**
     * Corresponds to {@link CuratorFramework#getData()}
     */
    GET_DATA,

    /**
     * Corresponds to {@link CuratorFramework#setData()}
     */
    SET_DATA,

    /**
     * Corresponds to {@link CuratorFramework#getChildren()}
     */
    CHILDREN,

    /**
     * Corresponds to {@link CuratorFramework#sync(String, Object)}
     */
    SYNC,

    /**
     * Corresponds to {@link CuratorFramework#getACL()}
     */
    GET_ACL,

    /**
     * Corresponds to {@link CuratorFramework#setACL()}
     */
    SET_ACL,

    /**
     * Corresponds to {@link Watchable#usingWatcher(Watcher)} or {@link Watchable#watched()}
     */
    WATCHED,

    /**
     * Event sent when client is being closed
     */
    CLOSING
}
响应码说明
0OK,接口调用
-4ConnectioLoss,客户端与服务器断开连接
-110NodeExists,节点已存在
-112SessionExpired,会话已过期

转载于:https://my.oschina.net/zhangxufeng/blog/2995711

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值