Zookeeper 简明教程

Zookeeper - Quick Guide

Zookeeper - Overview

ZooKeeper 是一种分布式协调服务,用于管理大量主机。在分布式环境中协调和管理服务是一个复杂的过程。ZooKeeper 通过其简单的架构和 API 解决此问题。ZooKeeper 使开发人员能够专注于核心应用程序逻辑,而不必担心应用程序的分布式性质。

ZooKeeper 框架最初是在“Yahoo!”构建的,目的是以一种简单而稳健的方式访问其应用程序。后来,Apache ZooKeeper 成为 Hadoop、HBase 和其他分布式框架使用的有组织服务的标准。例如,Apache HBase 使用 ZooKeeper 来跟踪分布式数据的状态。

在我们继续之前,了解分布式应用程序的一些内容非常重要。那么,让我们以分布式应用程序的快速概述开始讨论。

Distributed Application

分布式应用程序可以通过在它们之间进行协调来同时在网络中的多个系统上以快速高效的方式运行,以完成一项特定任务。通常,分布式应用程序可以通过使用所有涉及的系统的计算能力,在数分钟内完成一项复杂且耗时的任务,而一个非分布式应用程序(在一个系统中运行)完成该任务需要数小时。

通过配置分布式应用程序在更多系统上运行可以进一步缩短完成任务的时间。一个分布式应用程序正在运行的系统组称为 Cluster ,在集群中运行的每台机器称为 Node

一个分布式应用程序有两部分, ServerClient 应用程序。服务器应用程序实际上是分布式的,并具有一个共同的接口,以便客户端可以连接到集群中的任何一台服务器并获取相同的结果。客户端应用程序是与分布式应用程序进行交互的工具。

distributed application

Benefits of Distributed Applications

  1. Reliability − 单个或少数系统故障不会导致整个系统故障。

  2. Scalability − 可以在需要时通过在应用程序配置中添加更多机器来提高性能,而无需停机。

  3. Transparency − 隐藏了系统的复杂性,并展示为一个实体/应用程序。

Challenges of Distributed Applications

  1. Race condition − 两台或更多机器试图执行一项特定任务,而实际上在任何给定时间仅需一台机器执行该任务。例如,在任何给定时间仅应由一台机器修改共享资源。

  2. Deadlock − 两个或更多操作无限期地等待对方完成。

  3. Inconsistency − 数据部分故障。

What is Apache ZooKeeper Meant For?

Apache ZooKeeper 是一项服务,由集群(节点组)使用,用于在它们之间进行协调,并以稳健的同步技术维护共享数据。ZooKeeper 本身是一个分布式应用程序,它提供编写分布式应用程序的服务。

ZooKeeper 提供的常见服务如下所示 -

  1. Naming service − 按名称标识集群中的节点。它类似于 DNS,但适用于节点。

  2. Configuration management − 最新且最新的系统配置信息,用于加入节点。

  3. Cluster management − 加入/离开集群中的节点以及实时节点状态。

  4. Leader election − 选举一个节点作为领导者,以进行协调。

  5. Locking and synchronization service − 修改数据时锁定数据。此机制可帮助你在连接其他分布式应用程序(如 Apache HBase)时自动进行故障恢复。

  6. Highly reliable data registry − 即使一到几个节点宕机,也可以获得数据。

分布式应用程序提供了很多好处,但也带来了一些复杂且难以破解的挑战。ZooKeeper 框架提供了一种完整的机制来克服所有挑战。竞态条件和死锁使用 fail-safe synchronization approach 处理。另一个主要缺点是数据不一致,ZooKeeper 使用 atomicity 解决此问题。

Benefits of ZooKeeper

以下是在使用 ZooKeeper 时的优点 -

  1. Simple distributed coordination process

  2. Synchronization − 服务器进程之间的互斥和协作。此过程有助于 Apache HBase 进行配置管理。

  3. Ordered Messages

  4. Serialization − 根据特定的规则对数据编码。确保您的应用程序一致运行。此方法可用于 MapReduce 中,以协调队列来执行正在运行的线程。

  5. Reliability

  6. Atomicity − 数据传输要么完全成功,要么完全失败,但任何事务都不是部分的。

Zookeeper - Fundamentals

在深入探讨 ZooKeeper 的工作原理之前,让我们了解一下 ZooKeeper 的基本概念。在本章中,我们将讨论以下主题:

  1. Architecture

  2. Hierarchical namespace

  3. Session

  4. Watches

Architecture of ZooKeeper

请看以下图表。它描述了 ZooKeeper 的“客户端-服务器架构”。

architecture of zookeeper

ZooKeeper 架构的一部分的每个组件在以下表格中都有说明。

Part

Description

Client

客户机,作为分布式应用程序集群中的节点之一,从服务器访问信息。对于特定时间间隔,每个客户端会向服务器发送一条消息来让服务器知道该客户端是活动的。类似地,当客户端连接时,服务器会发送确认。如果连接的服务器没有响应,则客户端会自动将消息重定向到另一台服务器。

Server

服务器,作为 ZooKeeper 集群中的节点之一,向客户端提供所有服务。向客户端发送确认以通知其服务器是活动的。

Ensemble

ZooKeeper 服务器组。要形成集群所需的最小节点数为 3。

Leader

如果任何连接的节点发生故障,则执行自动恢复的服务器节点。领导者在服务启动时被选出。

Follower

遵循领导者指令的服务器节点。

Hierarchical Namespace

以下图表描述了用于内存表示的 ZooKeeper 文件系统的树结构。ZooKeeper 节点称为 znode 。每个 znode 由一个名称标识并由路径(/)序列分隔。

  1. 在图表中,您首先有一个根 znode ,用“/”分隔。在根下,您有两个逻辑名称空间 configworkers

  2. config 命名空间用于集中配置管理, workers 命名空间用于命名。

  3. config 命名空间下,每个 znode 可以最多存储 1MB 的数据。这与 UNIX 文件系统类似,但不同之处在于父 znode 也能存储数据。这种结构的主要目的是为了存储同步数据并描述 znode 的元数据。该结构被称为 ZooKeeper Data Model

hierarchical namespace

ZooKeeper 数据模型中的每个 znode 都维护一个 stat 结构。一个状态信息简单地提供了 znode 的 metadata 。它包括版本号、操作控制列表 (ACL)、时间戳和数据长度。

  1. Version number − 每个 znode 有一个版本号,这意味着与 znode 关联的数据每次更改时,其相应的版本号也会增加。当多个 zookeeper 客户端尝试对同一个 znode 执行操作时,使用版本号很重要。

  2. Action Control List (ACL) − ACL 基本上是一种访问 znode 的身份验证机制。它管理所有 znode 的读取和写入操作。

  3. Timestamp − 时间戳表示从 znode 的创建和修改开始经过的时间。它通常以毫秒为单位表示。ZooKeeper 会通过“事务 ID”(zxid)识别对 znode 进行的每次更改。 Zxid 是唯一的,并为每个事务记录时间,这样便于轻松识别从一个请求到另一个请求之间经过的时间。

  4. Data length − znode 中存储的数据总量就是数据长度。您可以最多存储 1MB 的数据。

Types of Znodes

znode 被归类为持久性、顺序性和临时性。

  1. Persistence znode − 持久性 znode 即使创建它的客户端断开连接后仍然存在。默认情况下,所有 znode 都是持久性的,除非另有指定。

  2. Ephemeral znode − 临时 znode 在客户端处于活动状态时是处于活动状态的。当客户端与 ZooKeeper 集群断开连接时,临时 znode 将被自动删除。出于此原因,不允许只有临时 znode 再有子 znode。如果一个临时 znode 被删除,那么下一个合适的节点将填补它的位置。临时 znode 在领导者选举中发挥着重要作用。

  3. Sequential znode − 顺序 znode 可以是持久性的,也可以是临时的。当创建一个新 znode 为顺序 znode 时,ZooKeeper 通过将一个 10 位的序列号附加到原始名称来设置 znode 的路径。例如,如果路径为 /myapp 的 znode 被创建为顺序 znode,ZooKeeper 将把路径更改为 /myapp0000000001 ,并将下一个序列号设置为 0000000002。如果同时创建两个顺序 znode,那么 Zookeeper 永远不会为每个 znode 使用相同的号码。顺序 znode 在锁定和同步中发挥着重要作用。

Sessions

会话对于 ZooKeeper 的操作非常重要。会话中的请求按先进先出的顺序执行。客户端连接到服务器后,将建立会话,并为客户端分配一个会话 session id

客户端在特定时间间隔发送 heartbeats 以保持会话有效。如果 ZooKeeper 集群未从客户端收到心跳时间超过在服务启动时指定的周期(会话超时),它将判定客户端已死亡。

会话超时通常以毫秒为单位表示。当会话因任何原因而结束时,在此会话期间创建的临时 znode 也会被删除。

Watches

监控器是一种简单的机制,供客户端获得 ZooKeeper 集群中更改的通知。客户端可以在读取特定 znode 时设置监控。监控器向注册的客户端发送有关 znode(客户端上进行注册的 znode)中任何更改的通知。

Znode 更改是对与 znode 关联的数据的修改或对 znode 子项的修改。监控器只会触发一次。如果客户端再次需要通知,必须通过另一项读取操作来完成。当连接会话过期时,客户端将与服务器断开连接,关联的监控器也将被移除。

Zookeeper - Workflow

当 ZooKeeper 协同程序启动后,会等待客户端连接。客户端将连接到 ZooKeeper 协同程序中的一个节点。可以是领导者节点或追随者节点。一旦客户端连接,节点会为特定客户端分配会话 ID,并向客户端发送确认。如果客户端没有收到确认,它会尝试连接 ZooKeeper 协同程序中的另一个节点。连接到一个节点后,客户端会定期向该节点发送心跳,以确保连接不会丢失。

  1. If a client wants to read a particular znode, 它会向带有 z 节点路径的节点发送 read request ,而该节点会通过从自己的数据库中获取所需的 z 节点返回它。因此,读取操作在 ZooKeeper 协同程序中的速度很快。

  2. If a client wants to store data in the ZooKeeper ensemble ,它会向服务器发送 z 节点路径和数据。已连接的服务器会将请求转发给领导者,然后领导者会向所有追随者重新发出写入请求。如果只有大多数节点成功响应,则写入请求将成功并且会向客户端发送成功返回码。否则,写入请求将失败。大多数节点会称为 Quorum

Nodes in a ZooKeeper Ensemble

让我们分析同时在 ZooKeeper 协同程序中拥有不同数量的节点的影响。

  1. 如果我们有 a single node ,则当那个节点失败时,ZooKeeper 协同程序会失败。它会促成“单点故障”并且不推荐在生产环境中使用。

  2. 如果我们有 two nodes 并且一个节点失败,我们也没有大多数节点,因为二分之一不是大多数。

  3. 如果我们有 three nodes 并且一个节点失败,我们有大多数节点,因此,它是最低要求。ZooKeeper 协同程序必须在实时生产环境中至少有三个节点。

  4. 如果我们有 four nodes 并且两个节点失败,则会再次失败,这就像拥有三个节点一样。额外的节点没有任何用处,因此,最好以奇数添加节点,例如 3、5、7。

我们知道一个写入进程在 ZooKeeper 协同程序中的开销要大于一个读取进程,因为所有的节点都需要在其数据库中写入相同的数据。因此,为了实现一个均衡的环境,拥有较少数量的节点(3、5 或 7)要优于拥有大量节点。

下图描绘了 ZooKeeper 工作流,而随后的表格解释了其不同的组件。

zookeeper ensemble

Component

Description

Write

写入进程由领导者节点处理。领导者将写入请求转发给所有 z 节点并等待 z 节点的答复。如果一半的 z 节点回复,则写入进程完成。

Read

读取操作由一个特定的已连接 z 节点在内部执行,因此无需与集群交互。

Replicated Database

该操作用于在 zookeeper 中存储数据。每个 z 节点都有自己的数据库,在帮助下,每个 z 节点在同一时间都有相同的数据。一致性

Leader

领导者是负责处理写入请求的 z 节点。

Follower

追随者从客户端接收写入请求,并将它们转发给领导者 z 节点。

Request Processor

仅存在于领导者节点中。它控制来自追随者节点的写入请求。

Atomic broadcasts

负责将领导者节点的更改广播给追随者节点。

Zookeeper - Leader Election

让我们分析一下如何在 ZooKeeper 协同程序中选举一个领导者节点。考虑集群中存在 N 节点。领导者选举过程如下 −

  1. 所有节点创建一个具有相同路径的顺序的临时 znode, /app/leader_election/guid_

  2. ZooKeeper 组件会将 10 位序列号附加到路径上,创建的 znode 会是 /app/leader_election/guid_0000000001, /app/leader_election/guid_0000000002, 等。

  3. 对于给定实例,在 znode 中创建最小数字的节点将成为领导,其他所有节点将是跟随者。

  4. 每个跟随者节点都监视拥有下一个最小数字的 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.

  5. 如果领导宕机,那么其对应的 znode /app/leader_electionN 将会被删除。

  6. 下一个跟随者节点将收到有关领导删除事件的观察器通知。

  7. 下一个跟随者节点将检查是否存在拥有最小数字的其他 znode。如果没有,那么它将承担领导角色。否则,它会寻找创建具有最小数字的 znode 的节点作为领导。

  8. 同样,所有其他跟随者节点都将创建具有最小数字的 znode 的节点选为领导。

当从头开始时,领导选举是复杂的过程。但 ZooKeeper 服务使其非常简单。让我们在下一章转到用于开发目的的 ZooKeeper 安装。

Zookeeper - Installation

在安装 ZooKeeper 之前,请确保您的系统在下列任何操作系统上运行:

  1. Any of Linux OS - 支持开发和部署。适用于演示应用程序。

  2. Windows OS - 仅支持开发。

  3. Mac OS - 仅支持开发。

ZooKeeper 服务器在 Java 中创建,并在 JVM 上运行。您需要使用 JDK 6 或更高版本。

现在,按照以下步骤在您的机器上安装 ZooKeeper 框架。

Step 1: Verifying Java Installation

我们相信您已经在系统上安装了 Java 环境。只需使用以下命令进行验证。

$ java -version

如果您的机器上已安装 Java,那么您可以看到已安装 Java 的版本。否则,请按照以下简单步骤安装最新版本的 Java。

Step 1.1: Download JDK

访问以下链接并下载 Java 的最新版本。 Java

最新版本(在编写本教程时)是 JDK 8u 60,文件为“jdk-8u60-linuxx64.tar.gz”。请下载该文件到您的机器上。

Step 1.2: Extract the files

通常,文件会下载到 downloads 文件夹。验证此文件夹并使用以下命令提取tar设置。

$ cd /go/to/download/path
$ tar -zxf jdk-8u60-linux-x64.gz

Step 1.3: Move to opt directory

为了让所有用户都可以使用 Java,将提取的 Java 内容移动到“/usr/local/java”文件夹中。

$ su
password: (type password of root user)
$ mkdir /opt/jdk
$ mv jdk-1.8.0_60 /opt/jdk/

Step 1.4: Set path

若要设置 path 及 JAVA_HOME 变量,请将以下命令添加到 ~/.bashrc 文件中。

export JAVA_HOME = /usr/jdk/jdk-1.8.0_60
export PATH=$PATH:$JAVA_HOME/bin

现在,将所有更改应用到当前正在运行的系统中。

$ source ~/.bashrc

Step 1.5: Java alternatives

使用以下命令更改 Java 备用项。

update-alternatives --install /usr/bin/java java /opt/jdk/jdk1.8.0_60/bin/java 100

Step 1.6

使用验证命令 (java -version) (第1步中已说明)验证Java安装。

Step 2: ZooKeeper Framework Installation

Step 2.1: Download ZooKeeper

若要在你的机器上安装ZooKeeper框架,请访问以下链接并下载ZooKeeper的最新版本。 http://zookeeper.apache.org/releases.html

到目前为止,ZooKeeper 的最新版本是 3.4.6 (ZooKeeper-3.4.6.tar.gz)。

Step 2.2: Extract the tar file

使用以下命令提取 tar 文件 −

$ cd opt/
$ tar -zxf zookeeper-3.4.6.tar.gz
$ cd zookeeper-3.4.6
$ mkdir data

Step 2.3: Create configuration file

使用命令 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服务器。

Step 2.4: Start ZooKeeper server

执行以下命令−

$ bin/zkServer.sh start

执行该命令后,你会收到如下的响应 −

$ JMX enabled by default
$ Using config: /Users/../zookeeper-3.4.6/bin/../conf/zoo.cfg
$ Starting zookeeper ... STARTED

Step 2.5: Start 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]

Stop ZooKeeper Server

连接服务器并执行所有操作后,你可以使用以下命令停止zookeeper服务器。

$ bin/zkServer.sh stop

Zookeeper - CLI

ZooKeeper命令行界面(CLI)用于出于开发目的与ZooKeeper组合进行交互。这对于调试和与不同选项协同工作很有用。

若要执行ZooKeeper CLI操作,请首先启用ZooKeeper服务器(“bin/zkServer.sh start”),然后启用ZooKeeper客户端(“bin/zkCli.sh”)。客户端启动后,你可以执行以下操作−

  1. Create znodes

  2. Get data

  3. Watch znode for changes

  4. Set data

  5. 创建znode的子项

  6. 列出znode的子项

  7. Check Status

  8. 移除/删除一个znode

现在让我们逐个查看以上命令,并使用一个示例。

Create Znodes

使用给定的路径创建一个znode。 flag 参数指定创建的znode为临时性、持久性还是连续性。默认情况下,所有znode都是持久的。

  1. Ephemeral znodes (标志:e)将在会话过期或客户端断开连接时自动删除。

  2. Sequential znodes 保证znode路径的唯一性。

  3. ZooKeeper组合会将序列号与10位填充内容添加到znode路径。例如,znode路径/myapp将转换为/myapp0000000001,下一个序列号将是/myapp0000000002。如果未指定标志,那么znode将被视为 persistent

Syntax

create /path /data

Sample

create /FirstZnode “Myfirstzookeeper-app”

Output

[zk: localhost:2181(CONNECTED) 0] create /FirstZnode “Myfirstzookeeper-app”
Created /FirstZnode

要创建一个 Sequential znode ,添加 -s flag ,如下所示。

Syntax

create -s /path /data

Sample

create -s /FirstZnode second-data

Output

[zk: localhost:2181(CONNECTED) 2] create -s /FirstZnode “second-data”
Created /FirstZnode0000000023

要创建一个 Ephemeral Znode ,添加 -e flag ,如下所示。

Syntax

create -e /path /data

Sample

create -e /SecondZnode “Ephemeral-data”

Output

[zk: localhost:2181(CONNECTED) 2] create -e /SecondZnode “Ephemeral-data”
Created /SecondZnode

请记住,当客户端连接丢失时,短暂节点将被删除。你可以尝试退出 ZooKeeper CLI,然后重新打开 CLI,来进行验证。

Get Data

它返回与指定节点相关联的数据和元数据。你将获得一些信息,例如数据最后一次修改的时间、以及修改的地点,和有关数据的信息。此 CLI 还用于分配监视,以显示有关数据的通知。

Syntax

get /path

Sample

get /FirstZnode

Output

[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

要访问序列节点,你必须输入节点的完整路径。

Sample

get /FirstZnode0000000023

Output

[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

Watch

监视在指定节点或节点的子节点数据发生更改时显示通知。你只能在 get 命令中设置 watch

Syntax

get /path [watch] 1

Sample

get /FirstZnode 1

Output

[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 命令,但它将在后台等待节点更改。<从这里开始>

Set Data

设置指定节点的数据。完成此设置操作后,你可以使用 get CLI 命令检查数据。

Syntax

set /path /data

Sample

set /SecondZnode Data-updated

Output

[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 命令中分配了 children 选项(如前一个命令中所示),则输出将类似于如下所示:

Output

[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

Create Children / Sub-znode

创建子节点类似于创建新节点。唯一不同的是子节点的路径也包含父路径。

Syntax

create /parent/path/subnode/path /data

Sample

create /FirstZnode/Child1 firstchildren

Output

[zk: localhost:2181(CONNECTED) 16] create /FirstZnode/Child1 “firstchildren”
created /FirstZnode/Child1
[zk: localhost:2181(CONNECTED) 17] create /FirstZnode/Child2 “secondchildren”
created /FirstZnode/Child2

List Children

此命令用于列出和显示节点的 children

Syntax

ls /path

Sample

ls /MyFirstZnode

Output

[zk: localhost:2181(CONNECTED) 2] ls /MyFirstZnode
[mysecondsubnode, myfirstsubnode]

Check Status

Status 描述指定节点的元数据。它包含诸如时间戳、版本号、ACL、数据长度和子节点等详细信息。

Syntax

stat /path

Sample

stat /FirstZnode

Output

[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

Remove a Znode

删除指定节点及其所有子节点。仅当此类节点可用时才会执行此操作。

Syntax

rmr /path

Sample

rmr /FirstZnode

Output

[zk: localhost:2181(CONNECTED) 10] rmr /FirstZnode
[zk: localhost:2181(CONNECTED) 11] get /FirstZnode
Node does not exist: /FirstZnode

删除 (delete /path) 命令类似于 remove 命令,不同之处在于它仅适用于没有子节点的节点。

Zookeeper - API

ZooKeeper 有一个官方的 Java 和 C API 绑定。ZooKeeper 社区为大多数语言(.NET、python 等)提供非官方 API。使用 ZooKeeper API,应用程序可以连接、交互、操作数据、协调,最后从 ZooKeeper 集群断开连接。

ZooKeeper API 具有丰富的一组功能,可以以一种简单安全的方式获得 ZooKeeper 集群的所有功能。ZooKeeper API 提供同步和异步方法。

ZooKeeper 集群和 ZooKeeper API 在各个方面都完全相辅相成,极大地有益于开发人员。让我们在本章中讨论 Java 绑定。

Basics of ZooKeeper API

与 ZooKeeper 集群互动的应用程序称为 ZooKeeper Client 或简称 Client

节点是 ZooKeeper 集群的核心组件,而 ZooKeeper API 提供了一小套方法来使用 ZooKeeper 集群操作所有节点的详细信息。

要与 ZooKeeper 集合进行清晰简洁的交互,客户端应遵循以下步骤。

  1. 连接到 ZooKeeper 集合。ZooKeeper 集合为客户端分配一个会话 ID。

  2. 定期向服务器发送心跳。否则,ZooKeeper 集合会使会话 ID 过期,而客户端需要重新连接。

  3. 只要会话 ID 处于活动状态,即可获取/设置 znode。

  4. 完成所有任务后,断开与 ZooKeeper 集合的连接。如果客户端长时间不活动,则 ZooKeeper 集合将自动断开客户端连接。

Java Binding

让我们在本节中了解最重要的 ZooKeeper API 集合。ZooKeeper API 的核心部分是 ZooKeeper class 。它提供选项,在其构造函数中连接 ZooKeeper 集合,并具有以下方法:

  1. connect - 连接到 ZooKeeper 集合

  2. create - 创建 znode

  3. exists - 检查 znode 是否存在及其信息

  4. getData - 从特定 znode 获取数据

  5. setData - 在特定 znode 中设置数据

  6. getChildren - 获取特定 znode 中可用的所有子节点

  7. delete - 获取特定 znode 及其所有子项

  8. close - 关闭连接

Connect to the ZooKeeper Ensemble

ZooKeeper 类通过其构造函数提供连接功能。构造函数的签名如下所示:

ZooKeeper(String connectionString, int sessionTimeout, Watcher watcher)

其中,

  1. connectionString - ZooKeeper 集合主机。

  2. sessionTimeout - 会话超时(以毫秒为单位)。

  3. watcher - 一个实现“观察者”接口的对象。ZooKeeper 集合通过观察者对象返回连接状态。

让我们创建一个新的帮助程序类 ZooKeeperConnection 并添加一个方法 connectconnect 方法创建 ZooKeeper 对象,连接到 ZooKeeper 集合,然后返回对象。

此处 CountDownLatch 用来停止(等待)主进程,直到客户端连接到 ZooKeeper 集群。

ZooKeeper 集群通过 Watcher callback 返回连接状态。客户端连接到 ZooKeeper 集群后,将调用 Watcher 回调,并且 Watcher 回调会调用 CountDownLatchcountDown 方法来释放 await 中主进程的锁。

以下是用于连接到 ZooKeeper 集群的完整代码。

Coding: 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 集群。

Create a Znode

ZooKeeper 类提供了 create method 用于在 ZooKeeper 集群中创建新 znode。 create 方法的签名如下 −

create(String path, byte[] data, List<ACL> acl, CreateMode createMode)

其中,

  1. path − Znode 路径。例如,/myapp1、/myapp2、/myapp1/mydata1、myapp2/mydata1/myanothersubdata

  2. data − 要存储在指定 znode 路径中的数据

  3. acl − 要创建的节点的访问控制列表。ZooKeeper API 提供了一个静态接口 ZooDefs.Ids 来获取一些基本 acl 列表。例如,ZooDefs.Ids.OPEN_ACL_UNSAFE 返回一个对开放式 znode 的 acl 列表。

  4. createMode − 节点的类型,可以是临时、顺序或同时是这两种类型。这是一个 enum

让我们创建一个新的 Java 应用程序来检查 ZooKeeper API 的 create 功能。创建一个文件 ZKCreate.java 。在主方法中,创建一个类型为 ZooKeeperConnection 的对象并调用 connect 方法来连接到 ZooKeeper 集群。

connect 方法将返回 ZooKeeper 对象 zk 。现在,使用自定义 pathdata 调用 zk 对象的 create 方法。

用于创建 znode 的完整程序代码如下 −

Coding: 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 – Check the Existence of a Znode

ZooKeeper 类提供 exists method 来检查 znode 的存在。如果指定的 znode 存在,它将返回 znode 的元数据。 exists 方法的签名如下 −

exists(String path, boolean watcher)

其中,

  1. path − Znode path

  2. watcher − 布尔值,用于指定是否监视指定的 znode

让我们创建一个新的 Java 应用程序来检查 ZooKeeper API 的“exists”功能。创建一个文件 “ZKExists.java”。在主方法中,使用 “ZooKeeperConnection” 对象创建一个 ZooKeeper 对象“zk”。然后,使用自定义“path”调用“zk”对象的“exists”方法。完整的清单如下 −

Coding: 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.

getData Method

ZooKeeper 类提供了 getData 方法来获取附加在指定 znode 中的数据及其状态。 getData 方法的签名如下 −

getData(String path, Watcher watcher, Stat stat)

其中,

  1. path − Znode path.

  2. watcher − 类型为 Watcher 的回调函数。ZooKeeper 集群将在指定 znode 的数据更改时通过 Watcher 回调通知。这是一次性通知。

  3. stat - 返回 z 节点的元数据。

我们创建一个新的 Java 应用程序以了解 ZooKeeper API 的 getData 功能。创建文件 ZKGetData.java 。在 main 方法中,使用 he ZooKeeperConnection 对象创建一个 ZooKeeper 对象 zk 。然后,使用自定义路径调用 zk 对象的 getData 方法。

以下是获取指定节点数据的完整程序代码 -

Coding: 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 更改指定的 z 节点的数据。

cd /path/to/zookeeper
bin/zkCli.sh
>>> set /MyFirstZnode Hello

现在,应用程序将打印以下输出并退出。

Hello

setData Method

ZooKeeper 类提供 setData 方法来修改附加在指定的 z 节点中的数据。 setData 方法的签名如下 -

setData(String path, byte[] data, int version)

其中,

  1. path − Znode path

  2. data - 存储在指定 z 节点路径中的数据。

  3. version - z 节点的当前版本。每当数据更改时,ZooKeeper 都会更新 z 节点的版本号。

我们现在创建一个新的 Java 应用程序以了解 ZooKeeper API 的 setData 功能。创建文件 ZKSetData.java 。在 main 方法中,使用 ZooKeeperConnection 对象创建一个 ZooKeeper 对象 zk 。然后,使用指定的路径、新数据和节点版本调用 zk 对象的 setData 方法。

以下是修改附加在指定 z 节点中的数据的完整程序代码。

Code: 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());
      }
   }
}

一旦应用程序编译并执行,指定 z 节点的数据将被更改,可以使用 ZooKeeper CLI, zkCli.sh 来检查它。

cd /path/to/zookeeper
bin/zkCli.sh
>>> get /MyFirstZnode

getChildrenMethod

ZooKeeper 类提供 getChildren 方法来获取某个特定 z 节点的全部子节点。 getChildren 方法的签名如下 -

getChildren(String path, Watcher watcher)

其中,

  1. path − Znode path.

  2. watcher - 类型为“观察者”的回调函数。当指定的 z 节点被删除或 z 节点下的子元素被创建/删除时,ZooKeeper 集成会通知。这是一次性通知。

Coding: 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

现在,编译并运行程序将输出创建的上述 z 节点。

myfirstsubnode
mysecondsubnode

Delete a Znode

ZooKeeper 类提供 delete 方法来删除指定的 z 节点。 delete 方法的签名如下 -

delete(String path, int version)

其中,

  1. path − Znode path.

  2. version - z 节点的当前版本。

我们创建一个新的 Java 应用程序以了解 ZooKeeper API 的 delete 功能。创建文件 ZKDelete.java 。在 main 方法中,创建一个 ZooKeeper 对象 zk 使用 ZooKeeperConnection 对象。然后,使用 zk 对象的指定 path 和节点的版本调用 delete 方法。

删除znode的完整程序代码如下所示−

Coding: 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 - Applications

Zookeeper 为分布式环境提供了灵活的协调基础架构。ZooKeeper 框架支持当今许多最佳工业应用。本章我们将讨论 ZooKeeper 一些最著名的应用。

Yahoo!

ZooKeeper 框架最初是在“Yahoo!”中构建的。设计良好的分布式应用需要满足数据透明性、更好的性能、健壮性、集中式配置和协调等要求。因此,他们设计了 ZooKeeper 框架来满足这些要求。

Apache Hadoop

Apache Hadoop 是大数据行业增长的推动力。Hadoop 依赖 ZooKeeper 来进行配置管理和协调。让我们采用一个场景来理解 ZooKeeper 在 Hadoop 中扮演的角色。

假设 Hadoop cluster 桥接 100 or more commodity servers 。因此,需要协调和命名服务。因为计算涉及大量节点,所以每个节点需要相互同步,知道在哪里访问服务,并知道它们应如何配置。在此时刻,Hadoop 集群需要跨节点服务。ZooKeeper 为 cross-node synchronization 提供了便利,并确保 Hadoop 项目中的任务被序列化和同步。

多个 ZooKeeper 服务器支持大型 Hadoop 集群。每台客户端机器与一台 ZooKeeper 服务器通信以检索和更新其同步信息。一些实时示例如下 −

  1. Human Genome Project − 人类基因组计划包含数 TB 数据。Hadoop MapReduce 框架可用于分析数据集并找出对人类发展具有启发意义的事实。

  2. Healthcare − 医院可以存储、检索和分析大量的患者医疗记录,这些记录通常以 TB 为单位。

Apache HBase

Apache HBase 是一个开源、分布式 NoSQL 数据库,用于大数据集的实时读/写访问,并在 HDFS 之上运行。HBase 遵循 master-slave architecture ,其中 HBase Master 管理所有从属。从属被称为 Region servers

HBase 分布式应用程序的安装依赖于一个正在运行的 ZooKeeper 集群。Apache HBase 使用 ZooKeeper 来跟踪主服务器和地区服务器中分布式数据的状态,这依靠 centralized configuration managementdistributed mutex 机制。以下是一些 HBase 的用例 −

  1. Telecom − 电信行业存储着数十亿条移动通话记录(约为每月 30TB),实时访问这些通话记录成为一项艰巨的任务。HBase 可用于实时轻松高效地处理所有记录。

  2. Social network − 类似于电信行业,Twitter、LinkedIn 和 Facebook 等网站会通过用户创建的帖子收到大量数据。HBase 可用于找出近期趋势和其他启发意义的事实。

Apache Solr

Apache Solr 是一个用 Java 编写的快速、开源的搜索平台。它是一个速度惊人、容错的分布式搜索引擎。它建立在 Lucene 之上,是一个高性能、功能齐全的文本搜索引擎。

Solr 大量使用了 ZooKeeper 的每一个功能,例如配置管理、领导者选举、节点管理、数据的锁定和同步。

Solr 有两个不同的部分: indexingsearching 。索引是一个以适当格式存储数据的过程,以便以后可以对其进行搜索。Solr 使用 ZooKeeper 在多个节点上对数据建立索引并从多个节点进行搜索。ZooKeeper 贡献了以下功能 −

  1. 根据需要添加/移除节点

  2. 在节点间复制数据,从而最大程度地减少数据丢失

  3. 在多个节点间共享数据,并且从多个节点搜索以获得更快的搜索结果

Apache Solr 的一些用例包括电子商务、职位搜索等。