Zookeeper相关知识点梳理

一、Zookeeper入门

1.ZK的数据模型

Zookeeper(简称ZK)是一种用于分布式应用程序的高性能协调服务,提供一种集中式信息存储服务。ZK的数据存储在内存中,它的存储结构类似文件系统的树形结构;ZK具有高吞吐量、低延迟和高可靠的特点。
ZK的数据存储结构类似文件系统,以“/”为根节点;但是不同之处在于每个ZK节点可包含与之关联的数据以及子节点(即是文件也是文件夹);每个节点路径总是表示为规范的、绝对的、斜杠分隔的路径。ZK具有4中节点类型:
(1)持久节点: 会话结束后也会一直存在;
(2)临时节点: 会话结束后就会消失;
(3)顺序节点: 节点名称顺序递增,会话结束后也会一直存在;
(4)临时顺序节点: 节点名称顺序递增,会话结束后就会消失。
【注】 对于顺序节点和临时顺序节点来说;节点序号为10位十进制数字,每个父节点有一个计数器,计数器为带符号int(4字节)数,超过2147483647后溢出导致变成负号。
ZK中的节点成为ZNode,ZNode的数据构成包含2个:
(1)节点数据: 存储的协调数据,如状态信息、配置和位置信息等,数据量上限为1M(可设置);
(2)节点元数据: 包含此节点的信息,是stat结构。
可用get命令获取Znode数据信息,包含以下信息:
(1)my_data: 此ZNode存储的数据;
(2)czxid: 创建该节点的事件ID;
(3)ctime: 创建该节点的时间;
(4)mzxid: 最后修改该节点的事件ID;
(5)mtime: 最后修改该节点的时间;
(6)pzxid: ZNode最后更新的子节点zxid;
(7)cversion: 该节点子节点的变更次数;
(8)dataversion: 该节点被修改的次数;
(9)aclversion: 访问控制列表被变更的次数;
(10)ephemralowner: 临时节点的所有者的会话ID,如果不是临时节点则为0;
(11)datalength: 该节点的数据长度;
(12)numchildren: 该节点子节点个数。
对于ZK中的时间概念,有以下说明:
(1)Zxid: ZK中每次更改操作都对应一个唯一的事物ID,称为Zxid;它是一个全局有序的戳记,如Zxid1小于Zxid2,则Zxid1一定在Zxid2之前发生;
(2)Version Numbers: 版本号,对应节点的每次更改都会导致该节点的版本号加一;
(3)Ticks: 当使用多服务器ZK时,服务器使用“滴答”来定义事件的时间,如状态上传、会话超时、对等节点之间的连接超时等;滴答时间仅通过最小会话超时(滴答时间的2倍)间接公开,如果客户端请求的会话小于最小会话超时,服务器将告诉客户端会话超时实际上是最小会话超时;
(4)RealTime: 真实时间,ZK除了在ZNode创建和修改时将时间戳放入stat结构外,根本用不到RealTime。

2.ZK的watch机制

ZK提供了watch机制,在一个ZNode上设置一个watch,即可监听此ZNode的变化。ZK中包含2类watch:
(1)data watch: 监听此节点的数据变化;
(2)child watch: 监听此节点的子节点变化。
操作节点会触发的watch事件如下:
(1)创建节点事件: 触发exists;
(2)删除节点事件: 触发exists、getdata、getchildren;
(3)更新节点事件: 触发exists、getdata;
(4)child事件: 触发getchildren。
watch机制有两个重要特性:
(1)一次性触发: watch触发后便会被删除,要持续监听变化,则需要再次设置watch;
(2)有序性: 客户端总是先得到watch通知,然后才会看到变化的结果。
由于以上的两个特性,使用watch时需要注意:
(1)因为watcher为一次性触发,且在获取事件和发送获取watch的新请求间存在延迟,所以不能可靠的获取节点发生的每个改变
(2)一个watch对象只会被特定的通知触发一次,若一个watch对象同时注册了exists、getData,当节点被删除时,删除事件对exists、getData都有效,但是只会调用watch一次。
以下是Java代码操作ZK的示例:

import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;

public class ZkClientWatchDemo {
   
	public static void main(String[] args) {
   
		// 创建一个zk客户端
		ZkClient client = new ZkClient("localhost:2181");
		//为此客户端设置watch(ZkClient做了封装,实现了一直监听ZNode的效果)
		client.subscribeDataChanges("/test/a", new IZkDataListener() {
   
			//设置节点被删除后进行的业务
			@Override
			public void handleDataDeleted(String dataPath) throws Exception {
   
				System.out.println("----收到节点被删除了-------------");
			}
			//设置节点数据变化后进行的业务
			@Override
			public void handleDataChange(String dataPath, Object data) throws Exception {
   
				System.out.println("----收到节点数据变化:" + data + "-------------");
			}
		});
		try {
   
			Thread.sleep(1000 * 60 * 2);
		} catch (InterruptedException e) {
   
			e.printStackTrace();
		}
	}
}

3.ZK的特性

ZK具有一些重要的特性:
(1)顺序一直性: 保证客户端操作是按顺序生效的(利用此特性可以做分布式锁等);
(2)原子性: 更新成功或失败,没有部分结果(见第三章的ZAB协议,说明了是如何实现的);
(3)单个系统映像: 无论连那个服务器,客户端都将看到相同的结果(强一致性);
(4)可靠性: 数据的变更不会丢失,除非被客户端覆盖修改(有日志记录来保证);
(5)及时性: 保证客户端读取到的数据是最新的。

二、Zookeeper应用场景

1.ZK实现配置中心

利用ZK实现配置中心可以动态修改配置并且可以实现热部署;利用了ZK节点的树形结构和watch机制。具体实现可以看我之前的博客:基于Zookeeper实现分布式配置中心

2.ZK实现命令服务

假设服务A中需要调用服务B的功能,如果服务A的代码开发完成,但是服务B的代码还没有开发完成;那么是否能让服务A先部署呢?利用ZK就可以实现此功能;只需要把服务B的服务名先注册到ZK中,而服务A只要在调用到服务B的地方指定调用服务B的服务名的服务(利用ZK的watch机制);这样先把服务A部署好,并且一直监听服务B是否已经部署完成,一旦当服务B部署完成服务A就会监听到,然后服务A再去调用服务B的功能即可。示例图如下:
在这里插入图片描述

3.ZK实现Master选举

在一些分布式集群中,需要选举Master节点时也可以利用ZK实现(比如Kafka就是);示例图如下:
在这里插入图片描述
如上图所示,需要说明的是:
(1)所以节点都去争抢成为Master节点;对于ZK来说,就是多个客户端去创建同一个临时节点,创建成功的客户端成为Master节点;比如A成为了Master节点,那么其他节点再去创建此ZNode时就会失败,并且这些节点可以监听此节点的变化,同时其他节点也会知道是谁成为了Master节点;当A节点挂了,其他节点将会继续争抢成为Master(类似实现分布式锁);
(2)除此之外,我们还可以定义一个server子节点,通过server子节点可以获知整个集群当前有哪些节点,那么Master节点就可以管理整个集群中的节点了。

4.ZK实现分布式队列

分布式队列其实本质上就是一个队列,只不过是在分布式的环境中,让各个服务共享。示例图如下:
在这里插入图片描述
如上图所示,使用分布式队列的步骤为:
(1)入队: 即创建顺序节点,节点里放生产者的数据,这个顺序即入队的顺序;
(2)出队: 因为队列是先进先出,所以先获取所有节点号,取出最小好的节点的数据,并移除最小号节点。
需要注意的是,以上创建的是无界队列,若要实现有界队列,则生产者必须得知已有顺序节点的个数,超过了则不能创建顺序节点;同时需要用到分布式锁,只有抢到锁的生产者才能创建顺序节点,创建完再释放锁;否则会超出队列的界限。

5.ZK实现分布式锁

(一)方式一:
示例图如下:
在这里插入图片描述
流程图如下:
在这里插入图片描述
首先所有实例争抢创建同一名字(如Lock)的临时节点,因为ZNode不能重名,所以只会有一个实例创建成功,即“抢锁”成功,其他示例抢锁失败,同时其他示例监听此临时节点,并且阻塞自己的代码;抢到锁的实例执行完自己的代码后,释放掉锁,即删除临时节点;或者抢到锁的实例突然挂了,也会因为临时节点的属性而自动删除此节点;如此一来,其他实例在监听到临时节点被删除后便会继续抢锁。(原理即:ZNode不能重名+watch机制)
【注】此种方式有一个缺点会引发“惊群”效应,即抢锁时会“惊动”集群中的所有实例抢锁,从而造成巨大的网络消耗;所有此种方式只适合并发量小的场景。
代码示例如下:

public class ZKDistributeLock implements Lock {
   
	private String lockPath;
	private ZkClient client;
	// 锁重入计数
	private ThreadLocal<Integer> reentrantCount = new ThreadLocal<>();
	public ZKDistributeLock(String lockPath) {
   
		super();
		this.lockPath = lockPath;

		client = new ZkClient("localhost:2181");
		client.setZkSerializer(new MyZkSerializer());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值