开源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. 七牛云图床和Markdown使用

    七牛云图床和Markdown使用 1.图床是什么? 图床一般是指储存图片的服务器,有国内和国外之分.国外的图床由于有空间距离等因素决定访问速度很慢影响图片显示速度.国内也分为单线空间.多线空间和cdn ...

  2. Python解题技巧

    Python解题技巧 一直都是用C++和C解题,某题简单解完后便心血来潮想用Python解一次,发现一些问题,特写此篇随笔来记录. 一. 输入格式: 例:输入第1行给出正整数n和整数m:第2行给出n个 ...

  3. 云开发新能力,支持 HTTP 调用 API

    今天来上班打开电脑,总感觉微信开发文档哪里有点不太一样,研究了半天原来是云开发又多了神级功能--HTTP API! HTTP API是什么?简单来说就是通过云开发HTTP API,可以不需要通过微信小 ...

  4. MyBatis从入门到精通(2):MyBatis XML方式的基本用法

    本章将通过完成权限管理的常见业务来学习 MyBatis XML方式的基本用法 2.1一个简单的权限控制需求 权限管理的需求: 一个用户拥有若干角色,一个角色拥有若干权限,权限就是对某个模块资源的某种操 ...

  5. 【bfs基础】①

    bfs,即广度优先搜索,主要通过队列(queue)进行操作. 稍微解释一下,队列是一种基础数据结构,其形态类似于一支长长的队伍,大概如下: 在C++中,队列的头文件定义为:#include<qu ...

  6. python如何将一个多位数数值转换为列表类型

    现在:a = 10,由于暂时没找到更好的方法,且使用下面的方法进行转换. 目标:转化为['10'] 以下为错误尝试: 1.直接转换,提示整型对象不可迭代. 2.先转换为字符串,再转换为列表,发现被分成 ...

  7. 所谓guard进程不能启动

    1.网络开关的配置是true还是false? 2.服务里有个循环,所以不用打messagebox也是可以调试的. 3.虽然调式时条件变量已经变化,可以把断点拉到促成这个变化的逻辑路径上. 4.可以在调 ...

  8. Excel催化剂开源第15波-VSTO开发之DataTable数据导出至单元格区域

    上篇提到如何从Excel界面上拿到用户的数据,另外反方向的怎样输出给用户数据,也是关键之处. VSTO最大的优势是,这双向的过程中,全程有用户的交互操作. 而一般IT型的程序,都是脱离用户的操作,只能 ...

  9. java高并发系列 - 第17天:JUC中的循环栅栏CyclicBarrier常见的6种使用场景及代码示例

    这是java高并发系列第17篇. 本文主要内容: 介绍CyclicBarrier 6个示例介绍CyclicBarrier的使用 对比CyclicBarrier和CountDownLatch Cycli ...

  10. dedecms新建内容模型“把数据保存到数据库附加表时出错‘xxx’出错”错误的原因分析和解决方案(转)

    把数据保存到数据库附加表 `bc_addonarticle17` 时出错,请把相关信息提交给DedeCms官方.INSERT INTO `bc_addonarticle17`(aid,typeid,r ...