【基础篇】详解Zookeeper客户端Curator

一、前言

Zookeeper被广泛应用于分布式环境下各种应用程序的协调,而Curator无疑是Zookeeper客户端中的瑞士军刀,解决了很多Zookeeper客户端非常底层的细节开发工作,包括连接重连、反复注册Watcher和NodeExistsException异常等等。

二、依赖

Curator包含了几个包:
curator-framework:对zookeeper的底层api的一些封装
curator-client:提供一些客户端的操作,例如重试策略等
curator-recipes:封装了一些高级特性,如:Cache事件监听、选举、分布式锁、分布式计数器、分布式Barrier等

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>2.12.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-client</artifactId>
    <version>2.12.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>2.12.0</version>
</dependency>

三、基本Api

3.1 会话创建

1.使用静态工厂方法创建会话

CuratorFramework zkClient = CuratorFrameworkFactory.newClient(zkAddr, 5000, 3000, new ExponentialBackoffRetry(1000, 5));

构造函数主要有4个参数:

参数名说明
connectionString服务器列表,格式host1:port1,host2:port2,…
retryPolicy重试策略
sessionTimeoutMs会话超时时间,单位毫秒
connectionTimeoutMs连接创建超时时间,单位毫秒
2.使用Fluent风格创建会话
CuratorFramework zkClient = CuratorFrameworkFactory.builder().connectString(zkAddr)
        .sessionTimeoutMs(5000)
        .connectionTimeoutMs(3000)
        .retryPolicy(new ExponentialBackoffRetry(1000, 5))
        .build();

3.创建包含隔离命名空间的会话
为了实现不同的Zookeeper业务之间的隔离,需要为每个业务分配一个独立的命名空间(NameSpace),即指定一个Zookeeper的根路径,例如(下面的例子)当客户端指定了独立命名空间为“/config”,那么该客户端对Zookeeper上的数据节点的操作都是基于该目录进行的。在多个应用共用一个Zookeeper集群的场景下,这对于实现不同应用之间的相互隔离十分有意义。

CuratorFramework zkClient = CuratorFrameworkFactory.builder().connectString(zkAddr)
        .sessionTimeoutMs(5000)
        .connectionTimeoutMs(3000)
        .retryPolicy(new ExponentialBackoffRetry(1000, 5))
        .namespace("config")
        .build();
3.2 启动客户端

当创建会话成功,得到实例然后可以直接调用其start( )方法启动客户端。

zkClient.start();
3.3 数据节点操作
3.3.1 创建数据节点

Zookeeper的节点模式总共有三种,持久节点,临时节点,顺序节点,通过组合我们可以实现以下4种节点类型:

PERSISTENT:持久节点
PERSISTENT_SEQUENTIAL:持久顺序节点
EPHEMERAL:临时节点
EPHEMERAL_SEQUENTIAL:临时顺序节点

// 创建一个节点,初始内容为空
// 如果没有设置节点属性,节点创建模式默认为持久化节点,内容默认为空
zkClient.create().forPath("test");

// 创建一个节点,附带初始化内容
zkClient.create().forPath("test", "contect".getBytes());

// 创建一个节点,指定创建模式(临时节点),内容为空
zkClient.create().withMode(CreateMode.EPHEMERAL).forPath("test");

// 创建一个节点,指定创建模式(临时节点),附带初始化内容
zkClient.create().withMode(CreateMode.EPHEMERAL).forPath("test", "contect".getBytes());

// 创建一个节点,指定创建模式(临时节点),附带初始化内容,并且自动递归创建父节点
zkClient.create().creatingParentContainersIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("test", "contect".getBytes());

自动递归创建父节点非常有用,一般情况创建一个子节点必须先判断它的父节点是否存在,如果不存在直接创建会抛出NoNodeException,使用creatingParentContainersIfNeeded()能够自动递归创建所有所需的父节点。

3.3.2 删除数据节点
// 删除一个节点
// 此方法只能删除叶子节点,否则会抛出异常。
zkClient.delete().forPath("test");

// 删除一个节点,并且递归删除其所有的子节点
zkClient.delete().deletingChildrenIfNeeded().forPath("test");

// 删除一个节点,强制指定版本进行删除
zkClient.delete().withVersion(1000).forPath("test");

// 删除一个节点,强制保证删除
// guaranteed()是一个保障措施,只要客户端会话有效,会在后台持续进行删除操作,直到成功。
zkClient.delete().guaranteed().forPath("test");

// 上面的多个流式接口是可以自由组合的,例如:
zkClient.delete().guaranteed().deletingChildrenIfNeeded().withVersion(1000).forPath("test");
3.3.3 读取数据节点
// 读取一个节点的数据内容
zkClient.getData().forPath("test");

// 读取一个节点的数据内容,同时获取到该节点的stat
Stat stat = new Stat();
zkClient.getData().storingStatIn(stat).forPath("test");

//获取某个节点的所有子节点路径
zkClient.getChildren().forPath("test");

通过传递Stat可以获取到读取的节点状态信息(例如版本号),zookeeper内部根据版本号区别当前的更新是不是最新,所以带版本号更新可避免并行操作带来的数据不一致问题。

3.3.3 更新数据节点
// 更新一个节点的数据内容
// 该接口会返回一个Stat实例
client.setData().forPath("test", "contect".getBytes());

// 更新一个节点的数据内容,强制指定版本进行更新
client.setData().withVersion(1000 ).forPath("test", "contect".getBytes());

// 检查节点是否存在
client.checkExists().forPath("test");
3.4 事务

CuratorFramework的实例包含inTransaction( )接口方法,调用此方法开启一个ZooKeeper事务。可以复合create, setData, check, and/or delete 等操作然后调用commit()作为一个原子操作提交。例如:

zkClient.inTransaction().check().forPath("test")
        .and().create().withMode(CreateMode.PERSISTENT).forPath("test", "contect".getBytes())
        .and().setData().forPath("test", "newContect".getBytes())
        .and().commit();
3.5 异步接口

上面提到的创建、删除、更新、读取等方法都是同步的,Curator提供异步接口,引入了BackgroundCallback接口用于处理异步接口调用之后服务端返回的结果信息。BackgroundCallback接口中一个重要的回调值为CuratorEvent,里面包含事件类型、响应吗和节点的详细信息。

ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 300, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(10000), new ThreadFactory() {
    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r, "zkSyncThreadPool");
    }
});
zkClient.create()
        .creatingParentsIfNeeded()
        .withMode(CreateMode.EPHEMERAL)
        .inBackground((curatorFramework, curatorEvent) -> {
            System.out.println(String.format("eventType:%s,resultCode:%s",curatorEvent.getType(),curatorEvent.getResultCode()));
        }, executor)
        .forPath("test");

如果inBackground()方法不指定executor,那么会默认使用Curator的EventThread去进行异步处理。

相关链接:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自平衡Azure

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值