开源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. 2018.10.20 2018NOIP冲刺之酒厂选址

    题目传送门 明显能够看出有一个建图求路程的优化 然而发现10000*10000爆空间QAQ 为了做一些初始化方面的优化 我们发现了一个叫做前缀和的东西 可以在环上查到两个之间的最短距离 同时还要做一些 ...

  2. HTTP 学习笔记03

    通用信息头 Cache-Control : no-cache(不缓存当前请求) [*] Connection:close(返回当前请求后立即断开)[*] Date:...(HTTP消息产生的时间) P ...

  3. Python之Pandas库学习(三):数据处理

    1. 合并 可以将其理解为SQL中的JOIN操作,使用一个或多个键把多行数据结合在一起. 1.1. 简单合并 参数on表示合并依据的列,参数how表示用什么方式操作(默认是内连接). >> ...

  4. Codeforces Gym100502A:Amanda Lounges(DFS染色)

    http://codeforces.com/gym/100502/attachments 题意:有n个地点,m条边,每条边有一个边权,0代表两个顶点都染成白色,2代表两个顶点都染成黑色,1代表两个顶点 ...

  5. Spring Bean 生命周期之destroy——终极信仰

    上一篇文章 Spring Bean 生命周期之我从哪里来 说明了我是谁? 和 我从哪里来? 的两大哲学问题,今天我们要讨论一下终极哲学我要到哪里去? 初始化 Spring Bean 有三种方式: @P ...

  6. Maven发布项目到Nexus私服中 (发布jar包)

    目录 1 需求说明 2 实现步骤 2.1 Maven服务的setting.xml文件 2.2 项目的pom.xml文件 2.3 发布项目 1 需求说明 开发完项目后, 将项目版本发布到Nexus私服中 ...

  7. [常用命令]OSX命令

    在mac os下,如何通过命令行来下载网络文件?如果你没有安装或wget命令,那么可以使用curl工具来达到我们的目的. curl命令参数: curl ‘url地址’ curl [选项] ‘url地址 ...

  8. Java学习笔记之---单例模型

    Java学习笔记之---单例模型 单例模型分为:饿汉式,懒汉式 (一)要点 1.某个类只能有一个实例 2.必须自行创建实例 3.必须自行向整个系统提供这个实例 (二)实现 1.只提供私有的构造方法 2 ...

  9. vue组件间通信六种方式(完整版)

    本文总结了vue组件间通信的几种方式,如props. $emit/ $on.vuex. $parent / $children. $attrs/ $listeners和provide/inject,以 ...

  10. 百度小程序自定义通用toast组件

    百度小程序Toast组件 author: @TiffanysBear 百度小程序自定义通用toast组件 BdToast百度小程序自定义通用组件-github地址 需求 手百小程序的toast仅支持在 ...