总结:ZooKeeper

元气小坏坏 提交于 2020-08-12 00:21:38

一、ZooKeeper数据模型

从图中我们可以看出ZooKeeper的数据模型,在结构上和标准文件系统的非常相似,都是采用这种树形层次结构,ZooKeeper树中的每个节点被称为—Znode。和文件系统的目录树一样,ZooKeeper树中的每个节点可以拥有子节点。但也有不同之处:

(1) 引用方式

  Zonde通过路径引用,如同Unix中的文件路径。路径必须是绝对的,因此他们必须由斜杠字符来开头。除此以外,他们必须是唯一的,也就是说每一个路径只有一个表示,因此这些路径不能改变。在ZooKeeper中,路径由Unicode字符串组成,并且有一些限制。字符串"/zookeeper"用以保存管理信息,比如关键配额信息。

(2) Znode结构

  ZooKeeper命名空间中的Znode,兼具文件和目录两种特点。既像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分。图中的每个节点称为一个Znode。 每个Znode由3部分组成:

   stat:此为状态信息, 描述该Znode的版本, 权限等信息

   data:与该Znode关联的数据

   children:该Znode下的子节点

  ZooKeeper虽然可以关联一些数据,但并没有被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,比如分布式应用中的配置文件信息、状态信息、汇集位置等等。这些数据的共同特性就是它们都是很小的数据,通常以KB为大小单位。ZooKeeper的服务器和客户端都被设计为严格检查并限制每个Znode的数据大小至多1M,但常规使用中应该远小于此值。

(3) 数据访问

  ZooKeeper中的每个节点存储的数据要被原子性的操作。也就是说读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。另外,每一个节点都拥有自己的ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的操作。

(4) 节点类型

  ZooKeeper中的节点有两种,分别为临时节点永久节点。节点的类型在创建时即被确定,并且不能改变。

  ① 临时节点:该节点的生命周期依赖于创建它们的会话。一旦会话(Session)结束,临时节点将被自动删除(这是个很常用的场景,如HBase集群和Zookeeper保持的就是会话,并维护临时节点,一但会话丢失,则临时节点也删除,临时节点删除,表示对应的HBase的服务器不可用了),当然可以也可以手动删除。虽然每个临时的Znode都会绑定到一个客户端会话,但他们对所有的客户端还是可见的。另外,ZooKeeper的临时节点不允许拥有子节点。

  ② 永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。

(5) 顺序节点

  当创建Znode的时候,用户可以请求在ZooKeeper的路径结尾添加一个递增的计数。这个计数对于此节点的父节点来说是唯一的,它的格式为"%10d"(10位数字,没有数值的数位用0补充,例如"0000000001")。当计数值大于232-1时,计数器将溢出。

(6) Watcher

   Zookeeper中的所有读操作有:getData(), getChildren()和exists(),都有一个设置Watcher的选项,我们称之为监视器。Zookeeper的Watcher会在观察数据发生变化时,发送给建立Watcher的客户端一个一次性触发器。在客户端接收到Watcher信息后,这个Watcher就会关闭。 

 

二、案例:利用zookeeper的znode节点路径的唯一性,从而方便的控制task的分布式运行

@Service
@Slf4j
public class ZookeeperUtils {

	public static String parentZnode = "/hubble-task";
	@Value("${zookeeper_address}")
	private String zookeeper_address;
	@Value("${zookeeper_user}")
	private String zookeeper_user;
	@Value("${zookeeper_password}")
	private String zookeeper_password;

	public static CuratorFramework curatorFramework = null;

	public CuratorFramework getCuratorFramework() throws Exception {
		try {
			if (curatorFramework != null)
				return curatorFramework;

			curatorFramework = CuratorFrameworkFactory.builder().connectString(zookeeper_address).sessionTimeoutMs(30000)
					.retryPolicy(new ExponentialBackoffRetry(1000, 3)).namespace("curator").build();
			curatorFramework.start();
			
			if (!StringUtils.isEmpty(zookeeper_user) && !StringUtils.isEmpty(zookeeper_password)) {
				ZooKeeper zk = curatorFramework.getZookeeperClient().getZooKeeper();
				zk.addAuthInfo("digest", (zookeeper_user + ":" + zookeeper_password).getBytes());
			}

			if (curatorFramework.checkExists().forPath(parentZnode) != null)
				return curatorFramework;

			curatorFramework.create().withMode(CreateMode.PERSISTENT).forPath(parentZnode,
					String.valueOf(System.currentTimeMillis()).getBytes());
		} catch (Exception e) {
			log.error("getCuratorFramework has error:",e);
		}
		return curatorFramework;
	}

	/**
	 * 创建节点。创建成功即获得执行权。
	 * 
	 * @throws Exception
	 */
	public void createNote(String nodeName) throws Exception {

		String path = parentZnode + nodeName;
		// 获取连接
		CuratorFramework curatorFramework = getCuratorFramework();

		try {
			// 获取节点值
			byte[] valNote = curatorFramework.getData().forPath(path);
			// 节点存在时
			if (valNote != null && valNote.length > 0) {
				// 获取节点值
				String strVal = new String(valNote);
				// 节点保存时间
				long logVal = Long.parseLong(strVal);
				// 当前时间
				long logCurrent = System.currentTimeMillis();

				// 节点超过存活时间,删除节点 暂定统一30秒
				if (logCurrent - logVal > 1000 * 30) {
					curatorFramework.delete().forPath(path);
				}
			}
		} catch (Exception e) {
		}

		// 创建节点
		curatorFramework.create().withMode(CreateMode.PERSISTENT).forPath(path,
				String.valueOf(System.currentTimeMillis()).getBytes());
	}

}

 

三、Watch机制

 Zookeeper提供了数据的发布/订阅功能,多个订阅者可同时监听某一特定主题对象,当该主题对象的自身状态发生变化时(例如节点内容改变、节点下的子节点列表改变等),会实时、主动通知所有订阅者。

  Zookeeper采用了Watcher机制实现数据的发布/订阅功能。该机制在被订阅对象发生变化时会异步通知客户端,因此客户端不必在Watcher注册后轮询阻塞,从而减轻了客户端压力。

Watcher机制实际上与观察者模式类似,也可看作是一种观察者模式在分布式场景下的实现方式。

 

四、基于ZooKeeper实现简单的服务注册于发现

  • 基于watcher机制,临时有序节点

基于ZooKeeper实现简单的服务注册于发现

 

五、分布式锁

  • 基于watcher机制,临时有序节点

Zookeeper基于临时有序节点实现分布式锁。

六、配置管理

  • 基于watcher机制

目前很多公司开发或者使用的程序都是分布式的,而程序总会或多或少的存在一些额外配置,且分散部署在多台机器上。某一条要修改配置,要逐个去修改配置就变得有些困难,特别是应用部署的点数特别大的时候,就成了不可能完成的事情了。一种方式就是把相关配置写到数据库中,每个应用去读取数据库的配置,这种方式优点就是把配置集中管理,缺点也很明显,就是不能及时获得配置的变革,要及时获得变更后的配置,就要不停的去扫描数据库,而这又会造成数据库的压力巨大。

我要说的这种方式就是利用zookeeper的这种发布订阅、watch来实现。即,把相关的配置数据写到zookeeper的某个指定的节点下。应用服务监听这个节点的数据变化,一旦节点数据(配置信息)发生了变化,应用服务就会收到zookeeper的通知,然后应用服务就可以从zookeeper获得新的配置信息。

 

七、ZooKeeper在HBase中的应用

HMaster

所有的 HMaster启动的时候竞争去创建一个名称相同的临时节点。Zookeeper确定只有一个能创建成功,其它失败的子节点针对这个成功的子节点注册Watcher事件,当临时节点被删除的时候,所有竞争失败的节点会重新参与竞争,这样就保证了一个集群中一定有且只有一个HMaster能成功,且当一个HMaster挂掉之后会有另一个立即晋升为matser,保证HA。

当HBase启动时,每个RegionServer都会到ZooKeeper的/hbase/rs节点下创建一个信息节点(下文中,我们称该节点为"rs状态节点"),例如/hbase/rs/[Hostname],同时,HMaster会对这个节点注册监听。当某个 RegionServer 挂掉的时候,ZooKeeper会因为在一段时间内无法接受其心跳(即 Session 失效),而删除掉该 RegionServer 服务器对应的 rs 状态节点。与此同时,HMaster 则会接收到 ZooKeeper 的 NodeDelete 通知,从而感知到某个节点断开,并立即开始容错工作。

HBase为什么不直接让HMaster来负责RegionServer的监控呢?如果HMaster直接通过心跳机制等来管理RegionServer的状态,随着集群越来越大,HMaster的管理负担会越来越重,另外它自身也有挂掉的可能,因此数据还需要持久化。在这种情况下,ZooKeeper就成了理想的选择。

RootRegion管理

对应HBase集群来说,数据存储的位置信息是记录在元数据region,也就是RootRegion上的。每次客户端发起新的请求,需要知道数据的位置,就会去查询RootRegion,而RootRegion自身位置则是记录在ZooKeeper上的(默认情况下,是记录在ZooKeeper的/hbase/meta-region-server节点中)。当RootRegion发生变化,比如Region的手工移动、重新负载均衡或RootRegion所在服务器发生了故障等是,就能够通过ZooKeeper来感知到这一变化并做出一系列相应的容灾措施,从而保证客户端总是能够拿到正确的RootRegion信息。

Region管理

HBase里的Region会经常发生变更,这些变更的原因来自于系统故障、负载均衡、配置修改、Region分裂与合并等。一旦Region发生移动,它就会经历下线(offline)和重新上线(online)的过程。

下线期间数据是不能被访问的,并且Region的这个状态变化必须让全局知晓,否则可能会出现事务性的异常。对于大的HBase集群来说,Region的数量可能会多达十万级别,甚至更多,这样规模的Region状态管理交给ZooKeeper来做也是一个很好的选择。

分布式SplitWAL任务管理

当某台RegionServer服务器挂掉时,由于总有一部分新写入的数据还没有持久化到HFile中,因此在迁移该RegionServer的服务时,一个重要的工作就是从WAL中恢复这部分还在内存中的数据,而这部分工作最关键的一步就是SplitWAL,即HMaster需要遍历该RegionServer服务器的WAL,并按Region切分成小块移动到新的地址下,并进行日志的回放(replay)

由于单个RegionServer的日志量相对庞大(可能有上千个Region,上GB的日志),而用户又往往希望系统能够快速完成日志的恢复工作。因此一个可行的方案是将这个处理WAL的任务分给多台RegionServer服务器来共同处理,而这就又需要一个持久化组件来辅助HMaster完成任务的分配。当前的做法是,HMaster会在ZooKeeper上创建一个SplitWAL节点(默认情况下,是/hbase/SplitWAL节点),将"哪个RegionServer处理哪个Region"这样的信息以列表的形式存放到该节点上,然后由各个RegionServer服务器自行到该节点上去领取任务并在任务执行成功或失败后再更新该节点的信息,以通知HMaster继续进行后面的步骤。ZooKeeper在这里担负起了分布式集群中相互通知和信息持久化的角色。

八、Zookeeper在Kafka中的应用

1、Broker注册

Broker是分布式部署并且相互之间相互独立,但是需要有一个注册系统能够将整个集群中的Broker管理起来,此时就使用到了Zookeeper。在Zookeeper上会有一个专门用来进行Broker服务器列表记录的节点:

/brokers/ids

每个Broker在启动时,都会到Zookeeper上进行注册,即到/brokers/ids下创建属于自己的节点,如/brokers/ids/[0...N]。

Kafka使用了全局唯一的数字来指代每个Broker服务器,不同的Broker必须使用不同的Broker ID进行注册,创建完节点后,每个Broker就会将自己的IP地址和端口信息记录到该节点中去。其中,Broker创建的节点类型是临时节点,一旦Broker宕机,则对应的临时节点也会被自动删除

2、Topic注册

在Kafka中,同一个Topic的消息会被分成多个分区并将其分布在多个Broker上,这些分区信息及与Broker的对应关系也都是由Zookeeper在维护,由专门的节点来记录,如:

/brokers/topics

Kafka中每个Topic都会以/brokers/topics/[topic]的形式被记录,如/brokers/topics/login和/brokers/topics/search等。Broker服务器启动后,会到对应Topic节点(/brokers/topics)上注册自己的Broker ID并写入针对该Topic的分区总数,如/brokers/topics/login/3->2,这个节点表示Broker ID为3的一个Broker服务器,对于"login"这个Topic的消息,提供了2个分区进行消息存储,同样,这个分区节点也是临时节点。

九、ZooKeeper在 Hadoop 中的应用

在Hadoop中,ZooKeeper主要用于实现HA(Hive Availability),包括HDFS的NamaNode和YARN的ResourceManager的HA。同时,在YARN中,ZooKeepr还用来存储应用的运行状态。HDFS的NamaNode和YARN的ResourceManager利用ZooKeepr实现HA的原理是一样的,所以本节以YARN为例来介绍。

YARN主要由ResourceManager(RM)、NodeManager(NM)、ApplicationMaster(AM)和Container四部分组成。其中最核心的就是ResourceManager

ResourceManager负责集群中所有资源的统一管理和分配,同时接收来自各个节点(NodeManager)的资源汇报信息,并把这些信息按照一定的策略分配给各个应用程序(Application Manager),其内部维护了各个应用程序的ApplicationMaster信息、NodeManager信息以及资源使用信息等。

为了实现HA,必须有多个ResourceManager并存(一般就两个),并且只有一个ResourceManager处于Active状态,其他的则处于Standby状态,当Active节点无法正常工作(如机器宕机或重启)时,处于Standby的就会通过竞争选举产生新的Active节点

主备切换

下面我们就来看看YARN是如何实现多个ResourceManager之间的主备切换的。

  1. 创建锁节点
    在ZooKeeper上会有一个/yarn-leader-election/appcluster-yarn的锁节点,所有的ResourceManager在启动的时候,都会去竞争写一个Lock子节点:/yarn-leader-election/appcluster-yarn/ActiveBreadCrumb,该节点是临时节点。ZooKeepr能够为我们保证最终只有一个ResourceManager能够创建成功创建成功的那个ResourceManager就切换为Active状态没有成功的那些ResourceManager则切换为Standby状态
[zk: localhost:2181(CONNECTED) 16] get /yarn-leader-election/appcluster-yarn/ActiveBreadCrumb

appcluster-yarnrm2
cZxid = 0x1b00133dc0
ctime = Tue Jan 03 15:44:42 CST 2017
mZxid = 0x1f00000540
mtime = Sat Jan 07 00:50:20 CST 2017
pZxid = 0x1b00133dc0
cversion = 0
dataVersion = 28
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 22
numChildren = 0

可以看到此时集群中ResourceManager2为Active。

  1. 注册Watcher监听
    所有Standby状态的ResourceManager都会向/yarn-leader-election/appcluster-yarn/ActiveBreadCrumb节点注册一个节点变更的Watcher监听,利用临时节点的特性,能够快速感知到Active状态的ResourceManager的运行情况。

  2. 主备切换
    当Active状态的ResourceManager出现诸如宕机或重启的异常情况时,其在ZooKeeper上连接的客户端会话就会失效,因此/yarn-leader-election/appcluster-yarn/ActiveBreadCrumb节点就会被删除。此时其余各个Standby状态的ResourceManager就都会接收到来自ZooKeeper服务端的Watcher事件通知,然后会重复进行步骤1的操作

以上就是利用ZooKeeper来实现ResourceManager的主备切换的过程,实现了ResourceManager的HA。

HDFS中NameNode的HA的实现原理跟YARN中ResourceManager的HA的实现原理相同。其锁节点为/hadoop-ha/mycluster/ActiveBreadCrumb

 

十、Zab协议

什么是Zab协议?

Zab协议 的全称是 Zookeeper Atomic Broadcast (Zookeeper原子广播),Zookeeper 是通过 Zab 协议来保证分布式事务的最终一致性

Zab协议是Zookeeper保证数据一致性的核心算法。Zab借鉴了Paxos算法,但又不像Paxos那样,是一种通用的分布式一致性算法。 它是特别为Zookeeper设计的支持崩溃恢复的原子广播协议基于该协议,zk实现了一种主备模型(即Leader和Follower模型)的系统架构来保证集群中各个副本之间数据的一致性。
 

Zab 协议实现的作用

 

 

1、 当主进程出现异常的时候,整个zk集群依旧能正常工作

2、保证分布式数据的一致性。

 

Zab协议内容

Zab 协议包括两种基本的模式:崩溃恢复  消息广播

  • 崩溃恢复

崩溃恢复主要包括两部分:Leader选举  数据恢复

  • 消息广播具体步骤

1)客户端发起一个写操作请求。

2)Leader 服务器将客户端的请求转化为事务 Proposal 提案,同时为每个 Proposal 分配一个全局的ID,即zxid。

3)Leader 服务器为每个 Follower 服务器分配一个单独的队列,然后将需要广播的 Proposal 依次放到队列中取,并且根据 FIFO 策略进行消息发送。

4)Follower 接收到 Proposal 后,会首先将其以事务日志的方式写入本地磁盘中,写入成功后向 Leader 反馈一个 Ack 响应消息。

5)Leader 接收到超过半数以上 Follower 的 Ack 响应消息后,即认为消息发送成功,可以发送 commit 消息。

6)Leader 向所有 Follower 广播 commit 消息,同时自身也会完成事务提交。Follower 接收到 commit 消息后,会将上一条事务提交。

zookeeper 采用 Zab 协议的核心,就是只要有一台服务器提交了 Proposal,就要确保所有的服务器最终都能正确提交 Proposal。这也是 CAP/BASE 实现最终一致性的一个体现。

Leader 服务器与每一个 Follower 服务器之间都维护了一个单独的 FIFO 消息队列进行收发消息,使用队列消息可以做到异步解耦。 Leader 和 Follower 之间只需要往队列中发消息即可。如果使用同步的方式会引起阻塞,性能要下降很多。



 

疑问

1、Follower事务处理失败了怎么办?zab如何保证数据一致性?

2、

 

参考:

Zookeeper学习之路 (一)初识

Zookeeper在kafka中的应用

一文了解Zookeeper的Watcher机制 Java花园

Zookeeper学习笔记四之持久节点和临时节点

Zookeeper实现分布式锁

基于ZooKeeper实现简单的服务注册于发现

ZooKeeper典型应用场景

Zookeeper——一致性协议:Zab协议

Zookeeper面试题

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!