Curator是zookeeper分布式协调服务的java客户端库,它包装了一系列操作zk的高级API和实用库,是的操作zk变得更加容易和可靠。例如使用原生zk的API实现分布式锁的话,代码量多,复杂,使用Curator后就相对简单的多,很多底层的api都直接封装好了,开箱即用,学习成本低。
1、使用Curator之前,你需要引入maven依赖
org.apache.curator
curator-framework
2.8.0
org.apache.curator
curator-recipes
2.8.0
2、实例化Curator,你可以通过CuratorFrameworkFactory类提供的来产生一个CuratorFramework对象
CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy)
zookeeperConnectionString就是连接的ip:端口信息,retryPolicy是重试策略,Curator提供了三种常用的重试策略,这里不详述
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3)
CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy);
client.start();
或者你也可以使用链式调用来实例化curator
CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
.connectString(zookeeperConnectionString)
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(retryPolicy)
.build();
curatorFramework.start();
curator操作zk的api主要包括 节点的增删改查、节点判断、节点监听等,下面的代码演示了如何使用基本的curator api
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.RetryUntilElapsed;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 类描述:zookeeper客户端curator使用demo
*/
public class CuratorDemo {
private static final Logger logger = LoggerFactory.getLogger(CuratorDemo.class);
private static final String NODE_PATH = "/node_8";
private static final String CONNECT_TOSTRING = "10.200.121.46:2181";
/*创建线程池,供给异步使用curator时调用*/
public static ExecutorService executorService = Executors.newCachedThreadPool();
public static void main(String[] args) throws Exception {
try {
/*重试策略一:重试三次,每重试一次,重试的间隔时间会越来越大
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
*/
/*重试策略二:最多重试三次,每次重试间隔1s
RetryPolicy retryPolicy1 = new RetryNTimes(3,1000);
*/
/*重试策略三:最大重试时间总和不超过5s,每次重试间隔为1s*/
RetryPolicy retryPolicy2 = new RetryUntilElapsed(5000, 1000);
/*
/* 方式一建立zookeeper连接
CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(CONNECT_TOSTRING,5000,5000,retryPolicy2);
*/
/*方式二建立zookeeper连接*/
CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
.connectString(CONNECT_TOSTRING)
.sessionTimeoutMs(5000)
.connectionTimeoutMs(5000)
.retryPolicy(retryPolicy2)
.build();
curatorFramework.start();
/*创建节点数据*/
curatorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(NODE_PATH, "456".getBytes());
/*删除节点(包含子节点)*/ curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().withVersion(-1).forPath(NODE_PATH);
/*获取子节点*/
List strings = curatorFramework.getChildren().forPath(NODE_PATH);
/*获取节点数据内容*/
byte[] bytes = curatorFramework.getData().forPath(NODE_PATH);
System.out.println(new String(bytes));
/*获取节点数据内容+状态信息*/
Stat stat = new Stat();
byte[] result = curatorFramework.getData().storingStatIn(stat).forPath(NODE_PATH);
System.out.println(new String(result));
/*修改节点数据内容*/
curatorFramework.setData().forPath(NODE_PATH, "123".getBytes());
/*判断节点是否存在*/
Stat stat1 = curatorFramework.checkExists().forPath(NODE_PATH);
/*异步操作,以判断节点是否存在为例,注意使用线程池以便节省单个线程的创建销毁开销,及最后线程的关闭*/
curatorFramework.checkExists().inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
Object context = curatorEvent.getContext(); //这里的上下文就是 传递进去的"123456"
}
}, "12345", executorService).forPath(NODE_PATH);
/*设置节点事件监听*/
final NodeCache nodeCache = new NodeCache(curatorFramework, NODE_PATH);
nodeCache.start();
nodeCache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
byte[] result = nodeCache.getCurrentData().getData();
logger.info("事件监听result=" + new String(result));
}
});
/*设置子节点事件监听*/
final PathChildrenCache childrenCache = new PathChildrenCache(curatorFramework, NODE_PATH, true);
childrenCache.start();
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
PathChildrenCacheEvent.Type type = pathChildrenCacheEvent.getType();
switch (type) {
case CHILD_ADDED:
logger.info("");
case CHILD_UPDATED:
logger.info("");
case CHILD_REMOVED:
logger.info("");
default:
break;
}
}
});
} catch (Exception e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
}
Curator中包装的master选举包含两种,Leader Latch和Leader Election,原理采用的是zk的节点特性,即多个客户端同时创建同一节点,zk保证只有一个客户端能创建成功,成功的客户端即为master节点,再master节点的机器上执行业务。Leader Latcher里面即包装了zk创建节点、设置监听,对应的操作均包装在LeaderLatch类中。LeaderLatch将随机选出一个master
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.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 类描述:基于Curator的Leader Latch实现的master选举
* 创建人:simonsfan
*/
@Component
public class CuratorLeaderLatch {
private static CuratorFramework curatorFramework;
private static LeaderLatch leaderLatch;
private static final String path = "/root/leaderlatch";
private static final String connectStr = "10.200.121.46:2181,10.200.121.159:2181,10.200.121.168:2181";
static {
curatorFramework = CuratorFrameworkFactory
.builder()
.connectString(connectStr)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.connectionTimeoutMs(5000)
.sessionTimeoutMs(5000)
.build();
curatorFramework.start();
leaderLatch = new LeaderLatch(curatorFramework, path.concat("/testtast"));
}
@Lazy
@Scheduled(cron = "")
public void testTask() {
//是master节点的执行业务流程,使用leaderLatch.hasLeadership()方法判断是否为leader,true表示是master节点
if (!leaderLatch.hasLeadership()) return;
//TODO something
}
}
这里讲的的是Shared Reentrant Lock(共享可重入锁,推荐使用,Curator还封装了其他类型的锁:共享不可重入锁之类的):全局同步的、公平的分布式共享重入式锁,可保证在任意同一时刻,只有一个客户端持有锁。使用到的类是InterProcessMutex
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.BackgroundCallback;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* 类描述:Curator实现的分布式锁
* 创建人:simonsfan
*/
public class DistributedLock {
private static CuratorFramework curatorFramework;
private static InterProcessMutex interProcessMutex;
private static final String connectString = "10.200.121.46:2181,10.200.121.43:2181,10.200.121.167:2181";
private static final String root = "/root";
private static ExecutorService executorService;
private String lockName;
public String getLockName() {
return lockName;
}
public void setLockName(String lockName) {
this.lockName = lockName;
}
static {
curatorFramework = CuratorFrameworkFactory.builder().connectString(connectString).connectionTimeoutMs(5000).sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
executorService = Executors.newCachedThreadPool();
curatorFramework.start();
}
public DistributedLock(String lockName) {
this.lockName = lockName;
interProcessMutex = new InterProcessMutex(curatorFramework, root.concat(lockName));
}
/*上锁*/
public void tryLock() {
int count = 0;
try {
while (!interProcessMutex.acquire(1, TimeUnit.SECONDS)) {
count++;
if (count > 3) {
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/*释放*/
public void releaseLock() {
try {
if (interProcessMutex != null) {
interProcessMutex.release();
}
curatorFramework.delete().inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
}
}, executorService).forPath(root.concat(lockName));
} catch (Exception e) {
e.printStackTrace();
}
}
}
参考:http://curator.apache.org/getting-started.html
Curator的github:https://github.com/apache/curator