一起进阶学习JAVA:Zookeeper


Zookeeper是一个开源的分布式协调服务,其设计目标是将复杂且容易出错的分布式一致性服务封装成一个高效可靠的原语集,并且以一些简单的接口提供给用户使用。Zookeeper是一个典型的分布式数据一致性解决方案,分布式应用程序可以基于它实现注入数据订阅/发布于、负载均衡、命名服务、集群管理、分布式锁和分布式队列等服务。

Zookeeper基本概念

集群角色

通常在分布式系统中,构成一个集群的每一台机器都有自己的角色,最典型的就是Master/Slave的主备模式。能够处理写操作的机器成为Master机器,把所有通过异步复制方式获取最新数据,并提供服务的机器为Slave机器。

在Zookeeper中,它没有引用Master/Slave概念,而是引入Leader、Follower、Observer三种角色。Zookeeper集群中的所有机器通过Leader选举来选定一台呗成为Leader的机器,Leader服务器为客户端提供读和写的服务,除了Leader机器外的其他机器,包括Follower和Observer都能提供读服务,但是Observer机器不参与Leader选举过程,不参与写操作的过半写成功策略,因此Observer可以在不影响写性能的情况下提升集群的性能。

会话(Session)

Session指的是客户端会话,一个客户端连接是指客户端和服务端之间的一个TCP长连接,Zookeeper对外的服务端口默认为2181,客户端启动的时候,首先会与服务器建立一个TCP长连接,从第一次连接建立开始,客户端会话的生命周期也就开始了,通过这个连接,客户端能够使用心跳检测机制与服务器保持有效的会话,也能够向Zookeeper服务器发送请求并接受响应,同时还能通过该连接接受来自服务器的Watch事件通知。

数据节点(ZNode)

通常‘节点’指的是组成集群的每一台机器,然而在Zookeeper中,“节点”分为两类,第一类同样指的是组成集群的机器,我们称之为机器节点;第二类则指的是数据模型中数据单元,我们称之为数据节点–ZNode。在Zookeeper中会将所有的数据存储在内存中,数据模型是一棵树也成为ZNode Tree,又斜杠(/)进行分割的路径,就是一个ZNode,例如/znode/path1。每个ZNode上都会保存自己的数据内容以及一系列看ii的属性信息。

版本

对于每个ZNode,Zookeeper都会为其为维护一个叫做Stat的数据结构,Stat记录了这个ZNode的三个数据版本,分别是version(当前ZNode的版本)、cversion(当前ZNode子节点的版本)、aversion(当前ZNode的ACL版本)。

Watcher(事件监听器)

Watcher事件监听器是在Zookeeper中很重要的特性,Zookeeper允许用户在指定节点上注册一些Watcher事件监听器,并且在特定时间触发的时候,Zookeeper服务端会将时间通知到感兴趣的客户端。

ACL

Zookeeper采用ACL(Access Control Lists)策略来进行权限控制,其中定义了五种权限

  • CREATE
    创建子节点的权限
  • READ
    获取节点数据和子节点列表的权限
  • WRITE
    更新节点数据的权限
  • DELETE
    删除子节点的权限
  • ADMIN
    设置节点的ACL权限的权限

Zookeeper环境搭建

Zookeeper搭建均以Linux环境为例

单机环境搭建

  1. 下载安装包
    首先下载稳定版本的zookeeper http://zookeeper.apache.org/releases.html,如下使用的版本为3.4.14》》zookeeper-3.4.14.tar.gz

  2. 上传
    将zookeeper安装压缩包zookeeper-3.4.14.tar.gz上传至服务器系统

  3. 解压缩安装包至/usr/local

    cd /usr/local
    tar -zxvf zookeeper-3.4.14.tar.gz
    
  4. 创建data文件夹

    cd zookeeper-3.4.14
    mkdir data
    
  5. 修改配置文件名称

    cd conf
    mv zoo_sample.cfg zoo.cfg
    
  6. 修改zoo.cfg中的data属性

    dataDir=/usr/local/zookeeper-3.4.14/data
    
  7. 启动服务

    ./zkServer.sh start
    

输出以下内容表示启动成功
在这里插入图片描述
关闭服务输入命令

./zkServer.sh stop

输出以下提是信息表示关闭成功
在这里插入图片描述
查看状态输入命令

./zkServer.sh status

集群模式搭建

Zookeeper不但可以在单机上运行单机模式,而且可以在单机模拟集群模式。以下为单机情况下模拟集群模式的搭建。我们将不同的实例运行在一台机器,用端口区分。

我们在一台机器上部署了3个server,也就是说单台机器上运行多个Zookeeper实例,这种情况下我们要保证每个不同的zookeeper实例的端口号是不能冲突的,除了clientPort不同之外,dataDir也不同。另外,还要再dataDir所对应的目录中创建myid文件再指定对应的zookeeper服务器实例

  • clientPort端口
    如果在1台机器上部署多个zookeeper实例,那么每个实例需要不同的clientPort
  • dataDir和dataLogDir
    如果在1台机器上部署多个zookeeper实例,dataDir和dataLogDir也需要区分
  • server.X和myid
    server.X这个数字就是对应data/myid中的数字。在3个server的myid文件中分别写入了1,2,3,那么每个server中的zoo.cfg都配置sever.1,server.2,server.3。在同一台机器上部署的情况下,后面连着的2个端接口都不能是一样的,否则会端口冲突
  1. 下载安装包
    首先下载稳定版本的zookeeper http://zookeeper.apache.org/releases.html,如下使用的版本为3.4.14》》zookeeper-3.4.14.tar.gz

  2. 上传
    将zookeeper安装压缩包zookeeper-3.4.14.tar.gz上传至服务器系统

  3. 解压缩安装包至/usr/local中创建的新目录zkcluster中

    cd /usr/local
    mkdir zkcluster
    tar -zxvf zookeeper-3.4.14.tar.gz -C /zkcluster
    
  4. 改变名称

    mv zookeeper-3.4.14 zookeeper01
    
  5. 复制多分模拟多个实例并改名

    cp -r zookeeper01/ zookeeper02
    cp -r zookeeper01/ zookeeper03
    
  6. 分别在这三个实例文件夹中创建data及logs目录

    cd /usr/local/zkcluster/zookeeper01
    mkdir data
    cd data
    mkdir logs
    
    cd /usr/local/zkcluster/zookeeper02
    mkdir data
    cd data
    mkdir logs
    
    cd /usr/local/zkcluster/zookeeper03
    mkdir data
    cd data
    mkdir logs
    
  7. 修改配置文件名称(三个实例需要都修改)

    cd /usr/local/zkcluster/zookeeper01/conf
    mv zoo_sample.cfg zoo.cfg
    
  8. 配置每一个Zookeeper的dataDir

    clientPort=2181
    dataDir=/usr/local/zkcluster/zookeeper01/data
    dataLogDir=/usr/local/zkcluster/zookeeper01/data/logs
    
    clientPort=2182
    dataDir=/usr/local/zkcluster/zookeeper02/data
    dataLogDir=/usr/local/zkcluster/zookeeper02/data/logs
    
    clientPort=2183
    dataDir=/usr/local/zkcluster/zookeeper03/data
    dataLogDir=/usr/local/zkcluster/zookeeper03/data/logs
    
  9. 配置集群

  • 在每个Zookeeper的data目录下创建一个myid文件,内容分别是1,2,3。这个文件就是记录每个服务器的ID

    touch myid
    
  • 在每个zookeeper的zoo.cfg配置客户端访问端口和集群服务器IP列表

    server.1=服务器IP:2881:3881
    server.2=服务器IP:2882:3882
    server.3=服务器IP:2883:3883
    #server.服务器ID=服务器IP地址:服务器之间通信端口:服务器之间头皮选举端口
    
  1. 启动集群
    依次启动三个zookeeper实例

Zookeeper基本使用

Zookeeper系统模型

Zookeeper数据模型ZNode

在Zookeeper中,数据信息被保存在一个个数据节点上,这些节点被称为ZNode。ZNode是zookeeper中的最小数据单位,在ZNode下面又可以再挂ZNode,一层层形成一个层次化命名空间的ZNode树,我们称之为ZNode Tree,它采取了类似文件系统的层级树状结构进行管理。
在这里插入图片描述

ZNode的类型

  1. 持久性节点(Persistent)
    该节点被创建后会一直存在再服务器,直到删除操作主动清除
  2. 临时性节点(Ephemeral)
    该节点的生命周期和客户端会话绑定在一起,客户端会话结束后节点就会被删除掉,临时性节点也不能创建子节点
  3. 顺序性节点(Sequential)
    顺序性节点又分为持久顺序节点和临时顺修节点。这两种节点的特性和其对应的持久性和临时性的特性一样。不同的额外特性表现在顺序上,顺序特性实质就是在创建节点的时候,会在节点后面加上一个数字后缀来表示其顺序。

事务ID

在Zookeeper中,事务是指能够改变Zookeeper服务器状态的操作,我们也称之为事务操作或更新操作,一般包括数据节点的创建与删除、数据节点内容更新等操作。对于每一个事务请求,Zookeeper都会为其分配一个全局唯一的事务ID,用ZXID来表示,通常是一个64位的数字。每一个ZXID对于一次更新操作。

ZNode的状态信息

在这里插入图片描述

整个ZNode节点内容包括两个部分:

  1. 节点数据内容:上图quota是数据内容
  2. 节点状态信息:除quota意外全是状态信息
属性全称含义
cZxidCreate ZXID节点被创建时的事务ID
ctimeCreate Time节点创建时间
mZxidModified ZXID节点最后一次被修改时的事务ID
mtimeModified Time节点最后一次被修改的时间
pZxid/节点的子节点列表最后一次被修改时的事务ID。只有子节点列表变更才会更新,子节点内容变更时不会更新
cversion/子节点版本号
dataVersion/内容版本号
aclVersion/ACL版本
ephemeralOwner/创建该临时节点时的花花sessionID,如果时持节性节点那么值为0
dataLength/数据长度
numChildren/直系子节点数

数据变更通知—Watcher

Zookeeper使用Watcher机制实现分布式数据的发布/订阅共嗯那个

Zookeeper允许客户端向服务端注册一个Watcher监听,当服务端的一些指定事件触发了这个Watcher,那么就会向指定客户端发送一个时间通知来实现分布式的通知功能。
在这里插入图片描述
Zookeeper的Watcher机制主要包括客户端线程、客户端WatcherManager、Zookeeper服务器三部分。

具体工作流程如下:

  1. 客户端在向Zookeeper服务器注册的同时,会将Watcher对象存储在客户端的WatcherManager当中
  2. 当Zookeeper服务器触发Watcher事件后,向客户端发送通知
  3. 客户端线程从WatcherManager中取出对应的Watcher对象来执行回调逻辑

ACL—保障数据的安全

Zookeeper作为一个分布式协调框架,其内部存储了分布式系统运行时状态的元数据,这些元数据会直接影响基于Zookeeper进行构造的分布式系统的运行状态,一次Zookeeper中提供了一套完善的ACL(Access Control List)权限控制机制来保障数据的安全。

权限模式:Scheme

权限模式用来确定权限验证过程中使用的检验策略

  1. IP
    IP模式就是通过IP地址粒度来进行权限控制,如ip:192.168.0.110表示针对192.168.0.110这个IP地址,同时也支持按网段方式进行配置,如ip:192.168.0.1/24表示针对192.168.0.*这个网段进行权限控制。
  2. Digest
    Digest是最常用的权限控制模式,它使用username:password形式的权限标识来进行权限配置,便于区分不同应用来进行权限空直,当我们通过username:password形式来妹纸之后,Zookeeper会先对其进行SHA-1加密和BASE64编码。
  3. World
    World是一种最开放的权限控制模式,这种权限控制方式几乎没有任何作用,数据节点的访问权限是对全部用户进行开放的,同时World模式也是一种特殊的Digest模式,即world:anyone
  4. Super
    超级用户模式,也是一种特殊的Digest模式,在Super模式下,超级用户可以对任意Zookeeper上的数据节点进行任何操作
授权对象:ID

授权对象是指权限赋予的用户或一个指定实体,例如IP地址或者机器等。

权限模式授权对象
IP通常是一个IP地址或地址段,如:192.168.0.110或192.168.0.1/24
Digest自定义,通常是username:BASE64(SHA-1(username:password)),例如:zm:sdfndsllndlksfn7c=
World只有一个ID:anyone
Super超级用户
权限

权限就是指哪些通过权限检查后被允许执行的操作,Zookeeper中又五大类权限

标识符含义描述
CCREATE数据节的创建权限,允许授权对象在该数据节点下创建子节点
DDELETE子节点的删除权限,允许授权对象删除该数据节点的子节点
RREAD数据节点的读取权限,允许授权对象访问该节点并读取其数据内容和子节点列表等
WWRITE数据节点的更新权限,允许授权对象访问该节点进行更新操作
AADMIN数据节点的管理权限,允许授权对象对该数据节点进行ACL相关的设置操作

Zookeeper命令行操作

  • 使用zkClient进入zookeeper客户端命令行
cd /usr/local/zookeeper-3.4.14/bin
./zkcli.sh 连接本地的zookeeper服务器
./zkCli.sh -server ip:port 连接指定服务器

连接成功后,系统会输出Zookeeper的相关环境及配置信息。输入help后,屏幕会输出可用的zookeeper命令
在这里插入图片描述

  • 创建节点
    使用create命令

    create [-s][-e] path data acl
    #其中,-s或-e分别指定节点特性,顺序或临时节点,若不指定则为持久节点;
    #acl用来进行权限控制
    
    create -s /zk-test 123
    #创建一个zk-test的顺序节点,节点内容为123
    
    create -e /zk-temp 123
    #创建一个zk-temp的临时节点,节点内容为123
    
    create /zk-permanent 123
    #创建一个zk-permanent的持节节点
    

退出使用quit命令

  • 读取节点
    与读取相关的命令有lsget命令
    ls命令可以列出Zookeeper指定节点下的所有子节点,但只能查看指定节点下的第一级的所有子节点

    ls path
    #其中,path标识的是指定数据节点的节点路径
    

    在这里插入图片描述

    get命令可以获取Zookeeper指定节点的数据内容和属性信息

    get path
    

    在这里插入图片描述

  • 更新节点

  • 使用set命令,可以更新指定节点的数据内容

    set path data [version]
    

    其中,data就是要更新的新内容,version标识数据版本,在zookeeper中,节点数据是有版本概念的,这个参数用于指定本次操作时基于ZNode的哪一个数据版本进行的

    set /zk-permanent 456
    #将/zk-permanent数据更新为456
    

    在这里插入图片描述

  • 删除节点
    使用delete命令可以删除Zookeeper上的指定节点

    delete path [version]
    

    其中version也是表示数据版本

    delete /zk-permanent
    #删除/zk-permanent节点
    

    若删除节点存在子节点,那么无法删除该节点,必须先删除子节点,再删除父节点

Zookeeper的api使用

Zookeeper作为一个分布式框架,主要用于解决分布一致性问题,它提供了多种编程语言API,以下为java客户端的API使用方式

Zookeeper API共包含5个包:

  1. org.apache.zookeeper
  2. org.apache.zookeeper.data
  3. org.apache.zookeeper.server
  4. org.apache.zookeeper.server.quorum
  5. org.apache.zookeeper.server.upgrade

其中org.apache.zookeeper,包含Zookeeper类,是我们最常用的类文件。如果使用Zookeeper服务,必须先创建一个Zookeeper实例,一旦客户端和Zookeeper服务端建立起来连接,Zookeeper系统将会给本次会话分配一个ID指,并且客户端会周期性的向服务端发送心跳来维持会话连接。只要连接有效,客户端就可以使用Zookeeper API进行处理了。

  1. 准备工作:导入依赖
	<dependency>
		 <groupId>org.apache.zookeeper</groupId>
		 <artifactId>zookeeper</artifactId>
		 <version>3.4.14</version>
 	</dependency>
  1. 建立会话
public class CreateSession implements Watcher {
	 //countDownLatch这个类使⼀个线程等待,主要不让main⽅法结束
	 private static CountDownLatch countDownLatch = new CountDownLatch(1);
	 
	 public static void main(String[] args) throws InterruptedException,
IOException {
		 /*
		 客户端可以通过创建⼀个zk实例来连接zk服务器
		 new Zookeeper(connectString,sesssionTimeOut,Wather)
		 connectString: 连接地址:IP:端⼝
		 sesssionTimeOut:会话超时时间:单位毫秒
		 Wather:监听器(当特定事件触发监听时,zk会通过watcher通知到客户端)
		 */
		 ZooKeeper zooKeeper = new ZooKeeper("127.0.0.1:2181", 5000, new
	CreateSession());
		 System.out.println(zooKeeper.getState());
		 countDownLatch.await();
		 //表示会话真正建⽴
		 System.out.println("=========Client Connected to zookeeper==========");
	 }
	// 当前类实现了Watcher接⼝,重写了process⽅法,该⽅法负责处理来⾃Zookeeper服务端的watcher通知,在收到服务端发送过来的SyncConnected事件之后,解除主程序在CountDownLatch上的等待阻塞,⾄此,会话创建完毕
	 public void process(WatchedEvent watchedEvent) {
	 	//当连接创建了,服务端发送给客户端SyncConnected事件
		 if(watchedEvent.getState() == Event.KeeperState.SyncConnected){
		 	countDownLatch.countDown();
		 }
	 }
}
  1. 创建节点
public class CreateNote implements Watcher {
	 //countDownLatch这个类使⼀个线程等待,主要不让main⽅法结束
	 private static CountDownLatch countDownLatch = new CountDownLatch(1);
	 private static ZooKeeper zooKeeper;
	 public static void main(String[] args) throws Exception {
		 zooKeeper = new ZooKeeper("127.0.0.1:2181", 5000, new CreateNote());
		 countDownLatch.await();
	 }
	 
	 public void process(WatchedEvent watchedEvent) {
		 //当连接创建了,服务端发送给客户端SyncConnected事件
		 if(watchedEvent.getState() == Event.KeeperState.SyncConnected){
		 	countDownLatch.countDown();
		 }
		 //调⽤创建节点⽅法
		 try {
		 	createNodeSync();
		 } catch (Exception e) {
		 	e.printStackTrace();
		 }
	 }
	 private void createNodeSync() throws Exception {
		 /**
		 * path :节点创建的路径
		 * data[] :节点创建要保存的数据,是个byte类型的
		 * acl :节点创建的权限信息(4种类型)
		 * ANYONE_ID_UNSAFE : 表示任何⼈
		 * AUTH_IDS :此ID仅可⽤于设置ACL。它将被客户机验证的ID替
		换。
		 * OPEN_ACL_UNSAFE :这是⼀个完全开放的ACL(常⽤)-->
		world:anyone
		 * CREATOR_ALL_ACL :此ACL授予创建者身份验证ID的所有权限
		 * createMode :创建节点的类型(4种类型)
		 * PERSISTENT:持久节点
		 * PERSISTENT_SEQUENTIAL:持久顺序节点
		 * EPHEMERAL:临时节点
		 * EPHEMERAL_SEQUENTIAL:临时顺序节点
		 String node = zookeeper.create(path,data,acl,createMode);
		 */
		 String node_PERSISTENT = zooKeeper.create("/lg_persistent", "持久节点内容".getBytes("utf-8"), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
		 String node_PERSISTENT_SEQUENTIAL = zooKeeper.create("/lg_persistent_sequential", "持久节点内容".getBytes("utf-8"),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
		 String node_EPERSISTENT = zooKeeper.create("/lg_ephemeral", "临时节点内容".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
		 System.out.println("创建的持久节点是:"+node_PERSISTENT);
		 System.out.println("创建的持久顺序节是:"+node_PERSISTENT_SEQUENTIAL);
		 System.out.println("创建的临时节点是:"+node_EPERSISTENT);
	 }
}
  • 获取节点数据
public class GetNoteData implements Watcher {
	 //countDownLatch这个类使⼀个线程等待,主要不让main⽅法结束
	 private static CountDownLatch countDownLatch = new CountDownLatch(1);
	 private static ZooKeeper zooKeeper;
	 
	 public static void main(String[] args) throws Exception {
	 zooKeeper = new ZooKeeper("127.0.0.1:2181", 10000, new GetNoteDate());
	 Thread.sleep(Integer.MAX_VALUE);
	
	 }
	 public void process(WatchedEvent watchedEvent) {
		 //⼦节点列表发⽣变化时,服务器会发出NodeChildrenChanged通知,但不会把变化情况告诉给客户端
		 // 需要客户端⾃⾏获取,且通知是⼀次性的,需反复注册监听
		 if(watchedEvent.getType() ==Event.EventType.NodeChildrenChanged){
			 //再次获取节点数据
			 try {
				 List<String> children =
				zooKeeper.getChildren(watchedEvent.getPath(), true);
			 	System.out.println(children);
			 } catch (KeeperException e) {
			 	e.printStackTrace();
			 } catch (InterruptedException e) {
			 	e.printStackTrace();
			 }
		 }
		 //当连接创建了,服务端发送给客户端SyncConnected事件
		 if(watchedEvent.getState() == Event.KeeperState.SyncConnected){
			 try {
				 //调⽤获取单个节点数据⽅法
				 getNoteDate();
				 getChildrens();
			 } catch (KeeperException e) {
			 	e.printStackTrace();
			 } catch (InterruptedException e) {
			 	e.printStackTrace();
			 }
		 }
	 }
	 
	 private static void getNoteData() throws Exception {
		 /**
		 * path : 获取数据的路径
		 * watch : 是否开启监听
		 * stat : 节点状态信息
		 * null: 表示获取最新版本的数据
		 * zk.getData(path, watch, stat);
		 */
		 byte[] data = zooKeeper.getData("/lg_persistent/lg-children", true,null);
		 System.out.println(new String(data,"utf-8"));
	 }
	 
	 private static void getChildrens() throws KeeperException,InterruptedException {
		 /*
		 path:路径
		 watch:是否要启动监听,当⼦节点列表发⽣变化,会触发监听
		 zooKeeper.getChildren(path, watch);
		 */
		 List<String> children = zooKeeper.getChildren("/lg_persistent", true);
	 	 System.out.println(children);
	 }
}
  • 修改节点
public class updateNote implements Watcher {
	
	 private static ZooKeeper zooKeeper;
	 
	 public static void main(String[] args) throws Exception {
		 zooKeeper = new ZooKeeper("127.0.0.1:2181", 5000, new updateNote());
		 Thread.sleep(Integer.MAX_VALUE);
	 }
	 public void process(WatchedEvent watchedEvent) {
		 //当连接创建了,服务端发送给客户端SyncConnected事件
		 try {
		 	updateNodeSync();
		 } catch (Exception e) {
		 	e.printStackTrace();
		 }
	 }
	 private void updateNodeSync() throws Exception {
	
		 /*
		 path:路径
		 data:要修改的内容 byte[]
		 version:为-1,表示对最新版本的数据进⾏修改
		 zooKeeper.setData(path, data,version);
		 */
		 byte[] data = zooKeeper.getData("/lg_persistent", false, null);
		 System.out.println("修改前的值:"+new String(data));
		 //修改 stat:状态信息对象 -1:最新版本
		 Stat stat = zooKeeper.setData("/lg_persistent", "客户端修改内容".getBytes(), -1);
		 byte[] data2 = zooKeeper.getData("/lg_persistent", false, null);
		 System.out.println("修改后的值:"+new String(data2));
	 }
}
  • 删除节点数据
public class DeleteNote implements Watcher {
	 private static ZooKeeper zooKeeper;
	 public static void main(String[] args) throws Exception {
		 zooKeeper = new ZooKeeper("10.211.55.4:2181", 5000, new DeleteNote());
		 Thread.sleep(Integer.MAX_VALUE);
	 }
	 public void process(WatchedEvent watchedEvent) {
		 //当连接创建了,服务端发送给客户端SyncConnected事件
		 try {
		 	deleteNodeSync();
		 } catch (Exception e) {
		 	e.printStackTrace();
		 }
	 }
	 private void deleteNodeSync() throws KeeperException,InterruptedException{
		 /*
		 zooKeeper.exists(path,watch) :判断节点是否存在
		 zookeeper.delete(path,version) : 删除节点
		 */
		 Stat exists = zooKeeper.exists("/lg_persistent/lg-children", false);
		 System.out.println(exists == null ? "该节点不存在":"该节点存在");
		 zooKeeper.delete("/lg_persistent/lg-children",-1);
		 Stat exists2 = zooKeeper.exists("/lg_persistent/lg-children", false);
		 System.out.println(exists2 == null ? "该节点不存在":"该节点存在");
	 }
}

Zookeeper开源客户端–zkClient

ZkClient是GitHub上的一个开源zookeeper客户端,再Zookeeper原生API接口上进行了包装,同时再内部还实现了Session超时重连、Watcher反复注册等功能

  • 添加依赖
	<dependency>
		 <groupId>com.101tec</groupId>
		 <artifactId>zkclient</artifactId>
		 <version>0.2</version>
	</dependency>
  • 创建会话
import java.io.IOException;
import org.I0Itec.zkclient.ZkClient;
public class CreateSession {
	 /*
	 创建⼀个zkClient实例来进⾏连接
	 注意:zkClient通过对zookeeperAPI内部包装,将这个异步的会话创建过程同步化了
	 */
	 public static void main(String[] args) {
		 ZkClient zkClient = new ZkClient("127.0.0.1:2181");
		 System.out.println("ZooKeeper session established.");
	 }
}
  • 创建节点
import org.I0Itec.zkclient.ZkClient;
public class Create_Node_Sample {
	 public static void main(String[] args) {
		 ZkClient zkClient = new ZkClient("127.0.0.1:2181");
		 System.out.println("ZooKeeper session established.");
		 //createParents的值设置为true,可以递归创建节点
		 zkClient.createPersistent("/lg-zkClient/lg-c1",true);
		 System.out.println("success create znode.");
	 }
}
  • 删除节点
    ZkClient提供了递归删除节点的接口,即帮助开发者先删除所有子节点(如果存在),再删除父节点
import org.I0Itec.zkclient.ZkClient;
public class Del_Data_Sample {
	 public static void main(String[] args) throws Exception {
		 String path = "/lg-zkClient/lg-c1";
		 ZkClient zkClient = new ZkClient("127.0.0.1:2181", 5000);
		 zkClient.deleteRecursive(path);
		 System.out.println("success delete znode.");
	 }
}
  • 获取子节点
import java.util.List;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
public class Get_Children_Sample {
	 public static void main(String[] args) throws Exception {
		 ZkClient zkClient = new ZkClient("127.0.0.1:2181", 5000);
		 List<String> children = zkClient.getChildren("/lg-zkClient");
		 System.out.println(children);
		
		 //注册监听事件
		 zkClient.subscribeChildChanges(path, new IZkChildListener() {
		 	public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
				 System.out.println(parentPath + " 's child changed,
				currentChilds:" + currentChilds);
			 }
		 });
		 zkClient.createPersistent("/lg-zkClient");
		 Thread.sleep(1000);
		 zkClient.createPersistent("/lg-zkClient/c1");
		 Thread.sleep(1000);
		 zkClient.delete("/lg-zkClient/c1");
		 Thread.sleep(1000);
		 zkClient.delete(path);
		 Thread.sleep(Integer.MAX_VALUE);
	 }
}
  • 获取数据(节点是否存在、更新、删除)
public class Get_Data_Sample {
	 public static void main(String[] args) throws InterruptedException {
		 String path = "/lg-zkClient-Ep";
		 ZkClient zkClient = new ZkClient("127.0.0.1:2181");
		 //判断节点是否存在
		 boolean exists = zkClient.exists(path);
		 if (!exists){
		 	zkClient.createEphemeral(path, "123");
		 }
		
		 //注册监听
		 zkClient.subscribeDataChanges(path, new IZkDataListener() {
			 public void handleDataChange(String path, Object data) throws Exception {
			 	System.out.println(path+"该节点内容被更新,更新后的内容"+data);
			 }
			 public void handleDataDeleted(String s) throws Exception {
			 	System.out.println(s+" 该节点被删除");
			 }
		 });
		 //获取节点内容
		 Object o = zkClient.readData(path);
		 System.out.println(o);
		 //更新
		 zkClient.writeData(path,"4567");
		 Thread.sleep(1000);
		 //删除
		 zkClient.delete(path);
		 Thread.sleep(1000);
	 }
}
  • 12
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Java中使用ZooKeeper监听节点变化需要使用ZooKeeper提供的Watcher接口。Watcher接口中定义了一个process()方法,当节点发生改变时,ZooKeeper会回调这个方法。 下面是一个简单的Java代码示例,用于监听ZooKeeper节点的变化: ```java import org.apache.zookeeper.*; import java.io.IOException; public class ZooKeeperWatcher implements Watcher { private ZooKeeper zooKeeper; public ZooKeeperWatcher(String connectString, int sessionTimeout) throws IOException, KeeperException, InterruptedException { zooKeeper = new ZooKeeper(connectString, sessionTimeout, this); // 创建一个节点,用于后面的演示 zooKeeper.create("/test", "hello".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } public void process(WatchedEvent event) { if (event.getType() == Event.EventType.NodeDataChanged) { try { byte[] data = zooKeeper.getData(event.getPath(), this, null); System.out.println("Node data changed: " + new String(data)); } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws IOException, KeeperException, InterruptedException { ZooKeeperWatcher watcher = new ZooKeeperWatcher("localhost:2181", 5000); Thread.sleep(Integer.MAX_VALUE); } } ``` 在这个代码中,我们定义了一个ZooKeeperWatcher类,其中实现了Watcher接口。在类的构造函数中,我们首先连接ZooKeeper,并创建了一个名为“/test”的节点。在process()方法中,我们判断事件类型是否为“NodeDataChanged”,如果是,我们就获取节点的最新数据,并输出到控制台。最后,我们在main()方法中启动了一个线程,以保持程序的运行。 当节点的数据发生变化时,程序将会输出一个消息,如下所示: ``` Node data changed: world ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值