Zookeeper 系列(五)Curator API
Zookeeper 系列(五)Curator API
一、Curator 使用
Curator 框架中使用链式编程风格,易读性更强,使用工程方法创建连接对象使用。
(1) CuratorFrameworkFactory :俩个静态工厂方法(参数不同)来实现
- 参数1: connectString,连接串
- 参数2: retryPolicy,重试连接策略。有四种实现分别为:ExponentialBackoffRetry、RetryNTimes. RetryOneTimes、RetryUntilElapsed
- 参数3: sessionTimeoutMs 会话超时时间默认为 6000oms
- 参数4: connectionTimeOutms 连接超时时间,默认为 15000ms
注意:对于 retrypolicy 策略通过一个接口来让用户自定义实现。
(2) create :创建节点,可选链式项
(3) delete :删除节点,可选链式项
(4) getdata、setdata :读取和修改数据
(5) 异步绑定回调方法 :比如创建节点时绑定一个回调函数,该回调函数可以输出服务器的状态码以及服务器事件类型。还可以加入一个线程池进行优化操作。
(6) getchildren :读取子节点方法
(7) checkexists :判断节点是否存在方法
示例
(1) 环境准备
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.0</version>
</dependency>
(2) Curator 操作 Zookeeper
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.CuratorEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.junit.Test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author: leigang
* @version: 2018-04-06
*/
public class ZkCuratorBase {
private static final String CONNECT_ADDR = "127.0.0.1:2181";
private static final int SEESION_OUTTIME = 5 * 1000;
@Test
public void test() throws Exception {
//1. 重试策略:初试时间为1s,重试10次
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 10);
//2. 通过工厂创建连接
CuratorFramework cf = CuratorFrameworkFactory.builder()
.connectString(CONNECT_ADDR)
.sessionTimeoutMs(SEESION_OUTTIME)
.retryPolicy(retryPolicy)
.build();
//3. 开启连接
cf.start();
//4. 创建
cf.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT)
.forPath("/curator", "curator".getBytes());
//5. 获取
byte[] data = cf.getData().forPath("/curator");
System.out.println("/curator:" + new String(data));
//6. 修改
cf.setData().forPath("/curator", "curator-xxx".getBytes());
//7. 递归删除
cf.delete().guaranteed().deletingChildrenIfNeeded().forPath("/curator");
//8. 回调函数
ExecutorService pool = Executors.newCachedThreadPool();
cf.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT)
.inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework cf, CuratorEvent event) throws Exception {
System.out.println("code:" + event.getResultCode());
System.out.println("type:" + event.getType());
System.out.println("线程为:" + Thread.currentThread().getName());
}
}, pool)
.forPath("/curator", "curator-test".getBytes());
System.out.println("主线程为:" + Thread.currentThread().getName());
Thread.sleep(100 * 1000);
cf.close();
}
}
二、Curator Watcher
- NodeCacheListener :监听节点的新增、修改操作
- PathChildrenCacheListener :监听子节点的新增增、修改、别除操作
(1) 环境准备
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.0</version>
</dependency>
(2) NodeCacheListener
// 节点本身监听
public class ZkCuratorWatcher1 {
private static final String CONNECT_ADDR = "127.0.0.1:2181";
private static final int SEESION_OUTTIME = 5 * 1000;
@Test
public void test() throws Exception {
//1. 重试策略:初试时间为1s,重试10次
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 10);
//2. 通过工厂创建连接
CuratorFramework cf = CuratorFrameworkFactory.builder()
.connectString(CONNECT_ADDR)
.sessionTimeoutMs(SEESION_OUTTIME)
.retryPolicy(retryPolicy)
.build();
//3. 开启连接
cf.start();
//4. 建立一个cache缓存
NodeCache cache = new NodeCache(cf, "/curator", false);
cache.start(true);
cache.getListenable().addListener(new NodeCacheListener() {
//触发事件为创建、更新、删除节点
@Override
public void nodeChanged() throws Exception {
ChildData data = cache.getCurrentData();
if (data != null) {
System.out.println("路径为:" + cache.getCurrentData().getPath());
System.out.println("数据为:" + new String(cache.getCurrentData().getData()));
System.out.println("状态为:" + cache.getCurrentData().getStat());
} else {
System.out.println("删除节点");
}
System.out.println("================================================");
}
});
Thread.sleep(1000);
cf.create().forPath("/curator", "123".getBytes());
Thread.sleep(1000);
cf.setData().forPath("/curator", "12344".getBytes());
Thread.sleep(1000);
cf.delete().forPath("/curator");
Thread.sleep(10 * 1000);
cf.close();
}
}
(3) PathChildrenCacheListener
// 子节点监听
public class ZkCuratorWatcher2 {
private static final String CONNECT_ADDR = "127.0.0.1:2181";
private static final int SEESION_OUTTIME = 5 * 1000;
@Test
public void test() throws Exception {
//1. 重试策略:初试时间为1s,重试10次
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 10);
//2. 通过工厂创建连接
CuratorFramework cf = CuratorFrameworkFactory.builder()
.connectString(CONNECT_ADDR)
.sessionTimeoutMs(SEESION_OUTTIME)
.retryPolicy(retryPolicy)
.build();
//3. 开启连接
cf.start();
//4. 建立一个 PathChildrenCache 缓存,第三个参数为是否接受节点数据内容,如果为 false 则不接受
PathChildrenCache cache = new PathChildrenCache(cf, "/curator", true);
//5. 在初始化的时候就进行缓存监听
cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
cache.getListenable().addListener(new PathChildrenCacheListener() {
// 监听子节点变量,新建、修改、删除
@Override
public void childEvent(CuratorFramework cf, PathChildrenCacheEvent event) throws Exception {
switch (event.getType()) {
case CHILD_ADDED:
System.out.println("CHILD_ADDED:" + event.getData().getPath());
break;
case CHILD_UPDATED:
System.out.println("CHILD_UPDATED:" + event.getData().getPath());
break;
case CHILD_REMOVED:
System.out.println("CHILD_REMOVED:" + event.getData().getPath());
break;
default:
break;
}
}
});
cf.create().creatingParentsIfNeeded().forPath("/curator/c1", "c1".getBytes());
cf.create().creatingParentsIfNeeded().forPath("/curator/c2", "c2".getBytes());
Thread.sleep(1000);
cf.setData().forPath("/curator/c1", "c1-update".getBytes());
cf.delete().guaranteed().deletingChildrenIfNeeded().forPath("/curator");
cf.close();
}
}
三、Curator 场景应用
(一)分布式锁
在分布式场景中,我们为了保证数据的一性,经常在程序运行的某一个点需要进行同步操作(java 可提供 synchronized 或者 Reentrantlock 实现)比如我们看一个小示例,这个示例会出现分布式不同步的问题:因为我们之前所说的是在高并发下访问一个程序,现在我们则是在高并发下访问多个服务器节点(分布式)。
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;
/**
* ReentrantLock
* @author: leigang
* @version: 2018-04-06
*/
public class Lock1 {
private static ReentrantLock reentrantLock = new ReentrantLock();
private static int count = 10;
public static void genarNo() {
try {
reentrantLock.lock();
count--;
System.out.println(count);
} finally {
reentrantLock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
countDownLatch.await();
genarNo();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
Thread.sleep(1000);
countDownLatch.countDown();
}
}
我们使用 Curator 基于 zookeeper 的特性提供的分布式锁来处理分布式场景的数据一致性,zookeeper 本身的分布式是有写问题的,这里强烈推荐使用 Curator 的分布式锁!
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.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;
/**
* 由 Zookeeper 实现的分布式锁
* @author: leigang
* @version: 2018-04-06
*/
public class Lock2 {
private static final String CONNECT_ADDR = "127.0.0.1:2181";
private static final int SEESION_OUTTIME = 5 * 1000;
public static CuratorFramework createCuratorFramework() {
//1. 重试策略:初试时间为1s,重试10次
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 10);
//2. 通过工厂创建连接
CuratorFramework cf = CuratorFrameworkFactory.builder()
.connectString(CONNECT_ADDR)
.sessionTimeoutMs(SEESION_OUTTIME)
.retryPolicy(retryPolicy)
.build();
return cf;
}
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
CuratorFramework cf = createCuratorFramework();
cf.start();
// 由 Zookeeper 实现的分布式锁
InterProcessMutex lock = new InterProcessMutex(cf, "/curator");
//ReentrantLock reentrantLock = new ReentrantLock();
try {
countDownLatch.await();
lock.acquire();
//reentrantLock.lock();
genarNo();
System.out.println(Thread.currentThread().getName() + "执行业务逻辑...");
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 释放
lock.release();
//reentrantLock.unlock();
} catch (Exception e) {
;
}
}
}
}).start();
}
Thread.sleep(1000);
countDownLatch.countDown();
}
private static int count = 10;
public static void genarNo() {
System.out.println(--count);
}
}
(二)分布式计数器
分布式计数器功能一说到分布式计数器,你可能脑海里想到了 AtomicInteger 这种经典的方式如果针对于一个 JVM 的场景当然没有问题,但是我们现在是分布式场景下,就需要利用 Curator 框架的 DistributedAtomiclnteger 了。
public class ZkCuratorAtomicInteger {
private static final String CONNECT_ADDR = "127.0.0.1:2181";
private static final int SEESION_OUTTIME = 5 * 1000;
@Test
public void test() throws Exception {
//1. 重试策略:初试时间为1s,重试10次
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 10);
//2. 通过工厂创建连接
CuratorFramework cf = CuratorFrameworkFactory.builder()
.connectString(CONNECT_ADDR)
.sessionTimeoutMs(SEESION_OUTTIME)
.retryPolicy(retryPolicy)
.build();
//3. 开启连接
cf.start();
//4. 使用 DistributedAtomicInteger
DistributedAtomicInteger atomicInteger = new DistributedAtomicInteger(cf, "/curator",
new RetryNTimes(3, 1000));
atomicInteger.forceSet(0);
atomicInteger.increment();
AtomicValue<Integer> value = atomicInteger.get();
System.out.println(value.succeeded());
System.out.println(value.postValue()); //最新值????????
System.out.println(value.preValue()); //原始值????????
}
}
(三)Barrier
public class ZkCuratorBarrier1 {
private static final String CONNECT_ADDR = "127.0.0.1:2181";
private static final int SEESION_OUTTIME = 5 * 1000;
public static CuratorFramework createCuratorFramework() {
//1. 重试策略:初试时间为1s,重试10次
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 10);
//2. 通过工厂创建连接
CuratorFramework cf = CuratorFrameworkFactory.builder()
.connectString(CONNECT_ADDR)
.sessionTimeoutMs(SEESION_OUTTIME)
.retryPolicy(retryPolicy)
.build();
return cf;
}
@Test
public void test() throws InterruptedException {
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
CuratorFramework cf = createCuratorFramework();
cf.start();
DistributedDoubleBarrier barrier = new DistributedDoubleBarrier(cf, "/barrier", 5);
Thread.sleep(1000 * (new Random().nextInt(3)));
System.out.println(Thread.currentThread().getName() + "已经准备");
barrier.enter();
System.out.println("同时开始运行...");
Thread.sleep(1000 * (new Random().nextInt(3)));
System.out.println(Thread.currentThread().getName() + "运行完毕");
barrier.leave();
System.out.println("同时退出运行...");
} catch (Exception e) {
e.printStackTrace();
}
}
}, "t" + i).start();
}
Thread.sleep(100 * 1000);
}
}
运行结果:
t1已经准备
t3已经准备
t2已经准备
t4已经准备
同时开始运行...
同时开始运行...
同时开始运行...
同时开始运行...
同时开始运行...
t3运行完毕
t4运行完毕
t0运行完毕
t2运行完毕
t1运行完毕
同时退出运行...
同时退出运行...
同时退出运行...
同时退出运行...
同时退出运行...
(四)Cluster
(1) Zookeeper 监听器,用于监听子节点的变化:
public class ZkCuratorWatcher {
private static final String CONNECT_ADDR = "127.0.0.1:2181";
private static final int SEESION_OUTTIME = 5 * 1000;
public ZkCuratorWatcher() {
try {
//1. 重试策略:初试时间为1s,重试10次
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 10);
//2. 通过工厂创建连接
CuratorFramework cf = CuratorFrameworkFactory.builder()
.connectString(CONNECT_ADDR)
.sessionTimeoutMs(SEESION_OUTTIME)
.retryPolicy(retryPolicy)
.build();
//3. 开启连接
cf.start();
//4. 创建根节点
if (cf.checkExists().forPath("/cluster") == null) {
cf.create().withMode(CreateMode.PERSISTENT).forPath("/cluster", "cluster".getBytes());
}
//5. 建立一个 PathChildrenCache 缓存,第三个参数为是否接受节点数据内容,如果为 false 则不接受
PathChildrenCache cache = new PathChildrenCache(cf, "/cluster", true);
//6. 在初始化的时候就进行缓存监听
cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
cache.getListenable().addListener(new PathChildrenCacheListener() {
// 监听子节点变量,新建、修改、删除
@Override
public void childEvent(CuratorFramework cf, PathChildrenCacheEvent event) throws Exception {
switch (event.getType()) {
case CHILD_ADDED:
System.out.println("CHILD_ADDED:" + event.getData().getPath());
break;
case CHILD_UPDATED:
System.out.println("CHILD_UPDATED:" + event.getData().getPath());
break;
case CHILD_REMOVED:
System.out.println("CHILD_REMOVED:" + event.getData().getPath());
break;
default:
break;
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
(2) 启动两个客户端:
public class Client1 {
@Test
public void test() throws InterruptedException {
ZkCuratorWatcher zkCuratorWatcher = new ZkCuratorWatcher();
System.out.println(this.getClass().getSimpleName() + " start...");
Thread.sleep(Integer.MAX_VALUE);
}
}
public class Client2 {
@Test
public void test() throws InterruptedException {
ZkCuratorWatcher zkCuratorWatcher = new ZkCuratorWatcher();
System.out.println(this.getClass().getSimpleName() + " start...");
Thread.sleep(Integer.MAX_VALUE);
}
}
(3) 第三个客户端用于修改 Zookeeper 的节点,观察 Client1 和 Client2 是否监听到了节点的改变:
public class Test {
private static final String CONNECT_ADDR = "127.0.0.1:2181";
private static final int SEESION_OUTTIME = 5 * 1000;
@org.junit.Test
public void test() throws Exception {
//1. 重试策略:初试时间为1s,重试10次
ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 10);
//2. 通过工厂创建连接
CuratorFramework cf = CuratorFrameworkFactory.builder()
.connectString(CONNECT_ADDR)
.sessionTimeoutMs(SEESION_OUTTIME)
.retryPolicy(retryPolicy)
.build();
//3. 开启连接
cf.start();
cf.create().creatingParentsIfNeeded().forPath("/cluster/c1", "c1".getBytes());
cf.create().creatingParentsIfNeeded().forPath("/cluster/c2", "c2".getBytes());
Thread.sleep(1000);
cf.setData().forPath("/cluster/c1", "c1-update".getBytes());
cf.delete().guaranteed().deletingChildrenIfNeeded().forPath("/cluster/c1");
cf.delete().guaranteed().deletingChildrenIfNeeded().forPath("/cluster/c2");
cf.close();
}
}
(4) 运行结果,Client1 和 Client2 下都有输出如下结果,说明都监听到了 /cluster 下子节点的添加、修改和删除:
CHILD_ADDED:/cluster/c1
CHILD_ADDED:/cluster/c2
CHILD_UPDATED:/cluster/c1
CHILD_REMOVED:/cluster/c1
CHILD_REMOVED:/cluster/c2
(5) 下面做另外一个测试,在 Zookeeper 下手动创建 /cluster 节点后,再启动 Client1:
[zk: localhost:2181(CONNECTED) 3] ls /cluster
[]
[zk: localhost:2181(CONNECTED) 4] create /cluster/c1 c1
[zk: localhost:2181(CONNECTED) 6] ls /cluster
[c1]
运行 Client1 结果如下:
Client1 start...
CHILD_ADDED:/cluster/c1
以上测试说明 Curator 会自动将触发 CHILD_ADDED 事件,工作中可以用来自注册,不需要手动查询来注册。
Zookeeper 系列(五)Curator API的更多相关文章
- Zookeeper系列2 原生API 以及核心特性watcher
原生API 增删改查询 public class ZkBaseTest { static final String CONNECT_ADDR = "192.168.0.120"; ...
- Zookeeper系列五:Master选举、ZK高级特性:基本模型
一.Master选举 1. master选举原理: 有多个master,每次只能有一个master负责主要的工作,其他的master作为备份,同时对负责工作的master进行监听,一旦负责工作的mas ...
- Zookeeper系列三:Zookeeper客户端的使用(Zookeeper原生API如何进行调用、ZKClient、Curator)和Zookeeper会话
一.Zookeeper原生API如何进行调用 准备工作: 首先在新建一个maven项目ZK-Demo,然后在pom.xml里面引入zk的依赖 <dependency> <groupI ...
- Zookeeper 系列(三)Zookeeper API
Zookeeper 系列(三)Zookeeper API 本节首先介绍 Zookeeper 的 Shell 命令,再对 Java 操作 Zookeeper 的三种方式进行讲解,本节先介绍 Zookee ...
- ZooKeeper系列(四)—— Java 客户端 Apache Curator
一.基本依赖 Curator 是 Netflix 公司开源的一个 Zookeeper 客户端,目前由 Apache 进行维护.与 Zookeeper 原生客户端相比,Curator 的抽象层次更高,功 ...
- Zookeeper 系列(四)ZKClient API
Zookeeper 系列(四)ZKClient API 环境准备: <dependency> <groupId>com.101tec</groupId> <a ...
- 八:Zookeeper开源客户端Curator的api测试
curator是Netflix公司开源的一套ZooKeeper客户端,Curator解决了很多ZooKeeper客户端非常底层的细节开发工作.包括连接重连,反复注册Watcher等.实现了Fluent ...
- 【ZooKeeper系列】2.用Java实现ZooKeeper API的调用
温馨提示:在这里我再次提个小要求,希望大家能习惯看官方文档,文档虽然是英文但用词都比较简单,基本都能看懂文档表达的意思.授之以鱼不如授之以渔的道理相信大家都明白,也希望通过猿人谷的这个ZooKeepe ...
- Zookeeper的java客户端API使用方法(五)
前面几篇博文,我们简单的介绍了一下zookeeper,如何安装zookeeper集群,以及如何使用命令行等.这篇博文我们重点来看下Zookeeper的java客户端API使用方式. 创建会话 客户端可 ...
随机推荐
- os模块和sys模块,以及random模块
os模块 os模块是与操作系统交互的一个接口 os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname") 改变当前脚本工 ...
- PHP图片压缩
<?php /** * 分享请保持网址.尊重别人劳动成果.谢谢. * 图片压缩类:通过缩放来压缩.如果要保持源图比例,把参数$percent保持为1即可. * 即使原比例压缩,也可大幅度缩小.数 ...
- msq_table's methods2
-- 删除数据 自增长id被占用 -- 清楚所有数据并重置id 1 truncate table name; -- 主键(唯一) id int primary key; -- 主键内容不能重复,不能为 ...
- Redis基本操作-list
Redis的5种数据结构:string.list.hash.set和zset; Redis 所有的数据结构都是以唯一的 key 字符串作为名称,然后通过这个唯一 key 值来获取相应的 value 数 ...
- 脱离SVN的控制
在桌面创建一个记事本文件,然后吧这句话复制进去for /r . %%a in (.) do @if exist "%%a\.svn" rd /s /q "%%a\.svn ...
- HTML5 画图--文字
1:html <div style="margin:0 auto;width:794px;height:1123px"> <canvas id="myC ...
- 利用R求分位数及画出箱型图
1)数据集 data<-c(75.0,64.0,47.4,66.9,62.2,62.2,58.7,63.5,66.6,64.0,57.0,69.0,56.9,50.0,72.0) 默认是四分位: ...
- Loadrunner进行参数化
Loadrunner进行参数化 Loadrunner中进行参数化,这里有三种方法. 对需要多次使用的变量进行参数化,比如登录信息的用户名和密码,首先需要选中需要参数化的值,然后右键选择Replace ...
- 安装64位的oracle数据库, 使用自带的sqldeveloper
个人感觉这个东西比plsql要好用, 虽然界面有点丑, 整个使用与plsql也没多大区别, 这里是他的位置C:\oracle_11g\product\11.2.0\dbhome_1\sqldevelo ...
- mysql数据类型长度
1个字节= 8位 tinyint 为一个字节 2的8次方= 256 所以最多存储到256 日期和时间数据类型 MySQL数据类型 含义 date 3字节,日期,格式:2014-09-18 time ...