Curator
什么是 Curator
Curator 是一套由netflix 公司开源的,Java 语言编程的 ZooKeeper 客户端框架,Curator项目是现在ZooKeeper 客户端中使用最多,对ZooKeeper 版本支持最好的第三方客户端,并推荐使用,Curator 把我们平时
常用的很多 ZooKeeper 服务开发功能做了封装,例如 Leader 选举、分布式计数器、分布式锁。这就减少了技术人员在使用 ZooKeeper 时的大部分底层细节开发工作。在会话重新连接、Watch 反复注册、多种异常处理等使
用场景中,用原生的 ZooKeeper 处理比较复杂。而在使用 Curator 时,由于其对这些功能都做了高度的封装,使用起来更加简单,不但减少了开发时间,而且增强了程序的可靠性
Curator 实战
这里我们以 Maven 工程为例,首先要引入Curator 框架相关的开发包,这里为了方便测试引入了junit ,lombok,由于Zookeeper本身以来了 log4j 日志框架,所以这里可以创建对应的log4j配置文件后直接使用。 如
下面的代码所示,我们通过将 Curator 相关的引用包配置到 Maven 工程的 pom 文件中,将 Curaotr 框架引用到工程项目里,在配置文件中分别引用了两个 Curator 相关的包,第一个是 curator-framework 包,该
包是对 ZooKeeper 底层 API 的一些封装。另一个是 curator-recipes 包,该包封装了一些 ZooKeeper 服务的高级特性,如:Cache 事件监听、选举、分布式锁、分布式 Barrier
pom依赖引入
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.0.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.8</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.0.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.8</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
会话创建
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); CuratorFramework client = CuratorFrameworkFactory.builder() .connectString("192.168.128.129:2181") .sessionTimeoutMs(5000) // 会话超时时间 .connectionTimeoutMs(5000) // 连接超时时间 .retryPolicy(retryPolicy) .namespace("base") // 包含隔离名称 .build(); client.start();
参数解析:
connectionString:服务器地址列表,在指定服务器地址列表的时候可以是一个地址,也可以是多个地址。如果是多个地址,那么每个服务器地址列表用逗号分隔, 如 host1:port1,host2:port2,host3;port3
etryPolicy:重试策略,当客户端异常退出或者与服务端失去连接的时候,可以通过设置客户端重新连接 ZooKeeper 服务端。而 Curator 提供了 一次重试、多次重试等不同种类的实现方式。在 Curator 内部,可以通过
判断服务器返回的 keeperException 的状态代码来判断是否进行重试处理,如果返回的是 OK 表示一切操作都没有问题,而 SYSTEMERROR 表示系统或服务端错误
|
策略名称
|
描述
|
|
ExponentialBackoffRetry
|
重试一组次数,重试之间的睡眠时间增加
|
|
RetryNTimes
|
重试最大次数
|
|
RetryOneTime
|
只重试一次
|
|
RetryUntilElapsed
|
在给定的时间结束之前重试
|
超时时间:Curator 客户端创建过程中,有两个超时时间的设置。一个是 sessionTimeoutMs 会话超时时间,用来设置该条会话在 ZooKeeper 服务端的失效时间。另一个是 connectionTimeoutMs 客户端创建会话的超
时时间,用来限制客户端发起一个会话连接到接收 ZooKeeper 服务端应答的时间。sessionTimeoutMs 作用在服务端,而 connectionTimeoutMs 作用在客户端
创建节点:
@Test public void testCreate() throws Exception { String path = curatorFramework.create().forPath("/curator-node"); // curatorFramework.create().withMode(CreateMode.PERSISTENT).forPath("/curator-node","some-data".getBytes()) log.info("curator create node :{} successfully.",path); } 在 Curator 中,可以使用 create 函数创建数据节点,并通过 withMode 函数指定节点类型(持久化节点,临时节点,顺序节点,临时顺序节点,持久化顺序节点等),默认是持久化节点,之后调用 forPath 函数来指定
节点的路径和数据信息
一次性创建带层级结构的节点
@Test public void testCreateWithParent() throws Exception { String pathWithParent="/node-parent/sub-node-1"; String path = curatorFramework.create().creatingParentsIfNeeded().forPath(pathWithParent); log.info("curator create node :{} successfully.",path); }
获取数据
@Test public void testGetData() throws Exception { byte[] bytes = curatorFramework.getData().forPath("/curator-node"); log.info("get data from node :{} successfully.",new String(bytes)); }
更新节点
@Test public void testSetData() throws Exception { curatorFramework.setData().forPath("/curator-node","changed!".getBytes()); byte[] bytes = curatorFramework.getData().forPath("/curator-node"); log.info("get data from node /curator-node :{} successfully.",new String(bytes)); }
删除节点
@Test public void testDelete() throws Exception { String pathWithParent="/node-parent"; curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath(pathWithParent); }
guaranteed:该函数的功能如字面意思一样,主要起到一个保障删除成功的作用,其底层工作方式是:只要该客户端的会话有效,就会在后台持续发起删除请求,直到该数据节点在 ZooKeeper 服务端被删除。
deletingChildrenIfNeeded:指定了该函数后,系统在删除该数据节点的时候会以递归的方式直接删除其子节点,以及子节点的子节点。
异步接口
@Test public void test() throws Exception { curatorFramework.getData().inBackground((item1, item2) -> { log.info(" background: {}", item2); }).forPath(ZK_NODE); TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); }
指定线程池
@Test public void test() throws Exception { ExecutorService executorService = Executors.newSingleThreadExecutor(); curatorFramework.getData().inBackground((item1, item2) -> { log.info(" background: {}", item2); },executorService).forPath(ZK_NODE); TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); }
Curator 监听器:
NodeCache 对某一个节点进行监听
@Slf4j public class NodeCacheTest extends AbstractCuratorTest{ public static final String NODE_CACHE="/node-cache"; @Test public void testNodeCacheTest() throws Exception { createIfNeed(NODE_CACHE); NodeCache nodeCache = new NodeCache(curatorFramework, NODE_CACHE); nodeCache.getListenable().addListener(new NodeCacheListener() { @Override public void nodeChanged() throws Exception { log.info("{} path nodeChanged: ",NODE_CACHE); printNodeData(); } }); nodeCache.start(); } public void printNodeData() throws Exception { byte[] bytes = curatorFramework.getData().forPath(NODE_CACHE); log.info("data: {}",new String(bytes)); } }
PathChildrenCache 会对子节点进行监听,但是不会对二级子节点进行监听
@Slf4j public class PathCacheTest extends AbstractCuratorTest{ public static final String PATH="/path-cache"; @Test public void testPathCache() throws Exception { createIfNeed(PATH); PathChildrenCache pathChildrenCache = new PathChildrenCache(curatorFramework, PATH, true); pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { log.info("event: {}",event); } }); // 如果设置为true则在首次启动时就会缓存节点内容到Cache中 pathChildrenCache.start(true); } }
TreeCache 使用一个内部类TreeNode来维护这个一个树结构。并将这个树结构与ZK节点进行了映射。所以TreeCache 可以监听当前节点下所有节点的事件
@Slf4j public class TreeCacheTest extends AbstractCuratorTest{ public static final String TREE_CACHE="/tree-path"; @Test public void testTreeCache() throws Exception { createIfNeed(TREE_CACHE); TreeCache treeCache = new TreeCache(curatorFramework, TREE_CACHE); treeCache.getListenable().addListener(new TreeCacheListener() { @Override public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception { log.info(" tree cache: {}",event); } }); treeCache.start(); } }

浙公网安备 33010602011771号