开源zk客户端-Curator

创建会话:

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.131.128:2181",retryPolicy);

使用CuratorFrameworkFactory工厂类的静态方法newClient来创建会话。

在重试策略上, Curator通过一个接口RetryPolicy来让用户实现自定义的重试策略。在RetryPolicy接口中只定义了一个方法:

boolean allowRetry(int var1, long var2, RetrySleeper var4);

三个参数分别为:

  • baseSleepTimeMs:初始sleep时间
  • maxRetries:最大重试次数
  • maxSleepMs:最大sleep时间

ExponentialBackoffRetry的重试策略设计如下:

给定一个初始sleep时间baseSleepTimeMs,在这个基础上结合重试次数,通过以下公式计算出当前需要sleep的时间:

当前sleep时间 = baseSleepTimeMs  * Math.max(1,random.nextInt(1 << (retryCount = 1)))

可以看出,随着重试次数的增加,计算出的sleep时间会越来越大。如果该sleep时间在maxSleepMs的范围之内,那么就使用该sleep时间,否则使用maxSleepMs。另外,maxRetries参数控制了最大重试次数,以避免无限制的重试。

另外,newClient方法并没有完成创建客户端的工作,你需要主动调用CuratorFramework的start()方法来完成创建客户端。

一个完整的创建客户端的例子:

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.CreateBuilder;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode; public class Demo1 {
public static void main(String[] args) {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.131.128:2181",retryPolicy);
client.start();
CreateBuilder builder = client.create();
try {
builder.withMode(CreateMode.EPHEMERAL).forPath("/test","ceshi".getBytes());
} catch (Exception e) {
e.printStackTrace();
}
}
}

创建一个节点,初始内容为空:

builder.forPath("/test","ceshi".getBytes());

注意,如果没有设置节点属性,那么Curator默认创建的是持久节点,内容默认是空。

创建一个临时节点,初始内容为空:

builder.withMode(CreateMode.EPHEMERAL).forPath("/test");

创建一个临时节点,并自动逆归创建父节点:

builder.creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/test","ceshi".getBytes());

这个接口非常有用,在使用ZooKeeper 的过程中,开发人员经常会碰到

NoNodeException异常,其中一个可能的原因就是试图对一个不存在的父节点创建子节点。因此,开发人员不得不在每次创建节点之前,都判断-下该父节点是否存在。在使用Curator之后,通过调用creatingParentsIfNeeded接口,Curator就能够自动地递归创建所有需要的父节点。

同时要注意的一点是,由于在ZooKeeper中规定了所有非叶子节点必须为持久节点,调用上面这个API之后,只有path参数对应的数据节点是临时节点,其父节点均为持久节点。

删除节点:

同样创建和删除操作都是由CuratorFramework接口发出来的。

client.delete().forPath("/test/test1");

使用上面的方法只能删除叶子节点。

删除一个节点,并递归删除其所有子节点:

client.delete().deletingChildrenIfNeeded().forPath("/test/test1");

删除一个节点,强制指定版本进行删除:

client.delete().withVersion(1).forPath("/test/test1");

删除一个节点,强制保证删除:

client.delete().guaranteed().forPath("/test/test1");

一个完整的创建节点删除节点的例子:

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.CreateBuilder;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode; public class Demo1 {
public static void main(String[] args) {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.131.128:2181",retryPolicy);
client.start();
CreateBuilder builder = client.create();
try {
builder.creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/test/test1","ceshi".getBytes());
client.delete().guaranteed().forPath("/test/test1"); } catch (Exception e) {
e.printStackTrace();
}
}
}

异步接口:

Curator中引入了BackgroundCallback接口,用来处理异步接口调用。CreateBuilder提供了一个inBackground()方法可供使用,此接口就是Curator提供的异步调用入口。对应的异步处理接口为BackgroundCallback。此接口指提供了一个processResult的方法,用来处理回调结果。其中processResult的参数event中的getType()包含了各种事件类型,getResultCode()包含了各种响应码。

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.CreateBuilder;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode; import java.util.concurrent.Executors; public class Demo2 {
public static void main(String[] args) {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.131.128:2181",retryPolicy);
client.start();
CreateBuilder builder = client.create();
try {
builder.withMode(CreateMode.PERSISTENT).inBackground(new BackgroundCallback() {
public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
System.out.println("当前线程:" + Thread.currentThread().getName() + ",code:"
+ event.getResultCode() + ",type:" + event.getType());
}
}, Executors.newFixedThreadPool(10)).forPath("/test"); builder.withMode(CreateMode.EPHEMERAL).inBackground(new BackgroundCallback() {
public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
System.out.println("当前线程:" + Thread.currentThread().getName() + ",code:"
+ event.getResultCode() + ",type:" + event.getType());
}
}).forPath("/test1"); } catch (Exception e) {
e.printStackTrace();
}
}
}

注意:如果自己指定了线程池,那么相应的操作就会在线程池中执行,如果没有指定,那么就会使用Zookeeper的EventThread线程对事件进行串行处理。

事件监听:

ZooKeeper原生支持通过注册Watcher来进行事件监听,但是其使用并不是特别方便,需要开发人员自己反复注册Watcher,比较繁琐。Curator 引入了Cache来实现对ZooKeeper服务端事件的监听。Cache是Curator 中对事件监听的包装,其对事件的监听其实可以近似看作是一个本地缓存视图和远程ZooKeeper视图的对比过程。同时Curator能够自动为开发人员处理反复注册监听,从而大大简化了原生API开发的繁琐过程。Cache分为两类监听类型:节点监听和子节点监听。

NodeCache:

NodeCache不仅可以用于监听数据节点的内容变更,也能监听指定节点是否存在。如果原本节点不存在,那么Cache 就会在节点被创建后触发NodeCachelistener。但是,如果该数据节点被删除,那么Curator就无法触发NodeCachelistener了。

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.CreateBuilder;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.recipes.cache.NodeCache;
import org.apache.curator.framework.recipes.cache.NodeCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode; import java.util.concurrent.Executors; public class Demo2 {
public static void main(String[] args) {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.131.128:2181",retryPolicy);
client.start();
CreateBuilder builder = client.create();
try {
final NodeCache cache = new NodeCache(client,"/test/test1",false);
cache.start();
cache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
System.out.println("cache: "+cache.getCurrentData().getData());
}
});
builder.creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/test/test1","ceshi".getBytes());
client.setData().forPath("/test/test1","haha".getBytes());
client.delete().deletingChildrenIfNeeded().forPath("/test/test1"); } catch (Exception e) {
e.printStackTrace();
}
}
}

PathChildrenCache:

PathChildrenCache用于监听指定ZooKeeper数据节点的子节点变化情况。

当指定节点的子节点发生变化时,就会回调该方法。PathChildrenCacheEvent类中定义了所有的事件类型,主要包括新增子节点(CHILD_ADDED)、子节点数据变更(CHILD_UPDATED)和子节点删除(CHILD_RE问OVED)三类。

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.CreateBuilder;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode; import java.util.concurrent.Executors; public class Demo2 {
public static void main(String[] args) {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.131.128:2181",retryPolicy);
client.start();
CreateBuilder builder = client.create();
try {
final PathChildrenCache cache = new PathChildrenCache(client,"/test",true);
cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
cache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
switch (pathChildrenCacheEvent.getType()){
case CHILD_ADDED:
System.out.println("CHILD_ADDED: "+pathChildrenCacheEvent.getData().getPath());
break;
case CHILD_UPDATED:
System.out.println("CHILD_UPDATED: "+pathChildrenCacheEvent.getData().getPath());
break;
case CONNECTION_SUSPENDED:
System.out.println("CONNECTION_SUSPENDED: "+pathChildrenCacheEvent.getData().getPath());
break;
case INITIALIZED:
System.out.println("INITIALIZED: "+pathChildrenCacheEvent.getData().getPath());
break;
case CONNECTION_RECONNECTED:
System.out.println("CONNECTION_RECONNECTED: "+pathChildrenCacheEvent.getData().getPath());
break;
case CHILD_REMOVED:
System.out.println("CHILD_REMOVED: "+pathChildrenCacheEvent.getData().getPath());
break;
case CONNECTION_LOST:
System.out.println("CONNECTION_LOST: "+pathChildrenCacheEvent.getData().getPath());
break;
}
}
});
builder.withMode(CreateMode.PERSISTENT).forPath("/test","ceshi".getBytes());
builder.withMode(CreateMode.PERSISTENT).forPath("/test/test1","ceshi".getBytes());
client.setData().forPath("/test/test1","haha".getBytes());
client.delete().deletingChildrenIfNeeded().forPath("/test/test1"); } catch (Exception e) {
e.printStackTrace();
}
}
}

4. Curator应用

4.1 master选举

有这样的场景:在分布式系统中,我们需要从集群中选举出一台机器作为master来分发任务。借助zk我们可以很方便的实现master选举功能。大体思路如下:

选择一个根节点,例如/master,多台机器同时向该节点创建一个子节点/master/lock ,利用ZooKeeper的特性,最终只有一台机器能够创建成功,成功的那台机器就作为Master。

Curator也是基于这个思路,但是它将节点创建、事件监听和自动选举过程进行了封装,开发人员只需要调用简单的API 即可实现Master选举。

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.CreateBuilder;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.framework.recipes.leader.LeaderSelector;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListener;
import org.apache.curator.framework.state.ConnectionState;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class Demo2 {
public static void main(String[] args) {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.131.128:2181",retryPolicy);
client.start();
ExecutorService excutor = Executors.newFixedThreadPool(20);
for(int i = 0;i<4;i++){
excutor.submit(new LeaderSelect(client));
}
}
} class LeaderSelect implements Runnable{ private CuratorFramework client; public LeaderSelect(CuratorFramework client) {
this.client = client;
} @Override
public void run() {
createLeader();
} private void createLeader (){
LeaderSelector selector = new LeaderSelector(client, "/test", new LeaderSelectorListener() {
@Override
public void takeLeadership(CuratorFramework curatorFramework) throws Exception {
System.out.println(Thread.currentThread().getName()+" 成为leader");
} @Override
public void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) { }
});
selector.autoRequeue();
selector.start();
}
}

输出结果:

Curator-LeaderSelector-2 成为leader
Curator-LeaderSelector-3 成为leader
Curator-LeaderSelector-1 成为leader
Curator-LeaderSelector-0 成为leader
Curator-LeaderSelector-2 成为leader
Curator-LeaderSelector-3 成为leader
Curator-LeaderSelector-1 成为leader
Curator-LeaderSelector-0 成为leader
Curator-LeaderSelector-2 成为leader
Curator-LeaderSelector-3 成为leader
Curator-LeaderSelector-1 成为leader
Curator-LeaderSelector-0 成为leader
Curator-LeaderSelector-2 成为leader
...
4.2 分布式锁

锁的问题经常会遇到,在分布式环境中更甚。zk实现分布式锁的逻辑是:各个节点同时在某个根节点”Lock”下创建临时顺序子节点:

/Lock/instance1_00001
/Lock/instance2_00002
/Lock/instance3_00003
...

然后对比谁的序号最小即谁获得锁。

那么Curator也是同理做了封装:InterProcessMutex类提供了分布式锁支持。

import org.apache.curator.RetryPolicy;
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; import java.text.SimpleDateFormat;
import java.util.Date; public class Demo5 {
public static void main(String[] args) {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.131.128:2181",retryPolicy);
client.start(); final InterProcessMutex lock = new InterProcessMutex(client,"/test/test1");
for(int i = 0;i<30;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
lock.acquire();
} catch (Exception e) {
e.printStackTrace();
}
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss|SSS");
String date = format.format(new Date());
System.out.println("date is : "+date);
try {
lock.release();
} catch (Exception e) {
e.printStackTrace();
} }
}).start();
}
}
}

结果:

date is : 2018-05-05 21:40:55|129
date is : 2018-05-05 21:40:55|175
date is : 2018-05-05 21:40:55|193
date is : 2018-05-05 21:40:55|249
date is : 2018-05-05 21:40:55|266
date is : 2018-05-05 21:40:55|291
date is : 2018-05-05 21:40:55|301
date is : 2018-05-05 21:40:55|314
date is : 2018-05-05 21:40:55|335
date is : 2018-05-05 21:40:55|357
date is : 2018-05-05 21:40:55|377
date is : 2018-05-05 21:40:55|383
date is : 2018-05-05 21:40:55|393
date is : 2018-05-05 21:40:55|404
date is : 2018-05-05 21:40:55|411
date is : 2018-05-05 21:40:55|422
date is : 2018-05-05 21:40:55|426
date is : 2018-05-05 21:40:55|431
date is : 2018-05-05 21:40:55|439
date is : 2018-05-05 21:40:55|446
date is : 2018-05-05 21:40:55|456
date is : 2018-05-05 21:40:55|465
date is : 2018-05-05 21:40:55|472
date is : 2018-05-05 21:40:55|480
date is : 2018-05-05 21:40:55|488
date is : 2018-05-05 21:40:55|492
date is : 2018-05-05 21:40:55|502
date is : 2018-05-05 21:40:55|519
date is : 2018-05-05 21:40:55|535
date is : 2018-05-05 21:40:55|541 Process finished with exit code 0
4.3 分布式计数器

如果有需求是在分布式环境中统计系统访问人数,那么这个时候分布式计数器可以发挥作用。基于zk的分布式计数器实现思路也很简单:

指定一个zk数据节点作为计数器,多个应用实例在分布式锁的控制下,通过更新该数据节点的内容来实现计数功能。

Curator同样将这一系列逻辑封装在了DistributedAtomic开头的类中,从其类名我们可以看出这是一个可以在分布式环境中使用的原子整型。具体使用与java中的Atomic类一样:

RetryPolicy policy = new RetryNTimes(3,1000);
DistributedAtomicLong atomicLong = new DistributedAtomicLong(client,"/test",policy);
try {
atomicLong.increment();
} catch (Exception e) {
e.printStackTrace();
}

Zookeeper开源客户端Curator的使用的更多相关文章

  1. Zookeeper开源客户端Curator之创建会话

    前面Zookeeper的链接使用的都是其提供的原生代码,实际开发过程中非常底层的细节开发工作如连接重连,反复注册等耗费开发人员大量的工作精力并且重复工作.而开源客户端Curator的出现解决了该类问题 ...

  2. 八:Zookeeper开源客户端Curator的api测试

    curator是Netflix公司开源的一套ZooKeeper客户端,Curator解决了很多ZooKeeper客户端非常底层的细节开发工作.包括连接重连,反复注册Watcher等.实现了Fluent ...

  3. zookeeper开源客户端curator

    zookeeper的原生api相对来说比较繁琐,比如:对节点添加监听事件,当监听触发后,我们需要再次手动添加监听,否则监听只生效一次:再比如,断线重连也需要我们手动代码来判断处理等等.对于curato ...

  4. Zookeeper开源客户端Curator之事件监听详解

    Curator对Zookeeper典型场景之事件监听进行封装,提供了使用参考.这篇博文笔者带领大家了解一下Curator的实现方式. 引入依赖 对于Curator封装Zookeeper的典型场景使用都 ...

  5. ZooKeeper(3.4.5) - 开源客户端 Curator(2.7.0) 的简单示例

    一.创建会话 1. 创建会话 package com.huey.dream.demo; import org.apache.curator.framework.CuratorFramework; im ...

  6. Apache Zookeeper Java客户端Curator使用及权限模式详解

    这篇文章是让大家了解Zookeeper基于Java客户端Curator的基本操作,以及如何使用Zookeeper解决实际问题. Zookeeper基于Java访问 针对zookeeper,比较常用的J ...

  7. Zookeeper开源客户端框架Curator简介

    Curator是Netflix开源的一套ZooKeeper客户端框架. Netflix在使用ZooKeeper的过程中发现ZooKeeper自带的客户端太底层, 应用方在使用的时候需要自己处理很多事情 ...

  8. Zookeeper开源客户端框架Curator简介[转]

    Curator是Netflix开源的一套ZooKeeper客户端框架. Netflix在使用ZooKeeper的过程中发现ZooKeeper自带的客户端太底层, 应用方在使用的时候需要自己处理很多事情 ...

  9. Zookeeper开源客户端框架Curator的使用

    CuratorFramework Curator框架提供了一套高级的API, 简化了ZooKeeper的操作. 话不多说,看代码 package com.donews.data.util import ...

随机推荐

  1. 深入理解Java内存模型JMM与volatile关键字

    深入理解Java内存模型JMM与volatile关键字 多核并发缓存架构 Java内存模型 Java线程内存模型跟CPU缓存模型类似,是基于CPU缓存模型来建立的,Java线程内存模型是标准化的,屏蔽 ...

  2. Navicat Premium Mac破解版安装方法

    第一步:这部分暂时存到文本编辑器中 公钥: -----BEGIN PUBLIC KEY-----MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQB8vXG0ImYh ...

  3. 学Redis这篇就够了

    Redis 简介 Redis 优势 Redis 数据类型 string hash list set Zset 小总结 基本命令 发布订阅 简介 实例 发布订阅常用命令 事务 实例 Redis 事务命令 ...

  4. centos下安装色彩scrapy

    一.安装Python2.7.6 更新CentOS lib库文件 yum -y update 安装开发工具包 yum groupinstall -y development 安装扩展包 yum inst ...

  5. AWS S3 上传文件

    一.获取签名的URL 通过后端给的接口拿到已经签名好的文件上传的URL地址 二.读取文件(注:AWS 接受的二进制,不能使用form-data) // 获取文件二进制 getFileMd5 = (ke ...

  6. ROS中URDF的学习以及与Xacro的比较

    1.urdf与Xacro简单比较 "URDF" 是最初也是比较简单的机器人描述文件,它的结构简单明了,容易理解.但是这也导致当机器人模型变得复杂时,urdf的结构描述就变得冗长,无 ...

  7. ASP.NET Core MVC 之模型(Model)

    1.模型绑定 ASP.NET Core MVC 中的模型绑定将数据从HTTP请求映射到操作方法参数.参数既可以是简单类型,也可以是复杂类型.MVC 通过抽象绑定解决了这个问题. 2.使用模型绑定 当 ...

  8. rabbitmq升级新版本后,需要新建用户。新版本默认禁止别的机器用guest用户访问。

    rabbitmq升级新版本后,需要新建用户.新版本默认禁止别的机器用guest用户访问.

  9. idea 警告:Warning:java: 源值1.5已过时, 将在未来所有发行版中删除

    在pom.xml文件中添加 <properties>         <maven.compiler.source>1.8</maven.compiler.source& ...

  10. 为什么选择 Spring 作为 Java 框架

    1. 概述 在本文中,我们将讨论 Spring 作为最流行的 Java 框架之一的主要价值体现. 最重要的是,我们将尝试理解 Spring 成为我们选择框架的原因.Spring 的详细信息及其组成部分 ...