ZooKeeper原理解析

♀尐吖头ヾ 提交于 2020-02-07 11:16:27

1.ZooKeeper介绍

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

1.1文件系统

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

所以总结说来,znode 即是文件夹又是文件的概念,所以在 ZooKeeper 这里面就不叫文件文 件也不叫文件夹,叫znode,每个znode有唯一的路径标识,既能存储数据,也能创建子znode。 但是 znode 只适合存储非常小量的数据,不能超过 1M,最好小于 1K

在这里插入图片描述

Znode 的介绍:

  1. Znode 有两种类型:

    短暂(ephemeral)(断开连接自己删除) 持久(persistent)(断开连接不删除)

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

    PERSISTENT 持久化 znode 节点,一旦创建这个 znode 点存储的数据不会主动 消失,除非是客户端主动的 delete
    PERSISTENT_SEQUENTIAL 自动增加顺序编号的 znode 节点,比如 ClientA 去 zk service 上建 立一个 znode 名字叫做/zk/conf,指定了这种类型的节点后 zk 会 创 建 /zk/conf0000000000 , ClientB 再 去 创 建 就 是 创 建 /zk/conf0000000001,ClientC 是创建/zk/conf0000000002,以后任 意 Client 来创建这个 znode 都会得到一个比当前 zk 命名空间最 大 znode 编号+1 的 znode,也就说任意一个 Client 去创建 znode 都是保证得到的 znode 是递增的,而且是唯一的
    EPHEMERAL 临时 znode 节点,Client 连接到 zk service 的时候会建立一个 session,之后用这个 zk 连接实例创建该类型的 znode,一旦 Client 关闭了 zk 的连接,服务器就会清除 session,然后这个 session 建立的 znode 节点都会从命名空间消失。总结就是,这个类型的 znode 的生命周期是和 Client 建立的连接一样的。比如 ClientA 创建了一个 EPHEMERAL 的/zk/conf0000000011 的 znode 节点,一旦 ClientA 的 zk 连接关闭,这个 znode 节点就会消失。整个 zk service 命名空间里就会删除这个 znode 节点
    EPHEMERAL_SEQUENTIAL 临时自动编号节点,znode 节点编号会自动增加,但是会随 session 消失而消失
  3. 创建 znode 时设置顺序标识,znode 名称后会附加一个值,顺序号是一个单调递增的计 数器,由父节点维护

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

  5. EPHEMERAL 类型的节点不能有子节点

  6. 客户端可以在 znode 上设置监听器

1.2监听机制

客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、节点删除、子目录节 点增加删除)时,ZooKeeper 会通知客户端。监听机制保证 ZooKeeper 保存的任何的数据的 任何改变都能快速的响应到监听了该节点的应用程序 监听器的工作机制,其实是在客户端会专门创建一个监听线程,在本机的一个端口上等待 zk 集群发送过来事件

在这里插入图片描述

注意:监听只生效一次

如果要做到循环监听,可以使用看 ZooKeeper Shell 和 API

1.3监听工作原理

ZooKeeper 的 Watcher 机制主要包括客户端线程、客户端 WatcherManager、Zookeeper 服务 器三部分。客户端在向 ZooKeeper 服务器注册的同时,会将 Watcher 对象存储在客户端的 WatcherManager 当中。当 ZooKeeper 服务器触发 Watcher 事件后,会向客户端发送通知, 客户端线程从 WatcherManager 中取出对应的 Watcher 对象来执行回调逻辑

在这里插入图片描述

1.4ZooKeeper典型应用场景

1.4.1命名服务

命名服务是分布式系统中较为常见的一类场景,分布式系统中,被命名的实体通常可以是集 群中的机器、提供的服务地址或远程对象等,通过命名服务,客户端可以根据指定名字来获 取资源的实体、服务地址和提供者的信息。Zookeeper 也可帮助应用系统通过资源引用的方 式来实现对资源的定位和使用,广义上的命名服务的资源定位都不是真正意义上的实体资源, 在分布式环境中,上层应用仅仅需要一个全局唯一的名字。Zookeeper 可以实现一套分布式 全局唯一 ID 的分配机制。
在这里插入图片描述

1.4.2配置管理

程序总是需要配置的,如果程序分散部署在多台机器上,要逐个改变配置就变得困难。现在 把这些配置全部放到 ZooKeeper 上去,保存在 ZooKeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 ZooKeeper 的通知,然后从 ZooKeeper 获取新的配置信息应用到系统中就好

在这里插入图片描述

1.4.3集群管理

所谓集群管理无在乎两点:是否有机器退出和加入、选举 master。

对于第一点,所有机器约定在父目录 GroupMembers 下创建临时目录节点,然后监听父目录 节点的子节点变化消息。一旦有机器挂掉,该机器与 ZooKeeper 的连接断开,其所创建的 临时目录节点被删除,所有其他机器都收到通知:某个兄弟目录被删除,于是,所有人都知 道:有兄弟挂了。新机器加入也是类似,所有机器收到通知:新兄弟目录加入,又多了个新兄弟。

对于第二点,我们稍微改变一下,所有机器创建临时顺序编号目录节点,每次选取编号最小 的机器作为 master 就好。当然,这只是其中的一种策略而已,选举策略完全可以由管理员 自己制定。
在这里插入图片描述

1.4.4分布式锁

有了 ZooKeeper 的一致性文件系统,锁的问题变得容易。

锁服务可以分为两三类

  • 一个是写锁,对写加锁,保持独占,或者叫做排它锁,独占锁

  • 一个是读锁,对读加锁,可共享访问,释放锁之后才可进行事务操作,也叫共享锁

  • 一个是控制时序,叫时序锁

    1. 对于第一类,我们将 ZooKeeper 上的一个 znode 看作是一把锁,通过 createznode 的方式来 实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了 这把锁。用完删除掉自己创建的 distribute_lock 节点就释放出锁
    2. 对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录 节点,和选 master 一样,编号最小的获得锁,用完删除,依次有序在这里插入图片描述

1.4.5队列管理

两种类型的队列:

  1. 同步队列:当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。
  2. 先进先出队列:队列按照 FIFO 方式进行入队和出队操作。 第一类,在约定目录下创建临时目录节点,监听节点数目是否是我们要求的数目。 第二类,和分布式锁服务中的控制时序场景基本原理一致,入列有编号,出列按编号。 同步队列的流程图:
    在这里插入图片描述

2.ZooKeeper特点

ZooKeeper 作为一个集群提供数据一致的协调服务,自然,最好的方式就是在整个集群中的 各服务节点进行数据的复制和同步。

数据复制的好处:

  1. 容错:一个节点出错,不至于让整个集群无法提供服务、、

  2. 扩展性:通过增加服务器节点能提高 ZooKeeper 系统的负载能力,把负载分布到多个节 点上

  3. 高性能:客户端可访问本地 ZooKeeper 节点或者访问就近的节点,依次提高用户的访问速度

Zookeeper特性

  1. 最终一致性:client 不论连接到哪个 Server,展示给它都是同一个视图,这是 ZooKeeper 最重要的性能。
  2. 可靠性:具有简单、健壮、良好的性能,如果消息 m 被到一台服务器接受,那么它将被 所有的服务器接受。
  3. 实时性:ZooKeeper 保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者 服务器失效的信息。但由于网络延时等原因,ZooKeeper 不能保证两个客户端能同时得 到刚更新的数据,如果需要最新数据,应该在读数据之前调用 sync()接口。
  4. 等待无关(wait-free):慢的或者失效的 client 不得干预快速的 client 的请求,使得每个 client 都能有效的等待。
  5. 原子性:更新只能成功或者失败,没有中间状态。
  6. 顺序性:包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息 a 在消息 b 前发布,则在所有 Server 上消息 a 都将在消息 b 前被发布;偏序是指如果一个消息 b 在 消息 a 后被同一个发送者发布,a 必将排在 b 前面。

3.Zookeeper原理解析

3.1集群角色描述

在这里插入图片描述

在这里插入图片描述

3.2Paxos 算法概述(ZAB 协议)

Paxos 算法是莱斯利•兰伯特(英语:Leslie Lamport)于 1990 年提出的一种基于消息传递且 具有高度容错特性的一致性算法。

分布式系统中的节点通信存在两种模型:共享内存(Shared memory)消息传递(Messages passing)。基于消息传递通信模型的分布式系统,不可避免的会发生以下错误:进程可能会慢、被杀死或者重启,消息可能会延迟、丢失、重复,在基础 Paxos 场景中,先不考虑可能 出现消息篡改即拜占庭错误(Byzantine failure,即虽然有可能一个消息被传递了两次,但是 绝对不会出现错误的消息)的情况。Paxos 算法解决的问题是在一个可能发生上述异常的分 布式系统中如何就某个值达成一致,保证不论发生以上任何异常,都不会破坏决议一致性。

Paxos 算法使用一个希腊故事来描述,在 Paxos 中,存在三种角色,分别为

Proposer(提议者,用来发出提案 proposal),

Acceptor(接受者,可以接受或拒绝提案),

Learner(学习者,学习被选定的提案,当提案被超过半数的 Acceptor 接受后为被批准)。

下面更精确的定义 Paxos 要解决的问题:

  1. 决议(value)只有在被 proposer 提出后才能被批准

  2. 在一次 Paxos 算法的执行实例中,只批准(chose)一个 value

  3. learner 只能获得被批准(chosen)的 value

    ZooKeeper 的选举算法有两种:一种是基于 Basic Paxos(Google Chubby 采用)实现的,另外 一种是基于 Fast Paxos(ZooKeeper 采用)算法实现的。系统默认的选举算法为 Fast Paxos。 并且 ZooKeeper 在 3.4.0 版本后只保留了 FastLeaderElection 算法。

    ZooKeeper 的核心是原子广播,这个机制保证了各个 Server 之间的同步。实现这个机制的协 议叫做 ZAB 协议(Zookeeper Atomic BrodCast)。 ZAB 协议有两种模式,它们分别是崩溃恢复模式(选主)原子广播模式(同步)

  4. 当服务启动或者在领导者崩溃后,ZAB 就进入了恢复模式,当领导者被选举出来,且大 多数 Server 完成了和 leader 的状态同步以后,恢复模式就结束了。状态同步保证了 leader 和 follower 之间具有相同的系统状态。

  5. 当 ZooKeeper 集群选举出 leader 同步完状态退出恢复模式之后,便进入了原子广播模式。 所有的写请求都被转发给 leader,再由 leader 将更新 proposal 广播给 follower

    为了保证事务的顺序一致性,zookeeper 采用了递增的事务 id 号(zxid)来标识事务。所有 的提议(proposal)都在被提出的时候加上了 zxid。实现中 zxid 是一个 64 位的数字,它高 32 位是 epoch 用来标识 leader 关系是否改变,每次一个 leader 被选出来,它都会有一个新 的 epoch,标识当前属于那个 leader 的统治时期。低 32 位用于递增计数。

Basic Paxos 流程

  1. 选举线程由当前 Server 发起选举的线程担任,其主要功能是对投票结果进行统计,并选 出推荐的 Server

  2. 选举线程首先向所有 Server 发起一次询问(包括自己)

  3. 选举线程收到回复后,验证是否是自己发起的询问(验证 zxid 是否一致),然后获取对方 的 serverid(myid),并存储到当前询问对象列表中,最后获取对方提议的 leader 相关信息 (serverid,zxid),并将这些信息存储到当次选举的投票记录表中

  4. 收到所有 Server 回复以后,就计算出 id 最大的那个 Server,并将这个 Server 相关信息设 置成下一次要投票的 Server

  5. 线程将当前 id 最大的 Server 设置为当前 Server 要推荐的 Leader,如果此时获胜的 Server 获得 n/2 + 1 的 Server 票数, 设置当前推荐的 leader 为获胜的 Server,将根据获胜的 Server 相关信息设置自己的状态,否则,继续这个过程,直到 leader 被选举出来。

    • 通过流程分析我们可以得出:要使 Leader 获得多数 Server 的支持,则 Server 总数必须是奇 数 2n+1,且存活的 Server 的数目不得少于 n+1。
    • 每个 Server 启动后都会重复以上流程。在恢复模式下,如果是刚从崩溃状态恢复的或者刚 启动的 server 还会从磁盘快照中恢复数据和会话信息,zk 会记录事务日志并定期进行快照, 方便在恢复时进行状态恢复。
    • Fast Paxos 流程是在选举过程中,某 Server 首先向所有 Server 提议自己要成为 leader,当其 它 Server 收到提议以后,解决 epoch 和 zxid 的冲突,并接受对方的提议,然后向对方发送 接受提议完成的消息,重复这个流程,最后一定能选举出 Leader

3.2.1ZooKeeper的全新集群选主

以一个简单的例子来说明整个选举的过程:假设有五台服务器组成的 zookeeper 集群,它们的 serverid 从 1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点 上,都是一样的。假设这些服务器依序启动

  1. 服务器 1 启动,此时只有它一台服务器启动了,它发出去的报没有任何响应,所以它的 选举状态一直是 LOOKING 状态

  2. 服务器 2 启动,它与最开始启动的服务器 1 进行通信,互相交换自己的选举结果,由于 两者都没有历史数据,所以 id 值较大的服务器 2 胜出,但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是 3),所以服务器 1、2 还是继续保持 LOOKING 状态

  3. 服务器 3 启动,根据前面的理论分析,服务器 3 成为服务器 1,2,3 中的老大,而与上面不 同的是,此时有三台服务器(超过半数)选举了它,所以它成为了这次选举的 leader

  4. 服务器 4 启动,根据前面的分析,理论上服务器 4 应该是服务器 1,2,3,4 中最大的,但是 由于前面已经有半数以上的服务器选举了服务器 3,所以它只能接收当小弟的命了

  5. 服务器 5 启动,同 4 一样,当小弟

    总结:zookeeper server 的三种工作状态

    • LOOKING:当前 Server 不知道 leader 是谁,正在搜寻,正在选举

    • LEADING:当前 Server 即为选举出来的 leader,负责协调事务

    • FOLLOWING:leader 已经选举出来,当前 Server 与之同步,服从 leader 的命令

3.2.2ZooKeeper的非全新集群选主

那么,初始化的时候,是按照上述的说明进行选举的,但是当 zookeeper 运行了一段时间之 后,有机器 down 掉,重新选举时,选举过程就相对复杂了。

需要加入数据 version、serverid 和逻辑时钟。

数据 version:数据新的 version 就大,数据每次更新都会更新 version

server id:就是我们配置的 myid 中的值,每个机器一个

逻辑时钟:这个值从 0 开始递增,每次选举对应一个值,也就是说:如果在同一次选举中, 那么这个值应该是一致的;逻辑时钟值越大,说明这一次选举 leader 的进程更新,也就是 每次选举拥有一个 zxid,投票结果只取 zxid 最新的 选举的标准就变成:

  1. 逻辑时钟小的选举结果被忽略,重新投票

  2. 统一逻辑时钟后,数据 version 大的胜出

  3. 数据 version 相同的情况下,server id 大的胜出 根据这个规则选出 leader。

    根据这个规则选出 leader。

3.3数据同步

选完 leader 以后,zk 就进入状态同步过程。

  1. leader 等待 server 连接;
  2. follower 连接 leader,将最大的 zxid 发送给 leader;
  3. leader 根据 follower 的 zxid 确定同步点;
  4. 完成同步后通知 follower 已经成为 uptodate 状态;
  5. follower 收到 uptodate 消息后,又可以重新接受 client 的请求进行服务了。 以下是流程图:

在这里插入图片描述

3.4ZooKeeper工作流程

3.4.4Leader工作流程

Leader 主要有三个功能:

  1. 恢复数据

  2. 维持与 Learner 的心跳,接收 Learner 请求并判断 Learner 的请求消息类型

    Learner 的消息类型主要:

    • PING 消息:Learner 的心跳信息
    • REQUEST 消息:Follower 发送的提议信息,包括读写请求
    • ACK 消息:Follower 对提议的回复,超过半数的 Follower 通过,则 commit 该提议
    • REVALIDATE 消息:用来延长 SESSION 有效时间
  3. 根据不同的消息类型,进行不同的处理。

3.4.2Follower工作流程

Follower 主要有四个功能:

  1. 向 Leader 发送请求(PING 消息、REQUEST 消息、ACK 消息、REVALIDATE 消息);

  2. 接收 Leader 消息并进行处理;

  3. 接收 Client 的请求,如果为写请求,则转发给 Leader;

  4. 返回 Client 结果。

Follower 的消息循环处理如下几种来自 Leader 的消息:

  1. PING 消息: 心跳消息;
  2. PROPOSAL 消息:Leader 发起的提案,要求 Follower 投票;
  3. COMMIT 消息:服务器端最新一次提案的信息;
  4. UPTODATE 消息:表明同步完成;
  5. REVALIDATE 消息:根据 Leader 的 REVALIDATE 结果,关闭待 revalidate 的 session 还是允 许其接受消息;
  6. SYNC 消息:返回 SYNC 结果到客户端,这个消息最初由客户端发起,用来强制得到最新 的更新。

3.4.3Observer工作流程

Observer 流程和 Follower 的唯一不同的地方就是 Observer 不会参加 Leader 发起的投票,也 不会被选举为 Leader,所以不重复描述了。

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