目录
curator
curator是Netflix开源的一个zookeeper客户端,后来捐给apache。curator框架在zookeeper原生API接口上进行了包装,解决了很多zookeeper客户端非常底层的细节开发。提供zookeeper各种应用场景(分布式锁、集群领导选举、共享计数器、缓存机制、分布式队列等)的抽象封装,实现了Fluent风格的API接口,是最流行的zookeeper客户端。
原生zookeeperAPI的不足:
- 连接对象异步创建,需要开发人员自行编码等待
- 连接没有自动超时重连机制
- watcher一次注册只能生效一次
- 不支持递归创建树形结点
curator的特点:
- 解决session会话超时重连
- watcher可以反复注册
- 简化API使用
- 支持Fluent风格
- 提供了分布式锁服务、共享计数器、缓存等机制
依赖
<!-- Apache下的Zookeeper架包-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.5</version>
</dependency>
<!--curator-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.6.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.6.0</version>
</dependency>
使用curator连接zookeeper
public static void main(String[] args) {
CuratorFramework client = CuratorFrameworkFactory.builder()
//集群用逗号隔开
.connectString("192.168.63.129:2181")
//超时时间
.sessionTimeoutMs(5000)
//重连机制,超时3秒重连一次
//RetryOneTime 指定时间后重连一次
//RetryNTimes 指定时间重连n次
//RetryUntilElapsed 每隔指定时间后重连一次,不能超过设置总时间
//ExponentialBackoffRetry 重连n次,时间基于参数计算,越来越长
.retryPolicy(new RetryOneTime(3000))
//指定命名空间
.namespace("create")
//构建连接
.build();
//打开连接
client.start();
System.out.println(client.isStarted() ? "连接成功" : "连接失败");
//关闭连接
client.close();
}
工具类:
public class CuratorUtil {
private static CuratorFramework client=null;
public static CuratorFramework Connect(){
client = CuratorFrameworkFactory.builder()
//集群用逗号隔开
.connectString("192.168.63.129:2181")
//超时时间
.sessionTimeoutMs(5000)
//重连机制,超时3秒重连一次
//RetryOneTime 指定时间后重连一次
//RetryNTimes 指定时间重连n次
//RetryUntilElapsed 每隔指定时间后重连一次,不能超过设置总时间
//ExponentialBackoffRetry 重连n次,时间基于参数计算,越来越长
.retryPolicy(new RetryOneTime(3000))
//指定命名空间
.namespace("create")
//构建连接
.build();
//打开连接
client.start();
return client;
}
public static void close(){
if (client!=null){
//关闭连接
client.close();
}
}
}
新增结点 create
public static void main(String[] args) {
CuratorFramework client = CuratorUtil.Connect();
System.out.println(client.isStarted()?"连接成功":"连接失败");
try {
//创建持久化节点
String s = client.create()
//创建持久化节点
.withMode(CreateMode.PERSISTENT)
//权限列表为 world:anyone:cdrwa
.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
//arg1结点路径 arg2结点数据 由于指定了命名空间 会创建/create/node1
.forPath("/node2", "aaa".getBytes());
//返回节点路径"/node2"
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}finally {
CuratorUtil.close();
}
}
自定义权限列表
@BeforeEach
public void connect(){
client = CuratorUtil.Connect();
}
@AfterEach
public void close(){
client.close();
}
//自定义权限
@Test
public void test01() throws Exception {
//自定义权限列表
List<ACL> list=new ArrayList<>();
Id id=new Id("ip","192.168.63.129");
list.add(new ACL(ZooDefs.Perms.ALL,id));
//新增结点
client.create()
.withMode(CreateMode.PERSISTENT)
.withACL(list)
.forPath("/node3","2020GetGoodOffer".getBytes());
}
递归创建
//递归创建
@Test
public void test02() throws Exception {
client.create()
//如果父路径不存在则创建
.creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)
.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
.forPath("/node3/node31","2020GetGoodOffer".getBytes());
}
异步创建
//异步创建
@Test
public void create4() throws Exception{
//异步创建
client.create()
//如果父路径不存在则创建
.creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)
.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
//异步回调方法
.inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) {
System.out.println("结点路径"+curatorEvent.getPath());
System.out.println("事件类型"+curatorEvent.getType());
}
})
.forPath("/node4","2020GetGoodOffer".getBytes());
//防止异步回调还没执行完,程序就结束了
Thread.sleep(5000);
}
更改结点 setData
@BeforeEach
public void connect(){
client = CuratorUtil.Connect();
}
@AfterEach
public void close(){
client.close();
}
//更新节点
@Test
public void set01() throws Exception {
client.setData().forPath("/node1","bbb".getBytes());
}
//使用版本号更新
@Test
public void set2() throws Exception{
//使用版本号更新 -1代表不参与
client.setData()
//-1不更新版本号
.withVersion(0)
.forPath("/node2","2020GetGoodOffer".getBytes());
}
//异步修改
@Test
public void set3() throws Exception{
client.setData()
.withVersion(0)
.inBackground(new BackgroundCallback() {//异步回调方法
@Override
public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) {
System.out.println("结点路径"+curatorEvent.getPath());
System.out.println("事件类型"+curatorEvent.getType());
}
})
.forPath("/node4","hahah".getBytes());
Thread.sleep(5000);
}
删除结点 delete
@BeforeEach
public void connect(){
client = CuratorUtil.Connect();
}
@AfterEach
public void close(){
client.close();
}
//删除节点
@Test
public void del01() throws Exception {
client.delete().forPath("/node4");
}
//使用版本号删除
@Test
public void del2() throws Exception{
//使用版本号删除 -1代表不参与
client.delete()
//-1不更新版本号
.withVersion(1)
.forPath("/node2");
}
//递归删除
@Test
public void del3() throws Exception{
client.delete()
.deletingChildrenIfNeeded()
.forPath("/node3");
}
//异步删除
@Test
public void del4() throws Exception{
client.delete()
.withVersion(-1)
.inBackground(new BackgroundCallback() {//异步回调方法
@Override
public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) {
System.out.println("结点路径"+curatorEvent.getPath());
System.out.println("事件类型"+curatorEvent.getType());
}
})
.forPath("/node4");
Thread.sleep(5000);
}
查看结点 get
@BeforeEach
public void connect(){
client = CuratorUtil.Connect();
}
@AfterEach
public void close(){
client.close();
}
//读取结点
@Test
public void get01() throws Exception {
byte[] node1s = client.getData().forPath("node1");
System.out.println(new String(node1s));
}
//读取数据时读取结点属性
@Test
public void get2() throws Exception{
Stat stat=new Stat();
byte[] bytes = client.getData()
.storingStatIn(stat)
.forPath("/node1");
System.out.println("node1:"+ new String(bytes));
System.out.println("node1 version:"+stat.getVersion());
}
//异步读取
@Test
public void get3() throws Exception{
Executor executor = Executors.newFixedThreadPool(2);
client.getData()
.inBackground((curatorFramework, curatorEvent) -> {
System.out.println("结点路径:" + curatorEvent.getPath());
System.out.println("事件类型:" + curatorEvent.getType());
System.out.println("数据:"+new String(curatorEvent.getData()));
},executor)
.forPath("/node3");
Thread.sleep(5000);
System.out.println("结束");
}
读取子结点数据 getChildren
@BeforeEach
public void connect(){
client = CuratorUtil.Connect();
}
@AfterEach
public void close(){
client.close();
}
//读取子结点
@Test
public void getChild1() throws Exception{
List<String> list = client.getChildren()
.forPath("/node3");
System.out.println(list);
}
//读取数据时读取结点属性
@Test
public void getChild2() throws Exception{
Stat stat=new Stat();
client.getChildren()
.storingStatIn(stat)
.forPath("/node3");
}
//异步读取子节点
@Test
public void getChild3() throws Exception{
client.getChildren()
.inBackground(new BackgroundCallback() {//异步回调方法
@Override
public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) {
System.out.println("结点路径"+curatorEvent.getPath());
System.out.println("事件类型"+curatorEvent.getType());
System.out.println(curatorEvent.getChildren());
}
})
.forPath("/node3");
Thread.sleep(5000);
}
检查结点是否存在 checkExists
@BeforeEach
public void connect(){
client = CuratorUtil.Connect();
}
@AfterEach
public void close(){
client.close();
}
//判断结点是否存在
@Test
public void exists1() throws Exception{
Stat stat = client.checkExists()
.forPath("/node1");
//stat:370,385,1590978226504,1590980312872,1,0,0,0,3,0,370
System.out.println("stat:"+stat);
}
//异步判断
@Test
public void exists2() throws Exception{
client.checkExists()
.inBackground(new BackgroundCallback() {//异步回调方法
@Override
public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) {
System.out.println("结点路径"+curatorEvent.getPath());
System.out.println("事件类型"+curatorEvent.getType());
System.out.println("判断结果:"+curatorEvent.getStat().getVersion());
}
})
.forPath("/node1");
Thread.sleep(5000);
}
watcher
NodeCache 监听特定结点
@BeforeEach
public void connect(){
client = CuratorUtil.Connect();
}
@AfterEach
public void close(){
client.close();
}
//监听特定节点
@Test
public void watcher1() throws Exception{
//监视某个结点 arg1连接对象 arg2监视路径
NodeCache nodeCache=new NodeCache(client,"/watcher1");
//启动监视器
nodeCache.start();
System.out.println("监视器已打开");
nodeCache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
System.out.println("结点路径: "+nodeCache.getCurrentData().getPath());
System.out.println("结点数据: "+nodeCache.getCurrentData().getData());
}
});
Thread.sleep(150000);
//关闭监视器
nodeCache.close();
System.out.println("监视器已关闭");
}
PathChildren Cache 监听子结点
//监听子节点
@Test
public void watcher2() throws Exception{
//监视子结点 arg1 连接对象 arg2 监视路径 arg3 能否读取数据
PathChildrenCache pathChildrenCache=new PathChildrenCache(client,"/watcher1",true);
//启动监视器
pathChildrenCache.start();
System.out.println("监视器已打开");
pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) {
System.out.println("结点事件类型 :"+pathChildrenCacheEvent.getType());
System.out.println("结点的路径: "+pathChildrenCacheEvent.getData().getPath());
System.out.println("结点数据: "+new String(pathChildrenCacheEvent.getData().getData()));
}
});
Thread.sleep(150000);
//关闭监视器
pathChildrenCache.close();
System.out.println("监视器已关闭");
}
新增子节点时:
结点事件类型 :CHILD_ADDED
结点的路径: /watcher1/w1
结点数据: 123
删除子节点时:
结点事件类型 :CHILD_REMOVED
结点的路径: /watcher1/w1
结点数据: 123
事务 inTransaction
//开启事务
@Test
public void tran2() throws Exception{
client.inTransaction()
//创建一个节点
.create().forPath("/node6","ccc".getBytes())
.and()
//更新一个不存在的节点,必然失败,更新和创建这两操作全回滚
.setData().forPath("/node5","666".getBytes())
.and()
.commit();
}
分布式锁
分布式可重入排它锁
//分布式可重入排它锁
@Test
public void lock1() throws Exception{
//排它锁
InterProcessLock interProcessLock=new InterProcessMutex(client,"/lock1");
System.out.println("等待获取锁对象");
interProcessLock.acquire();
for(int i=1;i<=10;i++){
Thread.sleep(1000);
System.out.println(i);
}
interProcessLock.release();
System.out.println("等待释放锁");
}
同时启动两个客户端,启动配置:
客户端1:
当第一个客户端释放了锁,第二个才能继续:
分布式读写锁
读锁
//分布式读写锁
@Test
public void lock2() throws Exception{
//读写锁
InterProcessReadWriteLock interProcessReadWriteLock=new InterProcessReadWriteLock(client,"/lock1");
InterProcessMutex interProcessLock = interProcessReadWriteLock.readLock();
System.out.println("等待读锁对象");
interProcessLock.acquire();
for(int i=1;i<=10;i++){
Thread.sleep(3000);
System.out.println(i);
}
interProcessLock.release();
System.out.println("等待释放锁");
}
两个客户端可以同时读,读锁是共享锁:
写锁
//写锁 写锁之间、写锁与读锁都是互斥的
@Test
public void lock3() throws Exception{
InterProcessReadWriteLock interProcessReadWriteLock=new InterProcessReadWriteLock(client,"/lock1");
InterProcessMutex interProcessLock = interProcessReadWriteLock.writeLock();
System.out.println("等待写锁对象");
interProcessLock.acquire();
for(int i=1;i<=10;i++){
Thread.sleep(1000);
System.out.println(i);
}
interProcessLock.release();
System.out.println("等待释放锁");
}
演示效果和分布式可重入排它锁一样。