开源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. Facebook也炒币吗?Libra币是什么?

    Facebook 在上周发布了加密数字货币,称为 Libra币. 太火爆了,很多人都在关注和讨论,包括一些科技大佬们都很积极的讨论(当然,这里指的是真正的科技大佬,比如 马化腾.王兴等,而不是指哪些割 ...

  2. java中session和application的用法

    Session的用法 首先创建2个jsp文件t1.jsp  t2.jsp 在t1.jsp <% //设置session的键与值 session.setAttribute("abc&qu ...

  3. 基于C#的机器学习--旅行推销员问题

    我们有一个必须在n个城市之间旅行的推销员.他不在乎什么顺序.他最先或最后访问的城市除外.他唯一关心的是他会去拜访每一个人,每个城市只有一次,最后一站是他得家. 每个城市都是一个节点,每个节点通过一条边 ...

  4. PCB 板边倒圆角的实现方法(基本算法一)

    PCB外形是直角时外形时,通常工程制作时,外是直角或尖角的地方倒圆角,主要是为了防止板边容易划伤板且容易扎伤人 所以当客户没有特殊要求时,PCB外形是直角时一般会默认倒角0.5mm圆角(如下图所示) ...

  5. Ubuntu系统 apt-get update失败解决办法

    使用apt-get的时候发现ubuntu和阿里云均已经不提供该版本的源,所以需要找到其他的替代源. 使用的ubuntu版本是14.10,属于非LTS(长期支持版本),因此前一段时间还可以使用apt-g ...

  6. java截取避免空字符丢失

    1. 场景描述 数据后端是Hbase等nosql数据库,返回的数据以逗号分隔,java后端获取数据后,需要新增组装数据后再返回给前端. 2. 问题解决 2.1 问题定位 本来用的java的split进 ...

  7. 硬件笔记之Thinkpad T470P更换2K屏幕

    0x00 前言 手上的Thinkpad T470P屏幕是1920x1080的屏幕,色域范围NTSC 45%,作为一块办公用屏是正常配置,但是考虑到色彩显示和色域范围,计划升级到2K屏幕. 2k屏幕参数 ...

  8. 如何在vue中监听scroll,从而实现滑动加载更多

    首先需要明确3个定义: 文档高度:整个页面的高度 可视窗口高度:你看到的浏览器可视屏幕高度 滚动条滚动高度: 滚动条下滑过的高度 当 文档高度 = 可视窗口高度 + 滚动条高度  时,滚动条正好到底. ...

  9. 微信小程序开发--页面结构

    一.微信小程序开发--页面文件组成 [page.name].js 页面逻辑文件,用于创建页面对象,以及处理页面生命周期控制和数据处理 [page.name].wxml wxml指的是Wei Xin M ...

  10. +p解决vim粘贴自动缩进。 数字gg跳到vim指定行。 vim查找到后,enter键修改

    +p解决vim粘贴自动缩进.  数字gg跳到vim指定行. vim查找到后,enter键修改