Zookeeper客户端命令、JAVA API、监听原理、写数据原理以及案例

1. Zookeeper节点信息

指定服务端,启动客户端命令:

bin/zkCli.sh -server 服务端主机名:端口号

1)ls /  查看根节点下面的子节点

     ls -s /  查看根节点下面的子节点以及根节点详细信息

其中,cZxid是创建节点的事务id,每次修改Zookeeper的状态都会产生一个事务id;

ctime是节点被创建的毫秒数(从1970年开始),这里是zookeeper自带的默认节点,其ctime就是0;mZxid是节点最后被更新的事务id;

mtime是节点最后修改的毫秒数;pZxid是最后更新的子节点的事务id;ephemeralOwner如果是临时节点则表示拥有这个节点的s

ession id,如果不是临时节点则为0;

dataLength是该节点的数据长度;

numChildren是该节点的子节点数量。

2. Zookeeper节点类型

持久节点:客户端和服务端断开连接后,创建的节点不删除

短暂/临时节点:客户端和服务端断开连接后,创建的节点删除

上面两种节点还可以继续分为带序号和不带序号的,如果带序号,节点名称后面会接一个数值,顺序递增,由父节点维护。

创建永久节点:create path "val"

(注意,ls命令后面的路径不能以/结尾,这里跟Linux不一样)

查询节点的值:get -s path

创建带序号的永久节点:create -s path "val"

以相同的路径再次创建同名节点,带序号的节点会自动序号加1,不带序号的节点创建报错

以上是永久节点,退出客户端之后这些节点依然存在

创建临时节点:create -e path "val"

创建带序号的临时节点只需加上-s即可

因为已经有shuguo,weiguo,wuguo,所以新创建的带序号的临时节点的序号为3。

断开客户端之后,上面创建的临时节点wuguo会被删除。

修改节点的值:set path "newVal"

3. 监听原理

监听主要是通过getChildren和getData来实现,表面上是获取节点子节点或者节点数据的方法,但是第二个参数表示是否监听,一般为true(第二个参数也可以传一个自定义的监听器),所以实现了监听,当子节点发生增减或者节点数据发生变化时,就会通知客户端,触发process方法。getChildren和getData是Java API监听方式,稍后介绍,这里先介绍命令行监听。

命令行开启监听节点数据:get -w path

在另一个会话端修改sanguo的节点值,在本端会产生事件通知:

再次在另一个会话端修改sanguo的节点值,在本端不会产生事件通知,因为监听只生效一次,要想再次监听,需要再次注册,即执行get -w path

监控子节点变化:ls -w path

再另一个会话端创建一个子节点,在本端会产生一个事件通知

节点删除:delete path

删除节点及其下面的子节点:deleteall path

查看节点状态:stat path

4. Java API

添加pom依赖:

<dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.5.7</version>
</dependency>

建立Zookeeper连接:

Zookeeper zk = new Zookeeper(connectString, sessionTimeout, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {}        
})

其中connectString是主机地址,如果有多个主机,用逗号隔开,中间不能有空格。sessionTimeout是超时时间,单位是微秒。第三个参数是监听器,里面一般是根据事件类型以及事件路径来做相应的处理,也可在里面继续调用getChildren或者getData方法实现持续监听。

一旦连接上Zookeeper之后就会调用到process方法,里面一般会根据事件类型来对某个countDownLatch变量进行减1操作,在主线程中会等待这个变量为0,即等待Zookeeper连接上。

创建节点:

String node = zk.create(path, data, ZooDfs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

监控子节点的增删(注册监听):

List<String> children = zk.getChildren(path, true)for (String child : children) {System.out.println(child);
}

要想验证对子节点增删的监听,首先在java主线程中添加一个睡眠的函数,使其持续运行不至于很快结束,然后在process回调中添加相应的打印代码(比如继续getChildren,打印子节点信息),这样手动去添加节点,会执行到process函数中的打印信息。

判断节点是否存在:

Stat stat = ck.exists(path, false);
System.out.println(stat == null ? "not exist" : "exist");

5. 写数据原理

1)写请求直接发给Leader

其中,只要有半数节点写完,就可以发送ack给客户端,其他没写的服务端稍后再写。

2)  写请求发给Follower

这里也是半数节点写完就发送ack给客户端,所不同的是由接受写请求的Follower发送给客户端,而不是Leader,因为客户端最开始建立连接的是Follower。

6. 服务器动态上下线监听案例

分析:客户端监听服务器的上下线,本质是监听子节点的增删,服务器启动时会去Zookeeper集群注册(临时)子节点,使用的是create操作,而客户端监听则是get操作。注意这里的服务器和客户端对于Zookeeper集群来说都是客户端。

于是,代码主要分两部分,服务器创建子节点和客户端监听子节点。

// 服务器
private Zookeeper zk;public static void main(String[] args) throws Exception {// 创建本类(服务器类)的对象DistributeServer server = new DistributeServer();// 建立Zookeeper连接server.getConnect();// 注册,即创建子节点server.register(args[0]);// 服务端业务逻辑(睡觉)server.business();
}private void business() throws Exception {Thread.sleep(Long.MAX_VALUE);
}private void register(String hostName) throws Exception {String create = zk.create("/servers/" + hostName, hostName.getBytes(), ZooDef.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMRAL_SEQUENTIAL);System.out.println(hostName + "is online");
}private void getConnect() throws Exception {zk = new Zookeeper("xxx", 2000, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent){}});
}
// 客户端
private Zookeeper zk;public static void main(String[] args) throws Exception {// 创建本类(服务器类)的对象DistributeClient client = new DistributeClient();// 建立Zookeeper连接client.getConnect();// 注册,即创建子节点client.getServerList();// 客户端业务逻辑(睡觉)client.business();
}private void business() throws Exception {Thread.sleep(Long.MAX_VALUE);
}private void getServerList() throws Exception {List<String> children = zk.getChildren("/servers", true);List<String> servers = new ArrayList<>;for (String child : children) {byte[] data = zk.getData("/servers/" + child, false, null);servers.add(new String(data));}System.out.println(servers);
}private void getConnect() throws Exception {zk = new Zookeeper("xxx", 2000, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent){getServerList();}});
}

验证时,可以先验证客户端功能,服务端可以先用create -e -s 来代替,如果客户端功能ok,再继续验证服务端功能。

7. 分布式锁案例

分析:进程用客户端表示,每个客户端进程会去Zookeeper中创建一个临时带序号的子节点,如果子节点序号最小,则表示获取到锁,否则监听前一个序号更小的节点,持有锁执行完业务之后,会删除节点,表示释放锁,后面的节点/进场即可获取到锁。

private Zookeeper zk;
private String waitPath;
private String currentNode;
private CountDownLatch connectLatch = new CountDownLatch(1);
private CountDownLatch waitLatch = new CountDownLatch(1);public DistributeClient() throws Exception {// 创建本类(服务器类)的对象DistributeClient client = new DistributeClient();// 建立Zookeeper连接getConnect();connectLatch.await();
}// 加锁,创建临时节点,并判断是否是序号最小的节点,如果是则获取到锁,处理业务,如果不是,则监听前一个序号较小的节点
public void lock() throws Exception {// currentNode是全路径名String currentNode = zk.create("/locks/seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAGE, CreateMode.EPHEMETAL_SEQUENTIAL);List<String> children = zk.getChildren("locks", false);if (children.size() == 1) {return;} else {Collections.sort(children);String thisNode = currentNode.substring("/locks/".length());int index = children.indexOf(thisNode);if (index == -1) {System.out.println("数据异常");} else if (index == 0) {return;} else {// 监听前一个节点waitPath = "/locks/" + children.get(index -1);zk.getData(waitPath, true, null);waitLatch.await();}}}public void unlock()  throws Exception {zk.delete(currentNode, -1);
}private void getConnect() throws Exception {zk = new Zookeeper("xxx", 2000, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {if (watchedEvent.getType() == Event.KeeperState.SyncConnected) {connectLatch.countDown();       }if (watchedEvent.getType() == Event.EventType.NodeDeleted && Event.getPath.equals(waitPath)) {waitLatch.countDown();}}});
}

测试步骤:建立两个线程,每个线程起一个客户端去加锁解锁(加日志打印),加锁解锁之间有睡眠,运行这两个线程,可以看到最终只有1个客户端去持有锁

8. Curator框架

上述分布式锁的案例中,有如下缺点:

1)会话印布链接,需要自己使用CountDownLatch处理

2)监听需要重复注册

3)代码较复杂

4)不支持多节点删除与创建

因此引入Curator框架,添加pom依赖
<dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>4.3.0</version>
</dependency>
<dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>4.3.0</version>
</dependency>
<dependency><groupId>org.apache.curator</groupId><artifactId>curator-client</artifactId><version>4.3.0</version>
</dependency>
public static void main(String[] args) {// 获取分布式锁1InterProcessMutex lock1 = new InterProcessMutex(getCuratorFramework, "/locks");// 获取分布式锁2InterProcessMutex lock2 = new InterProcessMutex(getCuratorFramework, "/locks");// 启动两个线程,分别加锁释放锁,该锁可重入// 加锁: lock1.acquire(); 释放锁: lock1.release();
}private static CuratorFramework getCuratorFramework() {ExponentialBackoffRetry retry = new ExponentialBackoffRetry(3000, 3);CuratorFramework client =  CuratorFrameworkFactory.builder().connectString("xxx").connectionTimeoutMs(2000).sessionTimeoutMs(2000).retryPolicy(retry).build();client.start();System.out.println("客户端启动成功");return client;
}

9. 面试题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/491467.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python 鼠标模拟

鼠标模拟即&#xff1a;通过python 进行模拟鼠标操作 引入类库 示例如下&#xff1a; import win32api import win32con import time 设置鼠标位置 设置鼠标位置为窗口中的回收站。 示例如下&#xff1a; # 设置鼠标的位置 win32api.SetCursorPos([30, 40]) 双击图标 设置…

vue:find查找函数实际开发的使用

find的作用&#xff1a; find 方法主要是查找数组中的属性&#xff0c;会遍历数组&#xff0c;对每一个元素执行提供的函数&#xff0c;直到找到使该函数返回 true 的元素。然后返回该元素的值。如果没有元素满足测试函数&#xff0c;则返回 undefined。 基础使用&#xff1a…

五、数组——Java基础篇

六、数组 1、数组元素的遍历 1.1数组的遍历&#xff1a;将数组内的元素展现出来 1、普通for遍历&#xff1a;根据下表获取数组内的元素 2、增强for遍历&#xff1a; for&#xff08;数据元素类型 变量名&#xff1a;数组名&#xff09;{ 变量名&#xff1a;数组内的每一个值…

python统计分析——多解释变量的方差分析

参考资料&#xff1a;用python动手学统计学 1、导入库 # 导入库 # 用于数值计算的库 import numpy as np import pandas as pd import scipy as sp from scipy import stats # 用于绘图的库 from matplotlib import pyplot as plt import seaborn as sns sns.set() # 用于估计…

docker-compose 搭建laravel环境

laravel环境包含nginx,mysql,php7.4,redis 一、安装好docker后pull镜像 1.nginx镜像 docker pull nginx:latest单独启动容器 docker run --name nginx -p 80:80 -d nginx 2.php镜像 docker pull php:7.4-fpm3.mysql镜像 docker pull mysql:5.74.redis镜像 docker pull r…

高频面试题整理(一)

文章目录 平台无关性如何实现&#xff1f;JVM如何加载 .class文件&#xff1f;什么是反射?谈谈ClassLoader谈谈类的双亲委派机制类的加载方式Java的内存模型?JVM内存模型-jdk8程序计数器&#xff1a;Java虚拟机栈局部变量表和操作数栈&#xff1a; Java内存模型中堆和栈的区别…

Linux部署前后端分离项目

目录 一、jdk安装 二、tomcat安装 三、MySQL安装 四、nginx安装 五、配置多个tomcat负载&#xff0c;部署后端项目 六、前端部署 一、jdk安装 1. 上传jdk安装包 jdk-8u151-linux-x64.tar.gz 进入opt目录&#xff0c;将安装包拖进去 2. 解压安装包 这里需要解压到usr/loc…

17.材质和外观

1.图形学中的材质 在图形学中&#xff0c;材质&#xff08;Material&#xff09;是用来描述物体外观和表面特性的属性集合。它包含了控制光的反射、折射、吸收以及其他光学效果的信息&#xff0c;从而决定了物体在渲染过程中的外观。 渲染方程中那一项和材质有关&#xff1f; …

vue项目的前端工程化思路webpack(持续更新中)

写在前面&#xff1a;现在的前端网页功能丰富&#xff0c;特别是SPA&#xff08;single page web application 单页应用&#xff09;技术流行后&#xff0c;JavaScript的复杂度增加和需要一大堆依赖包&#xff0c;还需要解决Scss&#xff0c;Less……新增样式的扩展写法的编译工…

设计模式学习笔记 - 面向对象 - 8.实践:贫血模型和充血模型的原理及实践

1.Web开发常用的贫血MVC架构违背OOP吗&#xff1f; 前面我们依据讲过了面向对象四大特性、接口和抽象类、面向对象和面向过程编程风格&#xff0c;基于接口而非实现编程和多用组合少用继承设计思想。接下来&#xff0c;通过实战来学习如何将这些理论应用到实际的开发中。 大部…

第二节:Vben Admin 登录逻辑梳理和对接后端准备

系列文章目录 上一节&#xff1a;第一节&#xff1a;Vben Admin介绍和初次运行 文章目录 系列文章目录前言项目路径的概述一、登录逻辑梳理loginApi接口查看Mock 二、后端程序对接准备关闭Mock 总结 前言 第一节&#xff0c;我们已经配置了前端环境&#xff0c;运行起来了我们…

zabbix监控业务数据

前言 监控系统除了监控os和数据库性能相关的指标外&#xff0c;业务数据也是重点监控的对象。 一线驻场的运维同学应该深有体会&#xff0c;每天需要向甲方或者公司反馈现场的数据情况&#xff0c;正常情况下一天巡检两次&#xff0c;早上上班后和下午下班前各一次。监控项目…