热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

zookeeper的客户端操作以及集群介绍

上一篇文章介绍了zookeeper(以下简称zk)的基本概念以及用法,通过zk自带的客户

上一篇文章介绍了zookeeper(以下简称zk)的基本概念以及用法,通过zk自带的客户端进行了简单的操作,但平时很少直接使用zk自带的客户端,因为实在是不直观,今天介绍下实际开发时候是怎么使用zk的,然后再说下zk的集群。
  • 一、ZooInspector

ZooInspector是一个图形化客户端,通过它可以很直观的看到zk里的数据。
下载链接:
https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip
用法也很简单,解压后进入到build目录打开cmd,然后运行:
java -jar zookeeper-dev-ZooInspector.jar

创建一个节点试试,点击Add Node:

新增按钮后面那个垃圾桶状的就是删除,同理,鼠标定位的哪个节点就是删除的哪个节点,删除就不演示了,很简单。
  • 二、通过java客户端操作zk

  • 2.1、基于zk的原生客户端进行操作

添加依赖:


    org.apache.zookeeper
    zookeeper
    3.5.5

    java代码:

      package com.ayo.zk;


      import org.apache.zookeeper.KeeperException;
      import org.apache.zookeeper.WatchedEvent;
      import org.apache.zookeeper.Watcher;
      import org.apache.zookeeper.ZooKeeper;
      import org.junit.Before;
      import org.junit.Test;


      import java.io.IOException;
      import java.util.List;


      public class ZkTest {


      ZooKeeper zooKeeper = null;


      **
      * 初始化方法,构造zk客户端
      */
      @Before
      public void init() throws IOException {
      zk的地址,多个用,隔开
      String cOnnectString= "192.168.209.129:2181";
      **
      * 会话超时时间,单位毫秒
      */
      int sessiOnTimeout= 60000;
      全局监听,缺点,如果多个节点同时发生变化,就不太好处理了
      zooKeeper = new ZooKeeper(connectString, sessionTimeout, watchedEvent -> System.out.println("全局监听: " + watchedEvent.getPath()));
      }


      **
      * 运行前看下linux中的防火墙是否开启,开启的话需要关闭,或者在防火墙中打开2181端口也可以
      *
      * 测试不添加监听
      * @throws KeeperException
      * @throws InterruptedException
      */
      @Test
      public void testGet() throws KeeperException, InterruptedException {
      **
      * watch:是否监听,true:是,false:否
      * 注意,从zk中取出来的数据都是字节数组,需要自己转换为想要的格式
      */
      byte[] data = zooKeeper.getData("/ayo", false, null);
      String s = new String(data);
      System.out.println(s);
      }


      **
      * 测试添加监听
      * @throws KeeperException
      * @throws InterruptedException
      */
      @Test
      public void testGet2() throws KeeperException, InterruptedException {
      **
      * watch:是否监听,true:是,false:否,这里触发的是全局监听
      * 注意,从zk中取出来的数据都是字节数组,需要自己转换为想要的格式
      */
      byte[] data = zooKeeper.getData("/ayo", true, null);
      String s = new String(data);
      System.out.println(s);
      休眠一分钟,为了观察监听器打印
      Thread.sleep(60000);
      }


      **
      * 测试添加自定义的监听
      * @throws KeeperException
      * @throws InterruptedException
      */
      @Test
      public void testGet3() throws KeeperException, InterruptedException {
      **
      * watch:直接new Watcher()
      */
      byte[] data = zooKeeper.getData("/ayo", new Watcher() {
      public void process(WatchedEvent watchedEvent) {
      try {
      因为监听一次就失效,所以需要重新添加监听,类似递归
      zooKeeper.getData(watchedEvent.getPath(), this, null);
      } catch (KeeperException e) {
      e.printStackTrace();
      } catch (InterruptedException e) {
      e.printStackTrace();
      }
      System.out.println("自定义的监听: " + watchedEvent.getPath());
      }
      }, null);
      String s = new String(data);
      System.out.println(s);
      休眠一分钟,为了观察监听器打印
      Thread.sleep(60000);
      }


      **
      * 获取子节点,无监听
      * @throws KeeperException
      * @throws InterruptedException
      */
      @Test
      public void testGet4() throws KeeperException, InterruptedException {
      List children = zooKeeper.getChildren("/ayo", false);
      children.stream().forEach(System.out::println);
      }


      **
      * 获取子节点, 同时监听子节点的变化
      * @throws KeeperException
      * @throws InterruptedException
      */
      @Test
      public void testGet5() throws KeeperException, InterruptedException {
      List children = zooKeeper.getChildren("/ayo", watchedEvent -> System.out.println(watchedEvent.getPath()));
      children.stream().forEach(System.out::println);
      }
      }

      以上代码我就不再演示了,有兴趣的,可以自己跑跑,实际开发中基本上也用不着,因为zk原生的API有很多问题:
      watcher机制是一次性的,每次触发过后需要重新添加监听
      session超时后不会自动重连,需要手动
      异常信息繁琐,开发人员不知道如何处理
      如果在创建节点时发生异常,需要自己再去检查节点是否创建成功
      不能递归创建和删除节点
      等等。
      • 2.2、Apache curator

      实际项目里通常使用的是第三方的开源客户端zkclient和curator,那这俩又有啥区别呢?个人认为curator要更优雅,通俗的说就是curator适合被开发人员使用,而zkclient适合被开源框架使用,比如dubbo和kafka。今天就以curator为例来操作下zk。
      java代码:
      • 2.2.1、初始化客户端

      2.2.2、创建节点并写入数据

      • 2.2.3、监听本节点的数据变化

      用ZooInspector修改数据即可

      • 2.2.4、监听子节点的数据变化

      用ZooInspector修改数据即可,删除子节点自己操作下,会打印子节点里的内容。

      • 2.2.5、删除节点

      可以看到,已经/zhubajie这个节点已经没有了。
      • 2.2.6、附录完整代码


        org.apache.curator
        curator-x-discovery
        2.5.0

          package com.ayo.zk;


          import org.apache.curator.RetryPolicy;
          import org.apache.curator.framework.CuratorFramework;
          import org.apache.curator.framework.CuratorFrameworkFactory;
          import org.apache.curator.framework.recipes.cache.*;
          import org.apache.curator.retry.ExponentialBackoffRetry;
          import org.junit.Before;
          import org.junit.Test;


          /**
          * 测试Zookeeper CuratorFramework 框架
          */
          public class CuratorFrameworkTest {


          CuratorFramework curator = null;


          /**
          * 初始化客户端
          *
          * retryPolicy参数是指在连接ZK服务过程中重新连接测策略.
          * 在它的实现类ExponentialBackoffRetry(int baseSleepTimeMs, int maxRetries)中,
          * baseSleepTimeMs参数代表两次连接的等待时间,maxRetries参数表示最大的尝试连接次数
          */
          @Before
          public void init(){
          RetryPolicy policy = new ExponentialBackoffRetry(1000, 10);
          /**
          * CuratorFramework示例创建完成,代表ZooKeeper已经连接成功
          */
          curator = CuratorFrameworkFactory.builder().connectString("192.168.209.129:2181")
          .sessionTimeoutMs(2000).retryPolicy(policy).build();
          /**
          * 调用start()方法打开连接,在使用完毕后调用close()方法关闭连接
          */
          curator.start();
          }


          /**
          * 创建节点并给节点写入数据
          */
          @Test
          public void createNode() throws Exception {
          curator.create().forPath("/zhubajie");
          curator.setData().forPath("/zhubajie", "八戒老帅了".getBytes("GBK"));
          curator.create().forPath("/zhubajie/son");
          curator.setData().forPath("/zhubajie/son", "所以它儿子也很帅啊".getBytes("GBK"));
          }


          /**
          * 删除节点
          */
          @Test
          public void deleteNode() throws Exception {
          curator.delete().forPath("/zhubajie");
          }




          /**
          * 监听本节点的数据变化
          */
          @Test
          public void testWatch() throws Exception {
          final NodeCache nodeCache = new NodeCache(curator, "/zhubajie", false);
          nodeCache.getListenable().addListener(new NodeCacheListener() {
          public void nodeChanged() throws Exception {
          /**
          * 在这个地方实现你自己的逻辑,这里以打印节点信息为例
          */
          System.out.println("本节点的数据发生变化,路径为"
          + nodeCache.getCurrentData().getPath()
          + ",数据变为" + new String(nodeCache.getCurrentData().getData(), "GBK"));
          }
          });


          nodeCache.start();
          /**
          * 让程序休眠,观察打印情况
          */
          Thread.sleep(60000);


          }


          /**
          * 监听子节点的增加或者删除或者数据变化
          */
          @Test
          public void testWatchChild() throws Exception {
          PathChildrenCache pathChildrenCache = new PathChildrenCache(curator, "/zhubajie", true);
          pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
          public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
          /**
          * 在这个地方实现你自己的逻辑,这里以打印节点信息为例
          */
          System.out.println("子节点的数据发生变化,路径为"
          + pathChildrenCacheEvent.getData().getPath()
          + ",数据变为" + new String(pathChildrenCacheEvent.getData().getData(), "GBK"));
          }
          });
          pathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
          /**
          * 让程序休眠,观察打印情况
          */
          Thread.sleep(60000);
          }
          }

          • 三、zk集群

          • 3.1、在另外两台机器上也安装下zk,步骤看上一篇文章即可,传送门初识zookeeper

          • 3.2、配置文件修改

          执行:
          cd /usr/local/apache-zookeeper-3.5.5-bin/(三台机器一样的操作)
          mkdir data(三台机器一样的操作)
          echo "0" > data/myid(另外两台机器上分别执行
          echo "1" > data/myid
          echo "2" > data/myid,但要注意和server.后面的数字对应

          • 3.3、分别启动三个zk并查看状态

          在三台机器上分别执行:
          cd /usr/local/apache-zookeeper-3.5.5-bin/bin/
          ./zkServer start
          ./zkServer.sh status

          把leader干掉:

          可以看到马上选举出了新的leader,达成高可用。
          • 3.4、查看数据同步情况

          分别连接上三个zk的客户端
          执行:
          cd /usr/local/apache-zookeeper-3.5.5-bin/bin/
          ./zkCli.sh
          在一个zk中创建一个节点,查看另外两个zk中是否也有即可:

          • 3.5、zk集群中的角色

          角色

          描述

          leader

          主节点,又名领导者。用于写入数据,通过选举产生,如果宕机将会选举新的主节点。

          follower

          子节点,又名追随者。用于实现数据的读取。同时他也是主节点的备选节点,并用拥有投票权。

          observer

          次级子节点,又名观察者。用于读取数据,与fllower区别在于没有投票权,不能选为主节点。并且在计算集群可用状态时不会将observer计算入内。

          注:想要成为observer只需要在集群配置中加上observer后缀即可,如:server.2=192.168.209.130:2889:3889:observer

          • 3.6、zk的选举时机和选举流程

          选举时机
          服务节点初始化启动(当节点初始起动时会在集群中寻找Leader节点,如果找到则与Leader建立连接,其自身状态变化follower或observer。如果没有找到Leader,当前节点状态将变化LOOKING,进入选举流程)
          半数以上的节点无法和Leader建立连接(在集群运行其间如果有follower或observer节点宕机只要不超过半数并不会影响整个集群服务的正常运行。但如果leader宕机,将暂停对外服务,所有follower将进入LOOKING 状态,进入选举流程)
          选举流程
          第一轮投票投给自己
          第二轮投票给myid比自己大的相邻节点
          如果得票超过半数,选举结束

          好了,zk集群也差不多就这些了,还有的就是zk的数据同步机制,包括它是怎么保证一致性的,这些就比较难了,后面专门开文章说。




          推荐阅读
          • Spring特性实现接口多类的动态调用详解
            本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
          • Java太阳系小游戏分析和源码详解
            本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
          • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
          • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
          • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
          • Android系统移植与调试之如何修改Android设备状态条上音量加减键在横竖屏切换的时候的显示于隐藏
            本文介绍了如何修改Android设备状态条上音量加减键在横竖屏切换时的显示与隐藏。通过修改系统文件system_bar.xml实现了该功能,并分享了解决思路和经验。 ... [详细]
          • [大整数乘法] java代码实现
            本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
          • 标题: ... [详细]
          • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
          • Android源码深入理解JNI技术的概述和应用
            本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
          • Java学习笔记之面向对象编程(OOP)
            本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
          • IjustinheritedsomewebpageswhichusesMooTools.IneverusedMooTools.NowIneedtoaddsomef ... [详细]
          • JDK源码学习之HashTable(附带面试题)的学习笔记
            本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
          • (三)多表代码生成的实现方法
            本文介绍了一种实现多表代码生成的方法,使用了java代码和org.jeecg框架中的相关类和接口。通过设置主表配置,可以生成父子表的数据模型。 ... [详细]
          • Activiti7流程定义开发笔记
            本文介绍了Activiti7流程定义的开发笔记,包括流程定义的概念、使用activiti-explorer和activiti-eclipse-designer进行建模的方式,以及生成流程图的方法。还介绍了流程定义部署的概念和步骤,包括将bpmn和png文件添加部署到activiti数据库中的方法,以及使用ZIP包进行部署的方式。同时还提到了activiti.cfg.xml文件的作用。 ... [详细]
          author-avatar
          手机用户2502937527
          这个家伙很懒,什么也没留下!
          PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
          Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有