博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Zookeeper开源客户端Curator的使用
阅读量:2213 次
发布时间:2019-05-06

本文共 16555 字,大约阅读时间需要 55 分钟。

开源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 成为leaderCurator-LeaderSelector-3 成为leaderCurator-LeaderSelector-1 成为leaderCurator-LeaderSelector-0 成为leaderCurator-LeaderSelector-2 成为leaderCurator-LeaderSelector-3 成为leaderCurator-LeaderSelector-1 成为leaderCurator-LeaderSelector-0 成为leaderCurator-LeaderSelector-2 成为leaderCurator-LeaderSelector-3 成为leaderCurator-LeaderSelector-1 成为leaderCurator-LeaderSelector-0 成为leaderCurator-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|129date is : 2018-05-05 21:40:55|175date is : 2018-05-05 21:40:55|193date is : 2018-05-05 21:40:55|249date is : 2018-05-05 21:40:55|266date is : 2018-05-05 21:40:55|291date is : 2018-05-05 21:40:55|301date is : 2018-05-05 21:40:55|314date is : 2018-05-05 21:40:55|335date is : 2018-05-05 21:40:55|357date is : 2018-05-05 21:40:55|377date is : 2018-05-05 21:40:55|383date is : 2018-05-05 21:40:55|393date is : 2018-05-05 21:40:55|404date is : 2018-05-05 21:40:55|411date is : 2018-05-05 21:40:55|422date is : 2018-05-05 21:40:55|426date is : 2018-05-05 21:40:55|431date is : 2018-05-05 21:40:55|439date is : 2018-05-05 21:40:55|446date is : 2018-05-05 21:40:55|456date is : 2018-05-05 21:40:55|465date is : 2018-05-05 21:40:55|472date is : 2018-05-05 21:40:55|480date is : 2018-05-05 21:40:55|488date is : 2018-05-05 21:40:55|492date is : 2018-05-05 21:40:55|502date is : 2018-05-05 21:40:55|519date is : 2018-05-05 21:40:55|535date is : 2018-05-05 21:40:55|541Process 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();}

转载于:https://www.cnblogs.com/rickiyang/p/11074189.html

你可能感兴趣的文章
(PAT 1019) General Palindromic Number (进制转换)
查看>>
(PAT 1073) Scientific Notation (字符串模拟题)
查看>>
(PAT 1080) Graduate Admission (排序)
查看>>
Play on Words UVA - 10129 (欧拉路径)
查看>>
mininet+floodlight搭建sdn环境并创建简答topo
查看>>
【linux】nohup和&的作用
查看>>
Set、WeakSet、Map以及WeakMap结构基本知识点
查看>>
【NLP学习笔记】(一)Gensim基本使用方法
查看>>
【NLP学习笔记】(二)gensim使用之Topics and Transformations
查看>>
【深度学习】LSTM的架构及公式
查看>>
【python】re模块常用方法
查看>>
剑指offer 19.二叉树的镜像
查看>>
剑指offer 20.顺时针打印矩阵
查看>>
剑指offer 21.包含min函数的栈
查看>>
剑指offer 23.从上往下打印二叉树
查看>>
剑指offer 25.二叉树中和为某一值的路径
查看>>
剑指offer 60. 不用加减乘除做加法
查看>>
Leetcode C++《热题 Hot 100-14》283.移动零
查看>>
Leetcode C++《热题 Hot 100-15》437.路径总和III
查看>>
Leetcode C++《热题 Hot 100-17》461.汉明距离
查看>>