Curator是Netflix公司开源的一个ZooKeeper client library,用于简化ZooKeeper客户端编程。它包含如下模块:

Framework:Framework是ZooKeeper API的High-Level的封装,它让访问ZooKeeper更加简单。它基于ZooKeeper添加了一些新的特性,同时屏蔽了访问ZooKeeper集群在管理连接和重试操作方面的复杂度。

Recipes:在Framework的基础上,实现了一些通用的功能,称之为“菜单”。

Utilities:访问ZooKeeper时候的一些公用方法。

Client:一个Low-Level的ZooKeeper客户端,并有一些公用方法。

Errors:Curator的异常处理,包括连接问题,异常恢复等等。

Extensions:

连接ZooKeeper

  1. RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
  2. CuratorFramework _client = CuratorFrameworkFactory.newClient("10.23.22.237:2181", retryPolicy);
  3. _client.start();
  1.  

Curator通过CuratorFrameworkFactory来创建客户端。new出来的客户端可以保存并且重用。在使用之前需要start一下,绝大部分Curator的操作都必须先start。

在new函数中需要传入RetryPolicy接口,重连的策略。当和ZooKeeper发生连接异常或者操作异常的时候,就会使用重连策略。ExponentialBackoffRetry是其中一种重连策略。Curator支持很多种重连策略:RetryNTimes(重连N次策略)、RetryForever(永远重试策略)、ExponentialBackoffRetry(基于backoff的重连策略)、BoundedExponentialBackoffRetry(有边界的基于backoff的重连策略,即,设定最大sleep时间)等等。

下面是官方例子中,ExponentialBackoffRetry的代码片段。

  1.  
  1. long sleepMs = baseSleepTimeMs * Math.max(1, random.nextInt(1 << (retryCount + 1)));
  2. if ( sleepMs > maxSleepMs )
  3. {
  4. log.warn(String.format("Sleep extension too large (%d). Pinning to %d", sleepMs, maxSleepMs));
  5. sleepMs = maxSleepMs;
  6. }
  7. return sleepMs;

可以看出ExponentialBackoffRetry 重连的时间间隔一般是随着重试的次数递增的,如果时间间隔计算出来大于默认的最大sleep时间的话,则去最大sleep时间。ExponentialBackoffRetry 除了时间的限制以外,还有最大重连次数的限制。而BoundedExponentialBackoffRetry策略只是让用户设置最大sleep时间而已。默认的最大时间是Integer.MAX_VALUE毫秒。

ZooKeeper节点操作

ZooKeeper 节点优点像文件系统的文件夹,每个节点都可以包含数据。但是ZooKeeper的节点是有生命周期的,这取决于节点的类型。在 ZooKeeper 中,节点类型可以分为持久节点(PERSISTENT )、临时节点(EPHEMERAL),以及时序节点(SEQUENTIAL ),具体在节点创建过程中,一般是组合使用,可以生成以下 4 种节点类型。不同的组合可以应用到不同的业务场景中。

1. 持久化节点

持久化节点创建后,就一直存在,除非有删除操作主动来删除这个节点,持久化节点不会因为创建该节点的客户端会话失效而消失。如果重复创建,客户端会抛出NodeExistsException异常。

  1. byte[] data = { 1, 2, 3 };
  2. _client.create().withMode(CreateMode.PERSISTENT).forPath("/zktest/p1", data);

2. 临时节点

创建临时节点后,如果客户端会话失效,那么这个节点会自动被ZooKeeper删除。这里是客户端失效,并不是客户端断开连接。因为ZooKeeper服务端和客户端是用心跳维持状态,会话留一点时间,这个时间是在创建连接的时候可以设置sessionTimeoutMs参数:

  1. CuratorFrameworkFactory.newClient(connectString, sessionTimeoutMs, connectionTimeoutMs, retryPolicy);

创建临时节点的代码如下:

  1. _client.create().withMode(CreateMode.EPHEMERAL).forPath("/zktest/e1", data);

3. 持久化时序节点

  1. _client.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath("/zktest/ps1", data);

上述代码执行两次,你会发现客户端不会报NodeExistsException异常,ZooKeeper会为你创建2个节点,ZooKeeper在每个父节点会为他的第一级子节点维护一份时序,会记录每个子节点创建的先后顺序。在创建子节点的时候,可以设置这个属性,那么在创建节点过程中,ZooKeeper会自动为给定节点名加上一个数字后缀,作为新的节点名。

4. 临时时序节点

持久化时序节点不同的就是节点会在会话失效的时候回消失。

  1. _client.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/zktest/es1", data);

5. 设置和获取节点数据

  1. //设置节点数据
  2. _client.setData().forPath("/zktest/ps1", data);
  3. //获取节点数据
  4. byte[] data2 = _client.getData().forPath("/zktest/ps1");

分布式锁

使用数据库、Redis、文件系统都可以实现分布式锁,同样ZooKeeper也可以用来实现分布式锁。Curator提供了InterProcessMutex类来帮助我们实现分布式锁,其内部就是使用的EPHEMERAL_SEQUENTIAL类型节点。

  1. public void test() throws Exception {
  2. RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 3);
  3.  
  4. _client = CuratorFrameworkFactory.newClient("10.23.22.237:2181", retryPolicy);
  5. _client.start();
  6.  
  7. ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
  8.  
  9. for (int i = 0; i < 5; i++) {
  10. fixedThreadPool.submit(new Runnable() {
  11.  
  12. @Override
  13. public void run() {
  14.  
  15. while (true) {
  16. try {
  17. dowork();
  18. } catch (Exception e) {
  19. // TODO Auto-generated catch block
  20. e.printStackTrace();
  21. }
  22. }
  23. }
  24. });
  25. }
  26. }
  27.  
  28. private void dowork() throws Exception {
  29.  
  30. InterProcessMutex ipm = new InterProcessMutex(_client, "/zktest/distributed_lock");
  31.  
  32. try {
  33. ipm.acquire();
  34.  
  35. _logger.info("Thread ID:" + Thread.currentThread().getId() + " acquire the lock");
  36.  
  37. Thread.sleep(1000);
  38.  
  39. _logger.info("Thread ID:" + Thread.currentThread().getId() + " release the lock");
  40. } catch (Exception e) {
  41.  
  42. } finally {
  43. ipm.release();
  44. }
  45. }

执行结果如下图:

acquire()方法,会在给定的路径下面创建临时时序节点的时序节点。然后它会和父节点下面的其他节点比较时序。如果客户端创建的临时时序节点的数字后缀最小的话,则获得该锁,函数成功返回。如果没有获得到,即,创建的临时节点数字后缀不是最小的,则启动一个watch监听上一个(排在前面一个的节点)。主线程使用object.wait()进行等待,等待watch触发的线程notifyAll(),一旦上一个节点有事件产生马上再次出发时序最小节点的判断。

release()方法就是释放锁,内部实现就是删除创建的EPHEMERAL_SEQUENTIAL节点。

Leader选举

选举可以用来实现Master-Slave模式,也可以用来实现主备切换等功能。Curator提供两种方式实现选举:LeaderSelector 和 LeaderLatch。两种方法都可以使用,LeaderLatch语法较为简单一点,LeaderSelector控制度更高一些。

使用LeaderSelector:

  1. public void test() {
  2. RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 3);
  3.  
  4. _client = CuratorFrameworkFactory.newClient("10.23.22.237:2181", retryPolicy);
  5. _client.start();
  6.  
  7. dowork();
  8.  
  9. }
  10.  
  11. private void dowork() {
  12.  
  13. LeaderSelectorListener listener = new
  14.  
  15. LeaderSelectorListenerAdapter() {
  16. public void takeLeadership(CuratorFramework client) throws Exception {
  17. logger.info("Take the lead.");
  18.  
  19. Thread.sleep(10000);
  20.  
  21. logger.info("Relinquish the lead.");
  22. }
  23.  
  24. };
  25.  
  26. LeaderSelector selector = new LeaderSelector(_client, "/zktest/leader", listener);
  27. selector.autoRequeue();
  28. selector.start();
  29. }

LeaderSelector的内部使用分布式锁InterProcessMutex实现, 并且在LeaderSelector中添加一个Listener,当获取到锁的时候执行回调函数takeLeadership。函数执行完成之后就调用InterProcessMutex.release()释放锁,也就是放弃Leader的角色。

使用LeaderLatch:

  1. public void test() {
  2. RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 3);
  3.  
  4. _client = CuratorFrameworkFactory.newClient("10.23.22.237:2181", retryPolicy);
  5. _client.start();
  6.  
  7. dowork();
  8.  
  9. }
  10.  
  11. private void dowork() {
  12. LeaderLatch leader = new LeaderLatch(_client, "/zktest/leader");
  13. leader.addListener(new LeaderLatchListener() {
  14.  
  15. @Override
  16. public void isLeader() {
  17. // TODO Auto-generated method stub
  18. logger.info("Take the lead.");
  19.  
  20. try {
  21. Thread.sleep(10000);
  22. } catch (InterruptedException e) {
  23. // TODO Auto-generated catch block
  24. e.printStackTrace();
  25. }
  26.  
  27. logger.info("Relinquish the lead.");
  28. }
  29.  
  30. @Override
  31. public void notLeader() {
  32. // TODO Auto-generated method stub
  33. logger.info("I am not Leader");
  34. }
  35. });
  36.  
  37. try {
  38. leader.start();
  39. } catch (Exception e) {
  40. // TODO Auto-generated catch block
  41. e.printStackTrace();
  42. }
  43. }

同样是实现Leader选举的LeaderLatch并没有通过InterProcessMutex实现,它使用了原生的创建EPHEMERAL_SEQUENTIAL节点的功能再次实现了一遍。同样的在isLeader方法中需要实现Leader的业务需求,但是一旦isLeader方法返回,就相当于Leader角色放弃了,重新进入选举过程。

使用Curator操作ZooKeeper的更多相关文章

  1. Java curator操作zookeeper获取kafka

    Java curator操作zookeeper获取kafka Curator是Netflix公司开源的一个Zookeeper客户端,与Zookeeper提供的原生客户端相比,Curator的抽象层次更 ...

  2. 通过Curator操作Zookeeper的简单例子代码

    Curator主要解决了三类问题: 一个是ZooKeeper client与ZooKeeper server之间的连接处理; 一个是提供了一套Fluent风格的操作API; 一个是ZooKeeper各 ...

  3. curator操作zookeeper

    使用zookeeper原生API实现一些复杂的东西比较麻烦.所以,出现了两款比较好的开源客户端,对zookeeper的原生API进行了包装:zkClient和curator.后者是Netflix出版的 ...

  4. 使用curator框架简单操作zookeeper 学习笔记

    Curator 操作是zookeeper的优秀api(相对于原生api),满足大部分需求.而且是Fluent流式api风格. 参考文献:https://www.jianshu.com/p/70151f ...

  5. 15. 使用Apache Curator管理ZooKeeper

    Apache ZooKeeper是为了帮助解决复杂问题的软件工具,它可以帮助用户从复杂的实现中解救出来. 然而,ZooKeeper只暴露了原语,这取决于用户如何使用这些原语来解决应用程序中的协调问题. ...

  6. Java操作zookeeper

    Java操作zookeeper总共有三种方式: 1.原生的Java API 2.zkclient 3.curator 第一种实现代码: pom.xml <dependency> <g ...

  7. 15. 使用Apache Curator装饰ZooKeeper

    Apache ZooKeeper是为了帮助解决复杂问题的软件工具,它可以帮助用户从复杂的实现中解救出来. 然而,ZooKeeper只暴露了原语,这取决于用户如何使用这些原语来解决应用程序中的协调问题. ...

  8. storm操作zookeeper源码分析-cluster.clj

    storm操作zookeeper的主要函数都定义在命名空间backtype.storm.cluster中(即cluster.clj文件中).backtype.storm.cluster定义了两个重要p ...

  9. 使用Apache Curator管理ZooKeeper(转)

    Apache ZooKeeper是为了帮助解决复杂问题的软件工具,它可以帮助用户从复杂的实现中解救出来. 然而,ZooKeeper只暴露了原语,这取决于用户如何使用这些原语来解决应用程序中的协调问题. ...

随机推荐

  1. iOS开发-观察者模式

    观察者模式也被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己.观察者模式中 ...

  2. IDEA下使用Maven的test命令乱码

    IDEA下使用Maven的test命令乱码的时候,加上 -Dfile.encoding=GBK 就可以解决啦   如下图所示:   或者在Maven的pom.xml文件中增加: <propert ...

  3. APICloud和海马玩模拟器结合调试手机页面

    https://blog.csdn.net/pleasecallme_522/article/details/54577904

  4. 鱼缸的启示:Scale-out和Scale-up架构

    提到Scale-out和Scale-up,初看到可能会有点晕.其实我认为Scale-out和Scale-up的概念可以用一个简单的例子来解释. 不知您有没有养过鱼?当你只有六七条鱼的时候,一个小型鱼缸 ...

  5. MySQL的reset slave与reset slave all

    reset slave是各版本Mysql都有的功能,可以让slave忘记自己在master binary log中的复制位置. reset slave命令主要完成以下工作内容: -删除master.i ...

  6. String的split方法支持正则表达式

    String的split方法支持正则表达式: 1. 正则表达式\s表示匹配任何空白字符 2. +表示匹配一次或多次

  7. 每帧创建一个item

    -- 加载列表测试 function UIBagController:onLoadTest() self.goodsprop = DB.getTable("goodsprop"); ...

  8. Maven使用详解,非常详细

    本文转:http://blog.csdn.net/u010425776/article/details/52027706 什么是Maven? 如今我们构建一个项目需要用到很多第三方的类库,如写一个使用 ...

  9. Android调用相机拍摄照片并显示到 ImageView控件中

    在前面的一篇文章中曾介绍过简单的开启相机照相功能,详见 Android简单调用相机Camera功能,实现打开照相功能 ,这一次就会将前面拍摄的照片显示到ImageView中,形成一个完整的效果 看实例 ...

  10. MongoDB随笔3:使用索引

    创建索引的语句很简单. 1.单键索引的创建:db.test.ensureIndex({name:1},{name:'index_name'}) 2.复合索引的创建:db.test.ensureInde ...