Zookeeper(一)

一、引言

CAP理论:

1、一致性(Consistency)(C): 在分布式系统中的所有数据备份,在同一时刻是否同样的值。 (等同于所有节点访问同一份最新的数据副本)

2、可用性(Availability)(A): 在集群中一部分节点故障后,在一定时间内,集群整体是否 还能响应客户端的读写请求。(对数据更新具备高可用性)

3、分区容错性(Partition tolerance)(P): 以实际效果而言,分区相当于对通信的时限要求。 系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在 C 和 A 之间做出选择。

定理:任何分布式存储系统只能同时满足两点,没法三者兼顾

原因:数据备份的节点越多,分区容错性P越高,但要复制更新的数据越多,一致性C就很难保证。为了保证一致性,更新所有节点所需的时间就会加长,可用性A就会降低。

Mysql:满足CA 

Zookeeper:满足CP

二、Zookeeper简介

        ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现。它提供了简单原始的功能,分布式应用可以基于它实现更高级的服务,比如分布式同步,配置管理,集群管理,命名管理,队列管理。它被设计为易于编程,使用文件系统目录树作为数据模型。

        ZooKeeper 是集群的管理者监视着集群中各节点的状态,根据节点提交的反馈进行下一步合理的操作。最终将简单易用的接口和功能稳定、性能高效的系统提供给用户。

三、Zookeeper提供的功能

Zookeeper = 文件系统 + 监听机制

1. 文件系统

        ZooKeeper 的命名空间就是 ZooKeeper 应用的文件系统,它和 linux 的文件系统很像,也是树状,这样就可以确定每个路径都是唯一的,对于命名空间的操作必须都是绝对路径操作。与 linux 文件系统不同的是,Zookeeper没有文件和文件夹的概念,所有节点统一叫做Znode一个 Znode 节点可以包含子 Znode,同时也可以包含数据

        znode既是文件又是文件夹,每个znode有唯一的路径标识,既能存储数据(相当于文件),同时又能创建子znode(相当于文件夹中创建子文件)。

        Znode只适合存储非常小量的数据,不能超过1M,最好不要超过1K。

        Znode介绍:

        1)Znode有两种类型:

                临时节点(ephemeral):客户端断开连接后自动删除

                永久节点(persistent):只能手动删除

        2)Znode有四种形式的目录节点(默认是persistent)

PERSISTENT

永久节点

持久化 znode 节点,一旦创建这个 znode 点存储的数据不会主动消失,除非客户端主动delete

PERSISTENT_SEQUENTIAL

  永久有编号节点

自动增加顺序编号的 znode 节点。每当有客户端创建一个有编号节点,都会在节点名称后加上一个编号串(0000000000依次递增),编号为当前zk命名空间最大znode编号+1,命名空间由父znode维护。即任意一个 Client 去创建 znode 都是保证得到的 znode 是递增的,而且是唯一的

EPHEMERAL

临时节点

客户端连接zk service时会建立一个session,临时节点的声明周期与session相同。一旦客户端断开连接,服务器就会清楚session,临时节点就会被自动删除。

EPHEMERAL_SEQUENTIAL

临时有编号节点

临时有编号节点,Znode节点编号会自增,但会随session消失而消失,但命名空间中的编号不会重置

        3)有编号节点的编号值由父节点维护,即不同Znode的子目录的命名空间相互独立

        4)在分布式系统中,顺序号可以被用于为所有的事件进行全局排序,这样客户端可以通过 顺

              序号推断事件的顺序。

        5)临时节点不能创建子节点

        6)客户端可以在Znode上添加监听

Znode中的信息字段解释:

cZxid = 0x600000aad节点创建时候的zxid
ctime = Thu Dec 16 16:38:27 CST 2021 节点创建时间
mZxid = 0x600000aad    节点修改的zxid,与子节点修改无关
mtime = Thu Dec 16 16:38:27 CST 2021节点的修改时间
pZxid = 0x600000aad 子节点创建/删除的zxid,和修改无关,与孙子节点无关
cversion = 0     子节点的更新次数
dataVersion = 0  节点数据的更新次数
aclVersion = 0节点(ACL)的更新次数
ephemeralOwner = 0x0 临时节点:值为sessionId。永久节点:0
dataLength = 0节点数据的字节数
numChildren = 0子节点个数,不包括孙子节点

 cZxid、mZxid、pZxid都是全局唯一且循序递增,这3个id共同标识全局事件的提交顺序(标识对某一节点操作的事件的顺序)

Zxid共64位,其中:

        1. 高32位:标识Leader关系是否发生改变。选出一个新的Leader时,该值才会变化。

        2. 低32位:事件的提交顺序。该命名空间由leader维护,按事件的提交顺序递增。

                           leader发生改变后重置。

2. 监听机制

        客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、节点删除、子目录节 点增加删除)时,ZooKeeper 会通知客户端。监听机制保证 ZooKeeper 保存的任何的数据的 任何改变都能快速的响应到监听了该节点的应用程序。

        监听器的工作机制,其实是在客户端上专门创建一个监听线程,在本机的一个端口上等待 zk 集群发送过来事件

        监听事件有4类:

nodedatachanged节点内容变化事件
nodecreated节点创建事件
nodedeleted节点删除事件
nodechildrenchanged子节点发生变化事件

        Zookeeper就是运用监听机制,监听自身的数据变化,最后将变化发送给感兴趣的客户端(添加了监听事件的客户端)

[注]:监听只能生效一次 

监听工作原理:

        

四、Zookeeper典型应用场景

1. 配置管理

        在分布式系统中,配置文件分布在多台机器上,若要改变配置需要逐个改变所有机器上的配置十分困难。此时,可以将配置存储在Zookeeper上。将配置保存在Zookeeper某个目录的节点上,之后所有相关应用对该目录节点添加监听,一旦配置信息发生变化,所有应用都会收到Zookeeper的通知,然后从Zookeeper中下载最新的配置即可。 

2. 集群管理

        所谓集群管理无外乎两点:是否有节点加入或退出,集群选主

        对于第一点,所有机器约定在父目录GroupMembers下创建临时节点,并对该目录添加子节点变化的监听。一旦有新节点加入或有节点挂掉,集群中的节点都会接收到Zookeeper的通知。

        对于第二点,选主策略将会后续的Zookeeper选主中讲解。

3. 分布式锁

        锁服务有两种类型:

        写锁:对写操作加锁,对资源保持独占,又叫排他锁

        读锁:对读操作加锁,可共享访问,又叫共享锁

        对于写锁,可以将Zookeeper的一个Znode看做是锁,如/distribute_lock。当有客户端需要做写操作时,先在Zookeeper中创建/distribute_lock节点,若节点不存在,则创建成功,相当于获得锁。当写操作完成后再删除节点,相当于释放锁;若节点已存在,说明有其它客户端正在占用锁,进入等待。

        对于读锁,/distribute_lock节点已经预先存在,所有客户端想要获得锁在其下面创建临时有编号节点,每次在该目录下选择编号最小的节点获得锁。

 五、Zookeeper集群Cli使用

        首先,我们可以是用命令 bin/zkCli.sh 进入 ZooKeeper 的命令行客户端,这种是直接连接本 机的 ZooKeeper 服务器,还有一种方式,可以连接其他的 ZooKeeper 服务器,只需要我们在命令后面接一个参数-server 就可以了。例如:zkCli.sh -server hadoop01:2181

此处会出现一个小bug,在进入Zookeeper客户端的目录下会生成一个zookeeper.out文件,解决办法参考

CSDNhttps://mp.csdn.net/mp_blog/creation/editor/81838573

        进入命令行后键入help可以查看帮助文档,帮助文档中列出了所有操作以及参数设置如下图

 常用命令:

查看Znode子节点内容

ls /

ls /zk

创建Znode节点

create /zk "data"        # 默认创建永久无编号节点。 -e 创建临时节点。  -s 创建有编号节点。

获取Znode数据

get /zk/node

设置Znode数据

set /zk/node "data"

监听Znode事件

ls /zk watch        # 对zk节点的子节点变化事件注册监听

get /zk watch        # 对zk节点的数据内容变化事件注册监听

删除Znode节点

delete /zk        # 只能删除没有子节点的节点(空目录)

rmr /zk        # 强制删除

 六、Zookeeper集群Java API使用

常用API:

create(path, data, flags)

创建一个Znode,flags标识Znode类型:

PERSISTEN, PERSISTENT_SEQUENTAIL,

EPHEMERAL, EPHEMERAL_SEQUENTAIL

delete(path, version)

删除一个Znode,可以通过version删除指定版本,version=-1代表删除所欲版本

exists(path, watch)

判断Znode是否存在。watch=null表示不添加监听

getData(path, watch)

读取指定Znode的数据

setData(path, watch)

更新指定Znode的数据

getChildren(path, watch)

获取指定Znode的所有子Znode的名字

[注]:所有方法中watch参数代表添加的监听。Watcher是在创建Zookeeper实例时指定的。

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

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


public class ZkDemo {
	// 客户端去请求链接的时候的服务器链接地址信息
	static final String CONNECT_STRING = "hadoop01:2181";
	// 客户端去请求链接的超时时长
	static final int SESSION_TIMEOUT = 3000;
	static ZooKeeper zk = null;

	public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
		/**
		 * 创建 Zookeeper 对象,获取 Zookeeper 连接
		 * 参数1: 连接  主机名:2181
		 * 参数2: 连接的超时时间(ms)
		 * 参数3: 监听对象,不需要监听传null
		 */
		zk = new ZooKeeper(CONNECT_STRING, SESSION_TIMEOUT, null);

		/**
		 * 创建节点
		 * 参数1: 节点路径  参数2: 节点存储的路径 byte[]
		 * 参数3: 权限  OPEN_ACL_UNSAFE  最大权限
		 * 参数4: 节点的属性(节点的模式) -- 永久有(无)编号,临时有(无)编号
		 */
		zk.create("/test", "zk_test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

		/**
		 * 删除节点,不能删除非空节点,API中没有提供rmr
		 * 参数1: 路径
		 * 参数2: 版本,不知道写-1(代表当前版本)
		 */
		zk.delete("/test", -1);

		// 修改节点内容
		zk.setData("/test", "test_zk".getBytes(), -1);
		// 获取节点内容,返回byte[]
		byte[] data = zk.getData("/test", null, null);
		// 获取节点的子节点列表
		List<String> children = zk.getChildren("/test", null);
		/**
		 * 判断节点是否存在。存在返回状态信息,不存在返回null
		 * 返回值是 Stat 对象,Stat对象中封装着节点的Zxid,version等信息,可通过get()方法查看
		 */
		Stat exists = zk.exists("/test01", null);

		zk.close();
	}
}

添加监听:如何循环添加监听?

import org.apache.zookeeper.*;
import java.io.IOException;


public class ZkWatch {
	// 客户端去请求链接的时候的服务器链接地址信息
	static final String CONNECT_STRING = "hadoop01:2181";
	// 客户端去请求链接的超时时长
	static final int SESSION_TIMEOUT = 3000;
	static ZooKeeper zk = null;

	public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
		zk = new ZooKeeper(CONNECT_STRING, SESSION_TIMEOUT, new Watcher() {
			/**
			 * 回调方法,触发监听的时候会进行回调
			 * watchedEvent: 监听事件对象,提供3个重要属性:
			 * 1) Path: 监听事件路径
			 * 2) Type: 监听事件类型
			 * 3) State: 监听事件状态
			 */
			public void process(WatchedEvent watchedEvent) {
				String path = watchedEvent.getPath();
				Event.EventType type = watchedEvent.getType();
				Event.KeeperState state = watchedEvent.getState();
				System.out.println(path + "--" + type + "--" + state);

                // 循环添加监听,每次触发监听事件调用process函数后继续添加监听。

				try {
					zk.exists("/test", true);  
				} catch (KeeperException e) {
					e.printStackTrace();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		// 添加监听  getChildren  getData  exists
		/**
		 * 参数2的三种传参方式
		 * 1. null: 不添加监听
		 * 2. true: 添加监听  监听触发的时候,回调创建对象时的Watcher对象的process方法
		 * 	  false: ==null   不添加监听
		 * 3. watcher对象  触发监听的时候调用的是此处的process
		 * 		监听每触发一次,都会回调process函数
		 */
		zk.exists("/test", true);

		zk.exists("/tt02", new org.apache.zookeeper.Watcher() {
			public void process(WatchedEvent watchedEvent) {
				String path = watchedEvent.getPath();
				Event.EventType type = watchedEvent.getType();
				Event.KeeperState state = watchedEvent.getState();
				System.out.println(path + "--" + type + "--" + state);
			}
		});

		// 触发监听  exists添加的监听可由create  delete  setData触发
		zk.create("/test", "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
		zk.close();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值