Zookeeper的基本操作请参考之前博客:Zookeeper节点及客户端基本操作
本文只是简单的介绍一些常见的操作,更加详细的教程可以参考官网:http://curator.apache.org/getting-started.html
1.创建maven工程、引入curator依赖
curator使用sl4j作为日志框架,因此这里引入了logback
<dependencies>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>4.2.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>4.2.0version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
dependencies>
2. 基本增删改查
import java.util.List;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryNTimes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Curator 客户端基本操作
*
*/
public class CuratorClientDemo {
private static final Logger LOG = LoggerFactory.getLogger(CuratorClientDemo.class);
// Zookeeper 服务器地址
private static final String ZK_SERVER = "127.0.0.1:2181";
private CuratorFramework client;
public CuratorClientDemo() {
// 初始化客户端并启动
this.client = CuratorFrameworkFactory.newClient(ZK_SERVER, new RetryNTimes(5, 6000));
client.start();
LOG.info("============curator client started=====");
}
/**
* 创建新的节点
*
* @param path 节点路径
* @param data 节点数据
* @return 成功与否
*/
public boolean createNode(String path, String data) {
try {
this.client.create().creatingParentsIfNeeded().forPath(path, data.getBytes());
return true;
} catch (Exception e) {
e.printStackTrace();
LOG.warn("==============创建节点 {} 失败=======", path);
return false;
}
}
/**
* 获取节点数据
*
* @param path 节点路径
* @return 节点数据
*/
public String getData(String path) {
try {
byte[] data = this.client.getData().forPath(path);
return new String(data);
} catch (Exception e) {
e.printStackTrace();
LOG.warn("==============获取节点 {} 数据失败=======", path);
}
return null;
}
/**
* 获取子节点
*
* @param path 父节点
* @return 子节点路径集合
*/
public List<String> getChildren(String path) {
try {
return this.client.getChildren().forPath(path);
} catch (Exception e) {
e.printStackTrace();
LOG.warn("==============获取节点 {} 子节点失败=======", path);
}
return null;
}
/**
* 修改节点
*
* @param path 节点路径
* @param data 节点数据
* @return 成功与否
*/
public boolean modifyNode(String path, String data) {
try {
this.client.setData().forPath(path, data.getBytes());
return true;
} catch (Exception e) {
e.printStackTrace();
LOG.warn("==============修改节点 {} 失败=======", path);
return false;
}
}
/**
* 删除节点
*
* @param path 节点路径
* @return 成功与否
*/
public boolean deleteNode(String path) {
try {
this.client.delete().forPath(path);
return true;
} catch (Exception e) {
e.printStackTrace();
LOG.warn("==============修改节点 {} 失败=======", path);
return false;
}
}
/**
* 关闭连接
*/
public void closeConnection() {
this.client.close();
LOG.info("============curator client closed=====");
}
public static void main(String[] args) throws Exception {
CuratorClientDemo client = new CuratorClientDemo();
String path = "/qqxhb";
client.createNode(path, "test");
String data = client.getData(path);
System.out.println(data);
List<String> children = client.getChildren("/");
children.forEach(System.out::println);
client.modifyNode(path, "zookeeper");
client.deleteNode(path);
client.closeConnection();
}
}
3. 领导者选举功能
在分布式集群的环境中,经常需要一个领导者参与环境调度管理工作,curator客户端借助zookeeper可以很方便实现该功能。
import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.leader.LeaderSelector;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 领导者选举客户端
*/
public class LeaderClient extends LeaderSelectorListenerAdapter implements Closeable {
private static final Logger LOG = LoggerFactory.getLogger(LeaderSelectorListenerAdapter.class);
private final String name;
private final LeaderSelector leaderSelector;
private final AtomicInteger leaderCount = new AtomicInteger();
public LeaderClient(CuratorFramework client, String path, String name) {
this.name = name;
// 根据路径创建领导者选举实例,所有参与领导者选举的都必须使用同一个路径
leaderSelector = new LeaderSelector(client, path, this);
// 放弃领导者之后重新参与领导者选举
leaderSelector.autoRequeue();
}
public void start() throws IOException {
// 启动领导者选举,并在后台运行
leaderSelector.start();
}
@Override
public void close() throws IOException {
leaderSelector.close();
}
@Override
public void takeLeadership(CuratorFramework client) throws Exception {
// 同一时刻,只有一个Listener会进入takeLeadership()方法,说明它是当前的Leader。
// 注意:当Listener从takeLeadership()退出时就说明它放弃了“Leader身份”,下面是使用睡眠时间模拟持有领导者的时间
final int waitSeconds = (int) (5 * Math.random()) + 1;
LOG.info("当前领导者是:{},已经第 {} 次当选,占据领导者时间 {} 秒后放弃领导者身份。。。。。 ", this.name, leaderCount.getAndIncrement(),
waitSeconds);
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(waitSeconds));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
LOG.info("======= {} 放弃领导权。。。。。", this.name);
}
}
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.List;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.utils.CloseableUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
public class LeaderSelectorExample {
private static final Logger LOG = LoggerFactory.getLogger(LeaderSelectorListenerAdapter.class);
// 参与者数量
private static final int PARTITIONS = 5;
// 领导者选举节点路径
private static final String PATH = "/qqxhb/leader";
// Zookeeper 服务器地址
private static final String ZK_SERVER = "127.0.0.1:2181";
public static void main(String[] args) throws Exception {
List<CuratorFramework> clients = Lists.newArrayList();
List<LeaderClient> leaderClients = Lists.newArrayList();
try {
for (int i = 0; i < PARTITIONS; ++i) {
CuratorFramework client = CuratorFrameworkFactory.newClient(ZK_SERVER,
new ExponentialBackoffRetry(2000, 5));
clients.add(client);
LeaderClient leaderClient = new LeaderClient(client, PATH, "Client #" + i);
leaderClients.add(leaderClient);
client.start();
leaderClient.start();
}
LOG.info("====按回车键结束领导者选举过程。。。。。。。。。");
new BufferedReader(new InputStreamReader(System.in)).readLine();
} finally {
LOG.info("====领导者选举过程结束。。。。。。。。。");
for (LeaderClient exampleClient : leaderClients) {
CloseableUtils.closeQuietly(exampleClient);
}
for (CuratorFramework client : clients) {
CloseableUtils.closeQuietly(client);
}
}
}
}
4. 分布式锁功能
分布式集群环境经常也会遇到共享资源的问题,比如定时任务同一时间只希望其中一台服务器执行,则需要使用到分布式锁,同样在curator中有提供相应的简单实现。
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
/**
* 模拟任务锁
*
*/
public class TaskLocks {
private static final Logger LOG = LoggerFactory.getLogger(TaskLocks.class);
private final InterProcessMutex lock;
private final String taskName;
private final String clientName;
public TaskLocks(CuratorFramework client, String lockPath, String taskName, String clientName) {
this.taskName = taskName;
this.clientName = clientName;
lock = new InterProcessMutex(client, lockPath);
}
public void doWork(long time, TimeUnit unit) throws Exception {
if (!lock.acquire(time, unit)) {
throw new IllegalStateException(this.clientName + " could not acquire the lock");
}
try {
LOG.info("======= {} 获取到锁,开始执行任务{}。。。。", this.clientName, this.taskName);
Thread.sleep((long) (3 * Math.random()));
} finally {
LOG.info("======= {}释放任务锁{}。。。。", this.clientName, this.taskName);
lock.release();
}
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.utils.CloseableUtils;
public class LockingExample {
private static final int CLIENTS = 5;
// 锁节点路径
private static final String PATH = "/qqxhb/locks";
// Zookeeper 服务器地址
private static final String ZK_SERVER = "127.0.0.1:2181";
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newFixedThreadPool(CLIENTS);
try {
String task = "task_test";
for (int i = 0; i < CLIENTS; ++i) {
int cli = i;
service.execute(() -> {
CuratorFramework client = CuratorFrameworkFactory.newClient(ZK_SERVER,
new ExponentialBackoffRetry(2000, 5));
try {
client.start();
TaskLocks example = new TaskLocks(client, PATH, task, "Client #" + cli);
example.doWork(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) {
e.printStackTrace();
} finally {
CloseableUtils.closeQuietly(client);
}
});
}
service.shutdown();
service.awaitTermination(10, TimeUnit.MINUTES);
} finally {
service.shutdown();
}
}
}
本文涉及的源码地址(curator-demo模块):https://github.com/qqxhb/zookeeper