Curator 使用示例
一、Curator 简介
Apache Curator 是一个比较完善的 ZooKeeper 客户端框架,通过封装的一套高级 API 简化了 ZooKeeper 的操作。通过查看官方文档,可以发现 Curator 主要解决了三类问题:
- 封装 ZooKeeper client 与 ZooKeeper server 之间的连接处理
- 提供了一套 Fluent 风格的操作 API
- 提供 ZooKeeper 各种应用场景 recipes, 比如:分布式锁服务、集群领导选举、共享计数器、缓存机制、分布式队列等)的抽象封装
Curator 主要从以下几个方面降低了 zk 使用的复杂性:
- 重试机制: 提供可插拔的重试机制, 它将给捕获所有可恢复的异常配置一个重试策略,并且内部也提供了几种标准的重试策略(比如指数补偿)
- 连接状态监控: Curator 初始化之后会一直对 zk 连接进行监听,一旦发现连接状态发生变化将会作出相应的处理
- zk客户端实例管理: Curator 会对 zk 客户端到 server 集群的连接进行管理,并在需要的时候重建 zk 实例,保证与 zk 集群连接的可靠性
- 各种使用场景支持: Curator 实现了 zk 支持的大部分使用场景(甚至包括 zk 自身不支持的场景),这些实现都遵循了 zk 的最佳实践,并考虑了各种极端情况
二、工程中引入 curator 依赖
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.10.0</version>
</dependency>
三、示例(增删查改)
package com.topsail.curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
public class Demo1 {
public static void main(String[] args) throws Exception {
CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
.connectString("192.168.181.128:2181,192.168.181.128:2182,192.168.181.128:2183")
.sessionTimeoutMs(4000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.namespace("namespace-demo1")
.build();
curatorFramework.start();
System.out.println(ZooKeeper.States.CONNECTED);
System.out.println(curatorFramework.getState());
createPersistent(curatorFramework, "/demo1/persistent");
createPersistentSeq(curatorFramework, "/demo1/persistent_seq");
createEphemeral(curatorFramework, "/demo1/ephemeral");
createEphemeralSeq(curatorFramework, "/demo1/ephemeral_seq");
dataOps(curatorFramework, "/demo1/persistent");
createPersistent(curatorFramework, "/demo1/a/b/c");
deleteCascade(curatorFramework, "/demo1/a");
}
/**
* 创建持久化节点
*
* @param curatorFramework
* @throws Exception
*/
private static void createPersistent(CuratorFramework curatorFramework, String path) throws Exception {
Stat stat = curatorFramework.checkExists().forPath(path);
if (Objects.isNull(stat)) {
curatorFramework.create()
.creatingParentContainersIfNeeded() // 如果指定节点的父节点不存在,则会自动级联创建父节点
.withMode(CreateMode.PERSISTENT)
.forPath(path, "=== CONTENT ===".getBytes());
System.out.println(path + " create success!");
} else {
System.out.println(path + " is exists!");
}
}
/**
* 获取某个节点数据,设置某个节点数据
*
* @param curatorFramework
* @param path
* @throws Exception
*/
private static void dataOps(CuratorFramework curatorFramework, String path) throws Exception {
curatorFramework.setData().forPath(path, "=== CONTENT ===".getBytes());
byte[] bytes = curatorFramework.getData().forPath(path);
System.out.println("->->-> " + new String(bytes));
}
/**
* 创建持久化顺序节点
*
* @param curatorFramework
* @throws Exception
*/
private static void createPersistentSeq(CuratorFramework curatorFramework, String path) throws Exception {
for (int i = 0; i < 5; i++) {
curatorFramework.create()
.creatingParentContainersIfNeeded() // 如果指定节点的父节点不存在,则会自动级联创建父节点
.withMode(CreateMode.PERSISTENT_SEQUENTIAL)
.forPath(path + "/i-", "persistent seq data".getBytes());
}
List<String> children = curatorFramework.getChildren().forPath(path);
Collections.sort(children);
for (String child : children) {
System.out.println("-> " + child);
}
}
/**
* 创建临时节点
*
* @param curatorFramework
* @param path
* @throws Exception
*/
private static void createEphemeral(CuratorFramework curatorFramework, String path) throws Exception {
curatorFramework.create()
.creatingParentContainersIfNeeded() // 如果指定节点的父节点不存在,则会自动级联创建父节点
.withMode(CreateMode.EPHEMERAL)
.forPath(path, "ephemeral data".getBytes());
}
/**
* 创建临时顺序节点
*
* @param curatorFramework
* @param path
* @throws Exception
*/
private static void createEphemeralSeq(CuratorFramework curatorFramework, String path) throws Exception {
for (int i = 0; i < 5; i++) {
curatorFramework.create()
.creatingParentContainersIfNeeded() // 如果指定节点的父节点不存在,则会自动级联创建父节点
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
.forPath(path + "/i-", "ephemeral seq data".getBytes());
}
List<String> children = curatorFramework.getChildren().forPath(path);
Collections.sort(children);
for (String child : children) {
System.out.println("EphemeralSeq -> " + child);
}
}
/**
* 删除节点
*
* @param curatorFramework
* @param path
* @throws Exception
*/
private static void delete(CuratorFramework curatorFramework, String path) throws Exception {
curatorFramework.delete().forPath(path);
}
/**
* 级联删除子节点
*
* @param curatorFramework
* @param path
* @throws Exception
*/
private static void deleteCascade(CuratorFramework curatorFramework, String path) throws Exception {
curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath(path);
}
}
四、示例(监听)
package com.topsail.curator;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.ExponentialBackoffRetry;
public class Demo2 {
public static void main(String[] args) throws Exception {
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("192.168.181.128:2181,192.168.181.128:2182,192.168.181.128:2183")
.sessionTimeoutMs(4000)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.namespace("namespace-demo2")
.build();
client.start();
System.out.println(client.getState());
// PathChildCache 监听一个节点下子节点的创建、删除、修改事件
addListenerWithChildNodeCache(client, "/demo2/children");
// NodeCache 监听一个节点的修改和创建事件
addListenerWithNodeCache(client, "/demo2/single");
// TreeCache 监听所有节点的创建、删除、修改事件
addListenerWithTreeCache(client, "/demo2/tree");
System.in.read();
}
/**
* 监听某个节点的修改和创建事件
*
* @param curatorFramework
* @param path
*/
private static void addListenerWithNodeCache(CuratorFramework curatorFramework, String path) throws Exception {
curatorFramework.create().creatingParentContainersIfNeeded().forPath(path);
NodeCache nodeCache = new NodeCache(curatorFramework, path, false);
NodeCacheListener nodeCacheListener = () -> {
ChildData currentData = nodeCache.getCurrentData();
System.out.println("监听到节点事件: " + currentData.getPath() + " -> " + new String(currentData.getData()));
};
nodeCache.getListenable().addListener(nodeCacheListener);
nodeCache.start();
}
/**
* 监听某个节点下子节点的创建、删除、修改事件
*
* @param curatorFramework
* @param path
* @throws Exception
*/
private static void addListenerWithChildNodeCache(CuratorFramework curatorFramework, String path) throws Exception {
curatorFramework.create().creatingParentContainersIfNeeded().forPath(path);
PathChildrenCache pathChildrenCache = new PathChildrenCache(curatorFramework, path, true);
PathChildrenCacheListener pathChildrenCacheListener = (curatorFramework1, event) -> {
PathChildrenCacheEvent.Type type = event.getType();
switch (type) {
case CHILD_ADDED:
System.out.println("A child was added to the path:" + event.getType());
break;
case CHILD_UPDATED:
System.out.println("A child's data was changed:" + event.getType());
break;
case CHILD_REMOVED:
System.out.println("A child was removed from the path:" + event.getType());
break;
case CONNECTION_SUSPENDED:
System.out.println("CONNECTION_SUSPENDED:" + event.getType());
break;
case CONNECTION_RECONNECTED:
System.out.println("CONNECTION_RECONNECTED:" + event.getType());
break;
case CONNECTION_LOST:
System.out.println("CONNECTION_LOST:" + event.getType());
break;
case INITIALIZED:
System.out.println("INITIALIZED:" + event.getType());
break;
default:
break;
}
};
pathChildrenCache.getListenable().addListener(pathChildrenCacheListener);
pathChildrenCache.start();
}
/**
* 监听所有节点的创建、删除、修改事件
*
* @param curatorFramework
* @param path
* @throws Exception
*/
private static void addListenerWithTreeCache(CuratorFramework curatorFramework, String path) throws Exception {
curatorFramework.create().creatingParentContainersIfNeeded().forPath(path);
TreeCache treeCache = new TreeCache(curatorFramework, path);
TreeCacheListener treeCacheListener = (curatorFramework1, event) -> {
System.out.println("监听到节点事件:" + event.getType() + " - " + event.getData().getPath());
};
treeCache.getListenable().addListener(treeCacheListener);
treeCache.start();
}
}
五、示例(分布式锁)
package com.topsail.curator; import org.apache.commons.lang3.RandomUtils; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.locks.InterProcessMutex; import org.apache.curator.retry.ExponentialBackoffRetry; public class Demo3 { public static void main(String[] args) throws Exception { CuratorFramework curatorFramework = CuratorFrameworkFactory.builder() .connectString("192.168.181.128:2181,192.168.181.128:2182,192.168.181.128:2183") .sessionTimeoutMs(4000) .retryPolicy(new ExponentialBackoffRetry(1000, 3)) .namespace("namespace-demo3") .build(); curatorFramework.start(); System.out.println(curatorFramework.getState()); InterProcessMutex interProcessMutex = new InterProcessMutex(curatorFramework, "/locks"); for (int i = 0; i < 10; i++) { new Thread(() -> { System.out.println("线程 " + Thread.currentThread().getId() + " 尝试抢占锁..."); try { interProcessMutex.acquire(); System.out.println("线程 " + Thread.currentThread().getId() + " 获取锁成功!"); Thread.sleep(RandomUtils.nextInt(1000, 2000)); interProcessMutex.release(); System.out.println("线程 " + Thread.currentThread().getId() + " 释放锁成功!"); } catch (Exception e) { e.printStackTrace(); } }).start(); } } }
六、示例(Leader选举)
package com.topsail.curator; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.leader.LeaderLatch; import org.apache.curator.framework.recipes.leader.LeaderLatchListener; import org.apache.curator.retry.ExponentialBackoffRetry; public class Demo4 { public static void main(String[] args) throws Exception { CuratorFramework client = CuratorFrameworkFactory.builder() .connectString("192.168.181.128:2181,192.168.181.128:2182,192.168.181.128:2183") .sessionTimeoutMs(4000) .retryPolicy(new ExponentialBackoffRetry(1000, 3)) .namespace("namespace-demo4") .build(); client.start(); System.out.println(client.getState()); String lockPath = "/leader"; LeaderLatch leaderLatch = new LeaderLatch(client, lockPath); LeaderLatchListener listener = new LeaderLatchListener() { @Override public void isLeader() { System.out.println("i am master"); } @Override public void notLeader() { System.out.println("i am salver"); } }; leaderLatch.addListener(listener); leaderLatch.start(); leaderLatch.await(); } }
package com.topsail.curator; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.leader.LeaderLatch; import org.apache.curator.retry.ExponentialBackoffRetry; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.util.UUID; @Component public class Demo5 { private CuratorFramework client; private LeaderLatch leaderLatch; private String id = UUID.randomUUID().toString(); @PostConstruct public void init() throws Exception { client = CuratorFrameworkFactory.builder() .connectString("192.168.181.128:2181,192.168.181.128:2182,192.168.181.128:2183") .sessionTimeoutMs(4000) .retryPolicy(new ExponentialBackoffRetry(1000, 3)) .namespace("ns-demo5") .build(); client.start(); System.out.println("client 启动成功! " + client.getState()); leaderLatch = new LeaderLatch(client, "/leader", id); leaderLatch.start(); } @Scheduled(initialDelay = 2000, fixedDelay = 2000) public void execute() { System.err.println("id: " + id); if (leaderLatch.hasLeadership()) { System.err.println("Leadership..."); } } }