Zookeeper - 快速指南
Zookeeper - 概述
ZooKeeper 是一种分布式协调服务,用于管理大量主机。在分布式环境中协调和管理服务是一个复杂的过程。ZooKeeper 以其简单的架构和 API 解决了这个问题。ZooKeeper 允许开发人员专注于核心应用程序逻辑,而无需担心应用程序的分布式特性。
ZooKeeper 框架最初是在“Yahoo!”构建的。以简单而可靠的方式访问他们的应用程序。后来,Apache ZooKeeper 成为 Hadoop、HBase 和其他分布式框架使用的有组织服务的标准。例如,Apache HBase 使用 ZooKeeper 来跟踪分布式数据的状态。
在进一步讨论之前,我们对分布式应用程序了解一两件事很重要。因此,让我们从分布式应用程序的快速概述开始讨论。
分布式应用
分布式应用程序可以在给定时间(同时)在网络中的多个系统上运行,通过它们之间的协调来快速有效地完成特定任务。通常,非分布式应用程序(在单个系统中运行)需要数小时才能完成的复杂且耗时的任务,可以通过分布式应用程序利用所有相关系统的计算能力在几分钟内完成。
通过将分布式应用程序配置为在更多系统上运行,可以进一步减少完成任务的时间。运行分布式应用程序的一组系统称为集群,集群中运行的每台机器称为节点。
分布式应用程序有两部分,服务器和客户端应用程序。服务器应用程序实际上是分布式的,并且具有公共接口,以便客户端可以连接到集群中的任何服务器并获得相同的结果。客户端应用程序是与分布式应用程序交互的工具。
分布式应用程序的好处
可靠性- 单个或几个系统的故障不会导致整个系统故障。
可扩展性- 可以在需要时通过添加更多机器来提高性能,只需对应用程序的配置进行较小的更改,无需停机。
透明度- 隐藏系统的复杂性并将其自身显示为单个实体/应用程序。
分布式应用的挑战
竞争条件- 两台或多台机器尝试执行特定任务,实际上在任何给定时间只需要由一台机器完成。例如,共享资源在任何给定时间只能由一台机器修改。
死锁- 两个或多个操作无限期地等待彼此完成。
不一致- 数据部分失败。
Apache ZooKeeper 的用途是什么?
Apache ZooKeeper 是集群(节点组)使用的一项服务,用于在它们之间进行协调并通过强大的同步技术维护共享数据。ZooKeeper本身是一个分布式应用程序,为编写分布式应用程序提供服务。
ZooKeeper 提供的常见服务如下:
命名服务- 通过名称识别集群中的节点。它与 DNS 类似,但针对的是节点。
配置管理- 加入节点的系统的最新配置信息。
集群管理- 集群中节点的加入/离开以及实时节点状态。
领导者选举- 选举一个节点作为领导者以进行协调。
锁定和同步服务- 在修改数据时锁定数据。此机制可帮助您在连接其他分布式应用程序(例如 Apache HBase)时进行自动故障恢复。
高度可靠的数据注册- 即使一个或几个节点发生故障,数据也可用。
分布式应用程序提供了很多好处,但它们也带来了一些复杂且难以解决的挑战。ZooKeeper框架提供了一个完整的机制来克服所有的挑战。使用故障安全同步方法处理竞争条件和死锁。另一个主要缺点是数据不一致,ZooKeeper 通过Atomics性解决了这个问题。
ZooKeeper 的优点
以下是使用 ZooKeeper 的好处 -
简单的分布式协调流程
同步- 服务器进程之间的相互排斥和合作。此过程有助于 Apache HBase 中的配置管理。
有序消息
序列化- 根据特定规则对数据进行编码。确保您的应用程序持续运行。这种方法可以在 MapReduce 中使用来协调队列来执行正在运行的线程。
可靠性
Atomics性- 数据传输要么成功,要么完全失败,但没有事务是部分的。
Zookeeper - 基础知识
在深入了解ZooKeeper的工作原理之前,我们先来了解一下ZooKeeper的基本概念。我们将在本章中讨论以下主题 -
- 建筑学
- 分层命名空间
- 会议
- 手表
ZooKeeper的架构
看一下下图。它描述了 ZooKeeper 的“客户端-服务器架构”。
下表对属于 ZooKeeper 架构的每个组件进行了说明。
部分 | 描述 |
---|---|
客户 | 客户端是分布式应用程序集群中的节点之一,从服务器访问信息。在特定的时间间隔内,每个客户端都会向服务器发送一条消息,让服务器知道客户端还活着。 类似地,当客户端连接时服务器会发送确认。如果连接的服务器没有响应,客户端会自动将消息重定向到另一台服务器。 |
服务器 | 服务器是 ZooKeeper 整体中的节点之一,为客户端提供所有服务。向客户端发出确认,通知服务器处于活动状态。 |
合奏 | ZooKeeper 服务器组。形成集成所需的最小节点数为 3。 |
领导者 | 如果任何连接的节点发生故障,则执行自动恢复的服务器节点。领导者在服务启动时选举产生。 |
追随者 | 遵循领导者指令的服务器节点。 |
分层命名空间
下图描述了用于内存表示的 ZooKeeper 文件系统的树形结构。ZooKeeper 节点称为znode。每个 znode 由名称标识并由路径序列 (/) 分隔。
在图中,首先有一个用“/”分隔的根znode。在 root 下,您有两个逻辑命名空间config和workers。
config命名空间用于集中配置管理,workers命名空间用于命名。
在config命名空间下,每个znode最多可以存储1MB的数据。这与 UNIX 文件系统类似,只是父 znode 也可以存储数据。该结构的主要目的是存储同步数据并描述znode的元数据。这种结构称为ZooKeeper 数据模型。
ZooKeeper 数据模型中的每个 znode 都维护一个stat结构。统计数据仅提供znode 的元数据。它由版本号、操作控制列表 (ACL)、时间戳和数据长度组成。
版本号- 每个 znode 都有一个版本号,这意味着每次与 znode 关联的数据发生更改时,其相应的版本号也会增加。当多个 Zookeeper 客户端尝试在同一个 znode 上执行操作时,版本号的使用非常重要。
操作控制列表(ACL) - ACL 基本上是一种用于访问 znode 的身份验证机制。它控制所有 znode 的读写操作。
时间戳- 时间戳表示 znode 创建和修改所经过的时间。它通常以毫秒表示。ZooKeeper 从“事务 ID”(zxid) 中识别 znode 的每个更改。Zxid是唯一的,它维护每笔事务的时间,以便您可以轻松识别从一个请求到另一个请求所经过的时间。
数据长度- znode 中存储的数据总量是数据长度。您最多可以存储 1MB 的数据。
Znode 的类型
Znode 分为持久性、顺序性和短暂性。
持久性 znode - 即使创建该特定 znode 的客户端断开连接,持久性 znode 仍处于活动状态。默认情况下,除非另有说明,所有 znode 都是持久的。
临时 znode - 临时 znode 一直处于活动状态,直到客户端处于活动状态。当客户端与 ZooKeeper 整体断开连接时,临时 znode 会自动删除。因此,只有临时 znode 才不允许再有子节点。如果删除临时 znode,则下一个合适的节点将填补其位置。临时 znode 在 Leader 选举中发挥着重要作用。
顺序 znode - 顺序 znode 可以是持久的或短暂的。当新的 znode 创建为顺序 znode 时,ZooKeeper 通过将 10 位数字的序列号附加到原始名称来设置 znode 的路径。例如,如果将路径为/myapp 的znode创建为顺序 znode,ZooKeeper 会将路径更改为/myapp0000000001并将下一个序列号设置为 0000000002。如果同时创建两个顺序 znode,则 ZooKeeper 永远不会使用相同的编号每个 znode。顺序 znode 在锁定和同步中发挥着重要作用。
会议
会话对于ZooKeeper的运行非常重要。会话中的请求按照 FIFO 顺序执行。一旦客户端连接到服务器,就会建立会话,并将会话 ID分配给客户端。
客户端以特定的时间间隔发送心跳以保持会话有效。如果 ZooKeeper 整体在服务启动时指定的时间段(会话超时)内没有收到来自客户端的心跳,则判定该客户端已死亡。
会话超时通常以毫秒表示。当会话因任何原因结束时,在该会话期间创建的临时 znode 也会被删除。
手表
监视是一种简单的机制,客户端可以获取有关 ZooKeeper 整体中的更改的通知。客户端可以在读取特定 znode 时设置监视。监视程序会向已注册的客户端发送任何 znode(客户端在其上注册)更改的通知。
Znode 更改是与 znode 关联的数据的修改或 znode 子节点的更改。手表仅触发一次。如果客户端想要再次收到通知,则必须通过另一个读取操作来完成。当连接会话过期时,客户端将与服务器断开连接,并且关联的监视也将被删除。
Zookeeper - 工作流程
一旦 ZooKeeper ensemble 启动,它将等待客户端连接。客户端将连接到 ZooKeeper 整体中的节点之一。它可能是领导者或追随者节点。一旦客户端连接,节点就会向特定客户端分配会话 ID,并向客户端发送确认。如果客户端没有得到确认,它只会尝试连接 ZooKeeper 集合中的另一个节点。一旦连接到某个节点,客户端就会定期向该节点发送心跳,以确保连接不会丢失。
如果客户端想要读取特定的 znode,它会向具有 znode 路径的节点发送读取请求,并且该节点通过从自己的数据库获取所请求的 znode 来返回该 znode。因此,ZooKeeper ensemble 中的读取速度很快。
如果客户端想要将数据存储在 ZooKeeper ensemble 中,它将 znode 路径和数据发送到服务器。连接的服务器将请求转发给领导者,然后领导者将向所有追随者重新发出写入请求。如果只有大多数节点响应成功,则写入请求将成功,并向客户端发送成功的返回码。否则,写入请求将会失败。严格多数节点称为Quorum。
ZooKeeper 集合中的节点
让我们分析一下 ZooKeeper 集合中具有不同数量的节点的效果。
如果我们有一个节点,那么当该节点失败时,ZooKeeper 整体也会失败。它会导致“单点故障”,因此不建议在生产环境中使用。
如果我们有两个节点并且一个节点发生故障,那么我们也没有多数,因为二分之一不是多数。
如果我们有三个节点并且一个节点发生故障,那么我们就拥有多数,因此这是最低要求。ZooKeeper 整体在实时生产环境中必须至少具有三个节点。
如果我们有四个节点,其中两个节点失败,它会再次失败,这与三个节点类似。额外的节点没有任何用途,因此最好添加奇数个节点,例如 3、5、7。
我们知道,在 ZooKeeper 集合中,写入过程比读取过程昂贵,因为所有节点都需要在其数据库中写入相同的数据。因此,对于平衡环境来说,拥有较少数量的节点(3、5 或 7)比拥有大量节点更好。
下图描述了 ZooKeeper 工作流程,随后的表格解释了其不同的组件。
成分 | 描述 |
---|---|
写 | 写入过程由领导节点处理。Leader 将写请求转发给所有 znode,并等待 znode 的答复。如果一半的 znode 回复,则写入过程完成。 |
读 | 读取由特定连接的 znode 在内部执行,因此无需与集群交互。 |
复制数据库 | 它用于在zookeeper中存储数据。每个znode都有自己的数据库,并且在一致性的帮助下每个znode每次都有相同的数据。 |
领导者 | Leader是负责处理写请求的Znode。 |
追随者 | 追随者接收来自客户端的写入请求并将其转发给领导者 znode。 |
请求处理器 | 仅存在于领导节点中。它管理来自跟随者节点的写入请求。 |
Atomics广播 | 负责将领导节点的变化广播到跟随节点。 |
Zookeeper - 领导者选举
让我们分析一下在 ZooKeeper 集合中如何选举领导节点。考虑集群中有N个节点。领导者选举的过程如下 -
所有节点都会创建一个具有相同路径/app/leader_election/guid_的连续的临时 znode 。
ZooKeeper ensemble 会将 10 位序列号附加到路径中,创建的 znode 将为/app/leader_election/guid_0000000001、/app/leader_election/guid_0000000002等。
对于给定的实例,在 znode 中创建最小数量的节点成为领导者,所有其他节点都是追随者。
每个追随者节点都会监视编号次小的 znode。例如,创建 znode /app/leader_election/guid_0000000008的节点将监视 znode /app/leader_election/guid_0000000007,创建 znode /app/leader_election/guid_0000000007 的节点将监视 znode /app/leader_election/guid_0000000006。
如果领导者宕机,则其对应的 znode /app/leader_electionN将被删除。
下一个跟随者节点将通过观察者获得有关领导者删除的通知。
下一个跟随节点将检查是否还有其他具有最小编号的 znode。如果没有,那么它将承担领导者的角色。否则,它会找到创建编号最小的 znode 的节点作为领导者。
同样,所有其他follower节点选举创建编号最小的znode的节点作为leader。
从头开始进行领导选举是一个复杂的过程。但 ZooKeeper 服务使这一切变得非常简单。让我们在下一章继续安装用于开发目的的 ZooKeeper。
Zookeeper - 安装
在安装 ZooKeeper 之前,请确保您的系统正在以下任何操作系统上运行 -
任何 Linux 操作系统- 支持开发和部署。它是演示应用程序的首选。
Windows 操作系统- 仅支持开发。
Mac OS - 仅支持开发。
ZooKeeper 服务器是用 Java 创建的,并在 JVM 上运行。您需要使用 JDK 6 或更高版本。
现在,按照下面给出的步骤在您的计算机上安装 ZooKeeper 框架。
第 1 步:验证 Java 安装
我们相信您的系统上已经安装了 Java 环境。只需使用以下命令验证即可。
$ java -version
如果您的计算机上安装了 Java,那么您可以看到已安装 Java 的版本。否则,请按照下面给出的简单步骤安装最新版本的 Java。
步骤1.1:下载JDK
通过访问以下链接下载最新版本的JDK并下载最新版本。爪哇
最新版本(编写本教程时)是 JDK 8u 60,文件是“jdk-8u60-linuxx64.tar.gz”。请将文件下载到您的计算机上。
步骤1.2:解压文件
通常,文件会下载到下载文件夹中。验证它并使用以下命令提取 tar 设置。
$ cd /go/to/download/path $ tar -zxf jdk-8u60-linux-x64.gz
步骤1.3:移动到opt目录
要使 Java 对所有用户可用,请将提取的 java 内容移动到“/usr/local/java”文件夹。
$ su password: (type password of root user) $ mkdir /opt/jdk $ mv jdk-1.8.0_60 /opt/jdk/
步骤1.4:设置路径
要设置路径和 JAVA_HOME 变量,请将以下命令添加到 ~/.bashrc 文件中。
export JAVA_HOME = /usr/jdk/jdk-1.8.0_60 export PATH=$PATH:$JAVA_HOME/bin
现在,将所有更改应用到当前正在运行的系统中。
$ source ~/.bashrc
步骤 1.5:Java 替代方案
使用以下命令更改 Java 替代项。
update-alternatives --install /usr/bin/java java /opt/jdk/jdk1.8.0_60/bin/java 100
步骤1.6
使用步骤 1 中说明的验证命令(java -version)验证 Java 安装。
第2步:ZooKeeper框架安装
步骤2.1:下载ZooKeeper
要在您的计算机上安装 ZooKeeper 框架,请访问以下链接并下载最新版本的 ZooKeeper。http://zookeeper.apache.org/releases.html
截至目前,ZooKeeper的最新版本是3.4.6(ZooKeeper-3.4.6.tar.gz)。
步骤2.2:解压tar文件
使用以下命令提取 tar 文件 -
$ cd opt/ $ tar -zxf zookeeper-3.4.6.tar.gz $ cd zookeeper-3.4.6 $ mkdir data
步骤2.3:创建配置文件
使用命令vi conf/zoo.cfg打开名为 conf/zoo.cfg 的配置文件,并将以下所有参数设置为起点。
$ vi conf/zoo.cfg tickTime = 2000 dataDir = /path/to/zookeeper/data clientPort = 2181 initLimit = 5 syncLimit = 2
配置文件保存成功后,再次返回终端。您现在可以启动 Zookeeper 服务器。
步骤2.4:启动ZooKeeper服务器
执行以下命令 -
$ bin/zkServer.sh start
执行此命令后,您将得到如下响应 -
$ JMX enabled by default $ Using config: /Users/../zookeeper-3.4.6/bin/../conf/zoo.cfg $ Starting zookeeper ... STARTED
步骤 2.5:启动 CLI
输入以下命令 -
$ bin/zkCli.sh
输入上述命令后,您将连接到 ZooKeeper 服务器,并且您应该得到以下响应。
Connecting to localhost:2181 ................ ................ ................ Welcome to ZooKeeper! ................ ................ WATCHER:: WatchedEvent state:SyncConnected type: None path:null [zk: localhost:2181(CONNECTED) 0]
停止 ZooKeeper 服务器
连接服务器并执行所有操作后,您可以使用以下命令停止zookeeper服务器。
$ bin/zkServer.sh stop
动物园管理员 - CLI
ZooKeeper 命令行界面 (CLI) 用于与 ZooKeeper 整体进行交互以实现开发目的。它对于调试和使用不同的选项非常有用。
要执行 ZooKeeper CLI 操作,首先打开 ZooKeeper 服务器(“bin/zkServer.sh start”),然后打开 ZooKeeper 客户端(“bin/zkCli.sh”)。客户端启动后,您可以执行以下操作 -
- 创建 znode
- 获取数据
- 观察 znode 的变化
- 设置数据
- 创建 znode 的子节点
- 列出 znode 的子节点
- 检查状态
- 移除/删除 znode
现在让我们通过一个例子来一一看看上面的命令。
创建Z节点
使用给定路径创建一个 znode。flag参数指定创建的 znode 是短暂的、持久的还是顺序的。默认情况下,所有 znode 都是持久的。
当会话过期或客户端断开连接时,临时 znode (标志:e)将被自动删除。
顺序 znodes保证 znode 路径是唯一的。
ZooKeeper ensemble 将向 znode 路径添加序列号和 10 位数字填充。例如,znode 路径/myapp 将转换为 /myapp0000000001,下一个序列号将是/myapp0000000002。如果未指定标志,则该 znode 被视为持久节点。
句法
create /path /data
样本
create /FirstZnode “Myfirstzookeeper-app”
输出
[zk: localhost:2181(CONNECTED) 0] create /FirstZnode “Myfirstzookeeper-app” Created /FirstZnode
要创建顺序 znode,请添加-s 标志,如下所示。
句法
create -s /path /data
样本
create -s /FirstZnode second-data
输出
[zk: localhost:2181(CONNECTED) 2] create -s /FirstZnode “second-data” Created /FirstZnode0000000023
要创建临时 Znode,请添加-e 标志,如下所示。
句法
create -e /path /data
样本
create -e /SecondZnode “Ephemeral-data”
输出
[zk: localhost:2181(CONNECTED) 2] create -e /SecondZnode “Ephemeral-data” Created /SecondZnode
请记住,当客户端连接丢失时,临时 znode 将被删除。您可以通过退出 ZooKeeper CLI 然后重新打开 CLI 来尝试。
获取数据
它返回znode的关联数据和指定znode的元数据。您将获得诸如数据上次修改时间、修改位置以及有关数据的信息等信息。此 CLI 还用于分配监视以显示有关数据的通知。
句法
get /path
样本
get /FirstZnode
输出
[zk: localhost:2181(CONNECTED) 1] get /FirstZnode “Myfirstzookeeper-app” cZxid = 0x7f ctime = Tue Sep 29 16:15:47 IST 2015 mZxid = 0x7f mtime = Tue Sep 29 16:15:47 IST 2015 pZxid = 0x7f cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 22 numChildren = 0
要访问顺序 znode,您必须输入 znode 的完整路径。
样本
get /FirstZnode0000000023
输出
[zk: localhost:2181(CONNECTED) 1] get /FirstZnode0000000023 “Second-data” cZxid = 0x80 ctime = Tue Sep 29 16:25:47 IST 2015 mZxid = 0x80 mtime = Tue Sep 29 16:25:47 IST 2015 pZxid = 0x80 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 13 numChildren = 0
手表
当指定的 znode 或 znode 的子节点数据发生更改时,监视会显示通知。您只能在get命令中设置监视。
句法
get /path [watch] 1
样本
get /FirstZnode 1
输出
[zk: localhost:2181(CONNECTED) 1] get /FirstZnode 1 “Myfirstzookeeper-app” cZxid = 0x7f ctime = Tue Sep 29 16:15:47 IST 2015 mZxid = 0x7f mtime = Tue Sep 29 16:15:47 IST 2015 pZxid = 0x7f cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 22 numChildren = 0
输出与普通get命令类似,但它会在后台等待 znode 更改。<从这里开始>
设置数据
设置指定znode的数据。完成此设置操作后,您可以使用get CLI 命令检查数据。
句法
set /path /data
样本
set /SecondZnode Data-updated
输出
[zk: localhost:2181(CONNECTED) 1] get /SecondZnode “Data-updated” cZxid = 0x82 ctime = Tue Sep 29 16:29:50 IST 2015 mZxid = 0x83 mtime = Tue Sep 29 16:29:50 IST 2015 pZxid = 0x82 cversion = 0 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0x15018b47db00000 dataLength = 14 numChildren = 0
如果您在get命令中指定了watch选项(如上一个命令),那么输出将类似于如下所示 -
输出
[zk: localhost:2181(CONNECTED) 1] get /FirstZnode “Mysecondzookeeper-app” WATCHER: : WatchedEvent state:SyncConnected type:NodeDataChanged path:/FirstZnode cZxid = 0x7f ctime = Tue Sep 29 16:15:47 IST 2015 mZxid = 0x84 mtime = Tue Sep 29 17:14:47 IST 2015 pZxid = 0x7f cversion = 0 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 23 numChildren = 0
创建子节点/子节点
创建子节点类似于创建新的 znode。唯一的区别是子 znode 的路径也将具有父路径。
句法
create /parent/path/subnode/path /data
样本
create /FirstZnode/Child1 firstchildren
输出
[zk: localhost:2181(CONNECTED) 16] create /FirstZnode/Child1 “firstchildren” created /FirstZnode/Child1 [zk: localhost:2181(CONNECTED) 17] create /FirstZnode/Child2 “secondchildren” created /FirstZnode/Child2
列出孩子
该命令用于列出并显示znode 的子节点。
句法
ls /path
样本
ls /MyFirstZnode
输出
[zk: localhost:2181(CONNECTED) 2] ls /MyFirstZnode [mysecondsubnode, myfirstsubnode]
检查状态
状态描述了指定znode的元数据。它包含时间戳、版本号、ACL、数据长度和子节点等详细信息。
句法
stat /path
样本
stat /FirstZnode
输出
[zk: localhost:2181(CONNECTED) 1] stat /FirstZnode cZxid = 0x7f ctime = Tue Sep 29 16:15:47 IST 2015 mZxid = 0x7f mtime = Tue Sep 29 17:14:24 IST 2015 pZxid = 0x7f cversion = 0 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 23 numChildren = 0
删除一个Z节点
删除指定的 znode 并递归删除其所有子节点。仅当这样的 znode 可用时才会发生这种情况。
句法
rmr /path
样本
rmr /FirstZnode
输出
[zk: localhost:2181(CONNECTED) 10] rmr /FirstZnode [zk: localhost:2181(CONNECTED) 11] get /FirstZnode Node does not exist: /FirstZnode
删除(delete /path)命令与删除命令类似,但它仅适用于没有子节点的 znode。
动物园管理员 - API
ZooKeeper 有针对 Java 和 C 的官方 API 绑定。ZooKeeper 社区为大多数语言(.NET、Python 等)提供非官方 API。使用 ZooKeeper API,应用程序可以连接、交互、操作数据、协调并最终断开与 ZooKeeper 整体的连接。
ZooKeeper API 具有一组丰富的功能,可以以简单且安全的方式获取 ZooKeeper 整体的所有功能。ZooKeeper API 提供同步和异步方法。
ZooKeeper ensemble 和 ZooKeeper API 在各个方面都完全互补,这给开发人员带来了很大的好处。让我们在本章中讨论 Java 绑定。
ZooKeeper API 基础知识
与 ZooKeeper 整体交互的应用程序称为ZooKeeper Client或简称为Client。
Znode 是 ZooKeeper ensemble 的核心组件,ZooKeeper API 提供了一小组方法来通过 ZooKeeper ensemble 操作 znode 的所有细节。
客户端应按照下面给出的步骤与 ZooKeeper 整体进行清晰、干净的交互。
连接到 ZooKeeper 整体。ZooKeeper 整体为客户端分配一个会话 ID。
定期向服务器发送心跳。否则,ZooKeeper 整体会使会话 ID 过期,并且客户端需要重新连接。
只要会话 ID 处于活动状态,就获取/设置 znode。
所有任务完成后,断开与 ZooKeeper 整体的连接。如果客户端长时间不活动,ZooKeeper ensemble 将自动断开客户端连接。
Java 绑定
让我们了解本章中最重要的一组 ZooKeeper API。ZooKeeper API 的核心部分是ZooKeeper 类。它提供了在其构造函数中连接 ZooKeeper 整体的选项,并具有以下方法 -
连接- 连接到 ZooKeeper 整体
创建- 创建一个 znode
存在- 检查 znode 是否存在及其信息
getData - 从特定 znode 获取数据
setData - 在特定 znode 中设置数据
getChildren - 获取特定 znode 中可用的所有子节点
删除- 获取特定的 znode 及其所有子节点
close - 关闭连接
连接到 ZooKeeper Ensemble
ZooKeeper 类通过其构造函数提供连接功能。构造函数的签名如下 -
ZooKeeper(String connectionString, int sessionTimeout, Watcher watcher)
在哪里,
connectionString - ZooKeeper 整体主机。
sessionTimeout - 会话超时(以毫秒为单位)。
watcher - 实现“Watcher”接口的对象。ZooKeeper 整体通过观察者对象返回连接状态。
让我们创建一个新的帮助器类ZooKeeperConnection并添加一个方法connect。connect方法创建一个 ZooKeeper 对象,连接到 ZooKeeper 集合,然后返回该对象。
这里CountDownLatch用于停止(等待)主进程,直到客户端与 ZooKeeper 整体连接。
ZooKeeper 整体通过Watcher 回调回复连接状态。一旦客户端连接到 ZooKeeper ensemble,Watcher 回调就会被调用,Watcher 回调会调用CountDownLatch的countDown方法来释放锁,在主进程中等待。
以下是与 ZooKeeper 整体连接的完整代码。
编码:ZooKeeperConnection.java
// import java classes import java.io.IOException; import java.util.concurrent.CountDownLatch; // import zookeeper classes import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.AsyncCallback.StatCallback; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.data.Stat; public class ZooKeeperConnection { // declare zookeeper instance to access ZooKeeper ensemble private ZooKeeper zoo; final CountDownLatch connectedSignal = new CountDownLatch(1); // Method to connect zookeeper ensemble. public ZooKeeper connect(String host) throws IOException,InterruptedException { zoo = new ZooKeeper(host,5000,new Watcher() { public void process(WatchedEvent we) { if (we.getState() == KeeperState.SyncConnected) { connectedSignal.countDown(); } } }); connectedSignal.await(); return zoo; } // Method to disconnect from zookeeper server public void close() throws InterruptedException { zoo.close(); } }
保存上面的代码,它将在下一节中用于连接 ZooKeeper 整体。
创建一个Z节点
ZooKeeper 类提供了create 方法来在 ZooKeeper 集合中创建新的 znode。创建方法的签名如下 -
create(String path, byte[] data, List<ACL> acl, CreateMode createMode)
在哪里,
路径- Znode 路径。例如,/myapp1、/myapp2、/myapp1/mydata1、myapp2/mydata1/myanothersubdata
data - 存储在指定 znode 路径中的数据
acl - 要创建的节点的访问控制列表。ZooKeeper API提供了一个静态接口ZooDefs.Ids来获取一些基本的acl列表。例如,ZooDefs.Ids.OPEN_ACL_UNSAFE 返回打开的 znode 的 acl 列表。
createMode - 节点的类型,可以是临时的,顺序的,或两者兼而有之。这是一个枚举。
让我们创建一个新的 Java 应用程序来检查ZooKeeper API 的创建功能。创建文件ZKCreate.java。在 main 方法中,创建一个ZooKeeperConnection类型的对象并调用connect方法来连接到 ZooKeeper ensemble。
connect 方法将返回 ZooKeeper 对象zk。现在,使用自定义路径和数据调用zk对象的create方法。
创建 znode 的完整程序代码如下 -
编码:ZKCreate.java
import java.io.IOException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs; public class ZKCreate { // create static instance for zookeeper class. private static ZooKeeper zk; // create static instance for ZooKeeperConnection class. private static ZooKeeperConnection conn; // Method to create znode in zookeeper ensemble public static void create(String path, byte[] data) throws KeeperException,InterruptedException { zk.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } public static void main(String[] args) { // znode path String path = "/MyFirstZnode"; // Assign path to znode // data in byte array byte[] data = "My first zookeeper app”.getBytes(); // Declare data try { conn = new ZooKeeperConnection(); zk = conn.connect("localhost"); create(path, data); // Create the data to the specified path conn.close(); } catch (Exception e) { System.out.println(e.getMessage()); //Catch error message } } }
一旦应用程序被编译并执行,将在 ZooKeeper 集合中创建具有指定数据的 znode。您可以使用 ZooKeeper CLI zkCli.sh检查它。
cd /path/to/zookeeper bin/zkCli.sh >>> get /MyFirstZnode
Exists – 检查 Znode 是否存在
ZooKeeper类提供了exists方法来检查znode是否存在。如果指定的 znode 存在,它将返回 znode 的元数据。存在方法的签名如下 -
exists(String path, boolean watcher)
在哪里,
路径- Znode 路径
watcher - 布尔值来指定是否监视指定的 znode
让我们创建一个新的 Java 应用程序来检查 ZooKeeper API 的“存在”功能。创建文件“ZKExists.java”。在 main 方法中,使用“ZooKeeperConnection”对象创建 ZooKeeper 对象“zk”。然后,使用自定义“path”调用“zk”对象的“exists”方法。完整列表如下 -
编码:ZKExists.java
import java.io.IOException; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.data.Stat; public class ZKExists { private static ZooKeeper zk; private static ZooKeeperConnection conn; // Method to check existence of znode and its status, if znode is available. public static Stat znode_exists(String path) throws KeeperException,InterruptedException { return zk.exists(path, true); } public static void main(String[] args) throws InterruptedException,KeeperException { String path = "/MyFirstZnode"; // Assign znode to the specified path try { conn = new ZooKeeperConnection(); zk = conn.connect("localhost"); Stat stat = znode_exists(path); // Stat checks the path of the znode if(stat != null) { System.out.println("Node exists and the node version is " + stat.getVersion()); } else { System.out.println("Node does not exists"); } } catch(Exception e) { System.out.println(e.getMessage()); // Catches error messages } } }
编译并执行应用程序后,您将得到以下输出。
Node exists and the node version is 1.
获取数据方法
ZooKeeper类提供了getData方法来获取指定znode中附加的数据及其状态。getData方法的签名如下 -
getData(String path, Watcher watcher, Stat stat)
在哪里,
路径- Znode 路径。
watcher - Watcher类型的回调函数。当指定 znode 的数据发生变化时,ZooKeeper ensemble 将通过 Watcher 回调进行通知。这是一次性通知。
stat - 返回 znode 的元数据。
让我们创建一个新的 Java 应用程序来了解ZooKeeper API 的getData功能。创建文件ZKGetData.java。在main方法中,使用ZooKeeperConnection对象创建一个ZooKeeper对象zk。然后,使用自定义路径调用zk对象的getData方法。
这是从指定节点获取数据的完整程序代码 -
编码:ZKGetData.java
import java.io.IOException; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.data.Stat; public class ZKGetData { private static ZooKeeper zk; private static ZooKeeperConnection conn; public static Stat znode_exists(String path) throws KeeperException,InterruptedException { return zk.exists(path,true); } public static void main(String[] args) throws InterruptedException, KeeperException { String path = "/MyFirstZnode"; final CountDownLatch connectedSignal = new CountDownLatch(1); try { conn = new ZooKeeperConnection(); zk = conn.connect("localhost"); Stat stat = znode_exists(path); if(stat != null) { byte[] b = zk.getData(path, new Watcher() { public void process(WatchedEvent we) { if (we.getType() == Event.EventType.None) { switch(we.getState()) { case Expired: connectedSignal.countDown(); break; } } else { String path = "/MyFirstZnode"; try { byte[] bn = zk.getData(path, false, null); String data = new String(bn, "UTF-8"); System.out.println(data); connectedSignal.countDown(); } catch(Exception ex) { System.out.println(ex.getMessage()); } } } }, null); String data = new String(b, "UTF-8"); System.out.println(data); connectedSignal.await(); } else { System.out.println("Node does not exists"); } } catch(Exception e) { System.out.println(e.getMessage()); } } }
编译并执行应用程序后,您将得到以下输出
My first zookeeper app
应用程序将等待 ZooKeeper 整体的进一步通知。使用 ZooKeeper CLI zkCli.sh更改指定 znode 的数据。
cd /path/to/zookeeper bin/zkCli.sh >>> set /MyFirstZnode Hello
现在,应用程序将打印以下输出并退出。
Hello
设置数据方法
ZooKeeper类提供setData方法来修改指定znode中附加的数据。setData方法的签名如下 -
setData(String path, byte[] data, int version)
在哪里,
路径- Znode 路径
data - 存储在指定 znode 路径中的数据。
version - znode 的当前版本。每当数据发生更改时,ZooKeeper 都会更新 znode 的版本号。
现在让我们创建一个新的 Java 应用程序来了解ZooKeeper API 的setData功能。创建文件ZKSetData.java。在main方法中,使用ZooKeeperConnection对象创建一个ZooKeeper对象zk。然后,使用指定的路径、新数据和节点版本来调用zk对象的setData方法。
以下是修改指定 znode 中附加数据的完整程序代码。
代码:ZKSetData.java
import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.KeeperState; import java.io.IOException; public class ZKSetData { private static ZooKeeper zk; private static ZooKeeperConnection conn; // Method to update the data in a znode. Similar to getData but without watcher. public static void update(String path, byte[] data) throws KeeperException,InterruptedException { zk.setData(path, data, zk.exists(path,true).getVersion()); } public static void main(String[] args) throws InterruptedException,KeeperException { String path= "/MyFirstZnode"; byte[] data = "Success".getBytes(); //Assign data which is to be updated. try { conn = new ZooKeeperConnection(); zk = conn.connect("localhost"); update(path, data); // Update znode data to the specified path } catch(Exception e) { System.out.println(e.getMessage()); } } }
一旦应用程序被编译并执行,指定 znode 的数据将被更改,并且可以使用 ZooKeeper CLI zkCli.sh进行检查。
cd /path/to/zookeeper bin/zkCli.sh >>> get /MyFirstZnode
获取子级方法
ZooKeeper类提供了getChildren方法来获取特定znode的所有子节点。getChildren方法的签名如下 -
getChildren(String path, Watcher watcher)
在哪里,
路径- Znode 路径。
watcher - “Watcher”类型的回调函数。当指定的 znode 被删除或 znode 下的子节点被创建/删除时,ZooKeeper 集合将发出通知。这是一次性通知。
编码:ZKGetChildren.java
import java.io.IOException; import java.util.*; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.data.Stat; public class ZKGetChildren { private static ZooKeeper zk; private static ZooKeeperConnection conn; // Method to check existence of znode and its status, if znode is available. public static Stat znode_exists(String path) throws KeeperException,InterruptedException { return zk.exists(path,true); } public static void main(String[] args) throws InterruptedException,KeeperException { String path = "/MyFirstZnode"; // Assign path to the znode try { conn = new ZooKeeperConnection(); zk = conn.connect("localhost"); Stat stat = znode_exists(path); // Stat checks the path if(stat!= null) { //“getChildren” method- get all the children of znode.It has two args, path and watch List <String> children = zk.getChildren(path, false); for(int i = 0; i < children.size(); i++) System.out.println(children.get(i)); //Print children's } else { System.out.println("Node does not exists"); } } catch(Exception e) { System.out.println(e.getMessage()); } } }
在运行程序之前,让我们使用 ZooKeeper CLI zkCli.sh为/MyFirstZnode创建两个子节点。
cd /path/to/zookeeper bin/zkCli.sh >>> create /MyFirstZnode/myfirstsubnode Hi >>> create /MyFirstZnode/mysecondsubmode Hi
现在,编译并运行程序将输出上面创建的 znode。
myfirstsubnode mysecondsubnode
删除一个Z节点
ZooKeeper类提供了delete方法来删除指定的znode。删除方法的签名如下 -
delete(String path, int version)
在哪里,
路径- Znode 路径。
version - znode 的当前版本。
让我们创建一个新的 Java 应用程序来了解ZooKeeper API 的删除功能。创建文件ZKDelete.java。在main方法中,使用ZooKeeperConnection对象创建一个ZooKeeper对象zk。然后,调用指定节点路径和版本的zk对象的delete方法。
删除 znode 的完整程序代码如下 -
编码:ZKDelete.java
import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.KeeperException; public class ZKDelete { private static ZooKeeper zk; private static ZooKeeperConnection conn; // Method to check existence of znode and its status, if znode is available. public static void delete(String path) throws KeeperException,InterruptedException { zk.delete(path,zk.exists(path,true).getVersion()); } public static void main(String[] args) throws InterruptedException,KeeperException { String path = "/MyFirstZnode"; //Assign path to the znode try { conn = new ZooKeeperConnection(); zk = conn.connect("localhost"); delete(path); //delete the node with the specified path } catch(Exception e) { System.out.println(e.getMessage()); // catches error messages } } }
Zookeeper - 应用程序
Zookeeper为分布式环境提供了灵活的协调基础设施。ZooKeeper 框架支持当今许多最好的工业应用程序。我们将在本章中讨论 ZooKeeper 的一些最著名的应用程序。
雅虎!
ZooKeeper 框架最初是在“Yahoo!”构建的。一个设计良好的分布式应用程序需要满足数据透明、更好的性能、健壮性、集中配置和协调等要求。因此,他们设计了ZooKeeper框架来满足这些需求。
阿帕奇Hadoop
Apache Hadoop 是大数据行业增长的驱动力。Hadoop 依赖 ZooKeeper 进行配置管理和协调。让我们通过一个场景来了解ZooKeeper在Hadoop中的作用。
假设Hadoop 集群桥接100 台或更多商用服务器。因此,需要协调和命名服务。由于涉及大量节点的计算,每个节点需要相互同步,知道从哪里访问服务,并知道如何配置它们。此时,Hadoop集群需要跨节点服务。ZooKeeper 提供跨节点同步的工具,并确保跨 Hadoop 项目的任务被序列化和同步。
多个 ZooKeeper 服务器支持大型 Hadoop 集群。每台客户端计算机都与其中一台 ZooKeeper 服务器通信,以检索和更新其同步信息。一些实时示例是 -
人类基因组计划- 人类基因组计划包含数 TB 的数据。Hadoop MapReduce 框架可用于分析数据集并发现人类发展的有趣事实。
医疗保健- 医院可以存储、检索和分析大量患者医疗记录,这些记录通常以 TB 为单位。
阿帕奇HBase
Apache HBase 是一个开源、分布式、NoSQL 数据库,用于大型数据集的实时读/写访问,并运行在 HDFS 之上。HBase遵循主从架构,其中HBase Master管理所有从机。从属服务器称为区域服务器。
HBase 分布式应用程序安装依赖于正在运行的 ZooKeeper 集群。Apache HBase 使用 ZooKeeper 借助集中式配置管理和分布式互斥机制来跟踪主服务器和区域服务器中分布式数据的状态。以下是 HBase 的一些用例 -
电信- 电信行业存储了数十亿的移动通话记录(约 30TB/月),实时访问这些通话记录成为一项艰巨的任务。HBase可以实时、轻松、高效地处理所有记录。
社交网络- 与电信行业类似,Twitter、LinkedIn 和 Facebook 等网站通过用户创建的帖子接收大量数据。HBase 可用于查找最新趋势和其他有趣的事实。
阿帕奇·索尔
Apache Solr 是一个用 Java 编写的快速开源搜索平台。它是一个速度极快、容错的分布式搜索引擎。它建立在Lucene之上,是一个高性能、全功能的文本搜索引擎。
Solr 广泛使用了 ZooKeeper 的所有功能,例如配置管理、Leader 选举、节点管理、数据锁定和同步。
Solr 有两个不同的部分:索引和搜索。索引是以适当的格式存储数据以便以后搜索的过程。Solr 使用 ZooKeeper 对多个节点中的数据进行索引并从多个节点进行搜索。ZooKeeper 贡献以下功能 -
根据需要添加/删除节点
在节点之间复制数据,从而最大限度地减少数据丢失
在多个节点之间共享数据并随后从多个节点进行搜索以获得更快的搜索结果
Apache Solr 的一些用例包括电子商务、求职等。