一、ZooKeeper简介
ZooKeeper是一个分布式协调服务来管理大量的主机。协调和管理在分布式环境的一个服务是一个复杂的过程。ZooKeeper 简单解决了其结构和API这个问题。ZooKeeper允许开发人员能够专注于核心应用程序逻辑,而无需担心应用程序的分布式特性。
ZooKeeper框架始建于“雅虎”,一个简单而强大的方法用于访问应用程序。后来 Apache ZooKeeper 成为用 Hadoop,HBase 的组织服务以及其他分布式架构的标准。例如,Apache HBase 使用 ZooKeeper 跟踪分布式数据的状态。
分布式应用程序
分布式应用程序可以通过在它们之间协调以完成特定的任务,快速且有效的方式在多个系统中的网络在给定时间(同时)运行。通常,复杂的和耗时的任务,这将需要几个小时由非分布式应用程序(在一个系统上运行)来完成,可以在几分钟内由分布式应用程序通过使用所有所涉及的系统的计算能力来完成。
完成任务的时间可以通过配置分布式应用到更多的系统上运行来进一步降低。一组在分布式应用程序正在运行的系统被称为集群,在群集中运行的每台机器被称为节点。
分布式应用程序有两部分,分别是:服务器和客户端应用程序。服务器应用程序实际上是分布式的,有一个共同的接口,这样客户端可以连接到集群中的任何服务器,并得到同样的结果。客户端应用程序用一个分布式应用程序交互工具。
分布式应用程序的好处
- 可靠性 − 单个或几个系统的故障不会使整个系统失效。
- 可扩展性 − 性能可以在无需停机的应用程序的微小配置变化来在需要时增加更多的机器。
- 透明性 − 隐藏了系统的复杂性并表现为一个单一的实体/应用程序。
分布式应用程序的挑战
- 竞争条件 − 两个或更多的机器试图执行特定任务,这实际上需要将仅由一台机器在任何给定的时间内完成。例如,共享资源只能由一台机器在任何特定时间进行修改。
- 死锁 − 两个或更多的操作等待对方无限期完成。
- 不一致性 - 数据部分失败。
Apache ZooKeeper 意味着什么?
Apache ZooKeeper是由群集(组节点)之间进行相互协调,并保持强大的同步技术共享数据的服务。ZooKeeper本身是一个分布式应用写入分布式应用提供服务。
ZooKeeper 提供的通用服务如下:
- 命名服务 − 确定在一个集群中的节点的名字。它类似于DNS,只不是过节点。
- 配置管理 − 系统最近加入节点和向上最新配置信息。
- 集群管理 − 加入/节点的群集和节点状态实时离开。
- 节点领导者选举 − 选举一个节点作为领导者协调的目的。
- 锁定和同步服务 − 锁定数据,同时修改它。这种机制可以帮助自动故障恢复,同时连接其它的分布式应用程序。如Apache HBase。
- 高可靠的数据注册表 − 一个或几个节点的可用性的数据向下。
分布式应用程序提供了很多好处,但他们也带来了一些复杂的,难以破解的挑战。ZooKeeper框架提供了完整的机制来克服所有挑战。竞争条件和死锁使用故障安全同步的方式进行处理。另一个主要缺点是不一致的数据,ZooKeeper 使用原子性解决。
ZooKeeper的优点
下面是使用 ZooKeeper 的好处:
- 简单的分布式协调过程
- 同步 − 互斥和服务器进程之间的合作。这个过程有助于Apache HBase 的配置管理。
- 有序消息
- 序列化− 根据特定的规则进行编码数据。确保应用程序不断地运行。这种方法可以用来在MapReduce的协调队列以执行正在运行的线程。
- 可靠性
- 原子性 − 数据传输成功或完全失败,但没有事务处理部分。
二、Zookeeper基础
ZooKeeper的体系结构
看看下面的图。它描绘ZooKeeper 的“客户端 - 服务器架构”。
ZooKeeper 架构的一部分组件如下表中所解释。
部分 | 描述 |
---|---|
Client | 客户端,在我们的分布式应用集群的一个节点,从服务器获取信息。对于一个特定的时间间隔,每个客户端将消息发送到服务器,以让服务器都知道客户机是活的。同样,服务器会发送一个确认当客户端连接。如果没有从所连接的服务器的响应,客户端自动重定向消息到另一个服务器 |
Server | 服务器,ZooKeeper集成的一个节点,提供所有的服务提供给客户。给出应答客户,告知该服务器还活着 |
合组 | ZooKeeper 服务器组。节点所需要形成的合奏的最小数目为3 |
Leader | 它执行自动恢复,如果任何连接的节点的故障的服务器节点。领导者服务启动 |
Follower | 遵循领导指示服务器节点 |
分层命名空间
下图显示了用于内存中表示 ZooKeeper 文件系统的树形结构。 ZooKeeper节点被称为znode。每个znode由一个名称识别,并通过路径(/)序列隔开。
- 在图中,首先有一个根znode,它由“/”分隔。在根下,有两个逻辑命名空间 config 和 workers。
- 在config命名空间用于集中配置管理以及 workers 命名空间用于命名。
- 在 config 命名空间下,每个znode可以存储高达 1MB 的数据。这类似于UNIX文件系统,不同的是父 znode 也可以存储数据。这种结构的主要目的是存储同步数据以及描述znode的元数据。这种结构被称为 ZooKeeper数据模型。
在 ZooKeeper 数据模型中每个 znode 维护一个 stat 结构。 一个统计(stat )只是提供了一个 znode 元数据。 它由版本号,动作控制列表(ACL),时间戳和数据长度组成。 - 版本号 − 每个znode都有一个版本号,这意味着每个相关的时间使用节点改变数据,其相应的版本号也将增加。使用版本号是重要的,在多个 zookeeper 的客户端正在努力通过相同znode执行操作。
- 动作控制列表(ACL) −ACL是基本的身份验证机制,用于访问znode。它管理所有的znode读写操作。
- 时间戳 − 时间戳表示过去时间,从znode创建和修改起算。它通常以毫秒表示。ZooKeeper 确定每次从“事务ID”(zxid)更改znodes。Zxid是独特的,为每个事务处理维持时间,使您可以轻松地识别从一个请求到另一个请求经过的时间。
- 数据长度 − 存储在 znode 数据的合计量是数据长度。可以存储的最大数据容量为1MB。
Znodes 类型
Znodes 被归类为持久性,顺序和短暂。
- 持久性znode − 持久性 znode 处于活动状态,即使客户端,它创造了特定的 znode。默认情况下,所有的 znodes 是持久的,除非另有说明。
- 短暂znode − 短暂znodes活跃,直到客户端还活着。当客户端被从 ZooKeeper 集合断开连接,然后znodes自动删除。由于这个原因,只有短暂znodes不允许再有一个子。如果短周期znode被删除,那么下一个合适的节点,将填补其位置。短暂znodes 发挥在领导选举中起重要作用。
- 连续znode − 连续znodes可以是持久或短暂的。当一个新的znode作为连续znode创建的,则 ZooKeeper 通过将10位的序列号为原始名称设置znode的路径。例如,如果使用路径 /myapp 来创建一个znode作为连续znode,ZooKeeper将改变路径 /myapp0000000001并设置一个序列号为0000000002。如果两个连续znodes同时被创建,ZooKeeper从来不使用相同数量在每个znode上。连续znodes在锁定和同步中起到重要作用。
会话
会话对于 ZooKeeper 操作是非常重要的。请求在会话 FIFO 顺序执行。当一个客户端连接到服务器,会话将建立一个会话ID并分配给客户端。
客户端在特定的时间间隔发送心跳来保持会话有效。如果 ZooKeeper 从客户端接收检测信号超过在服务的开始指定的期间(会话超时),它认为该客户死亡。
会话超时通常以毫秒表示。当一个会话因任何原因而结束,该会话期间短暂创造了的 znodes 会被删除。
监视
监视是一个简单的机制,在ZooKeeper集合通知下以获取客户有关的变化。 客户端可以设置监视,同时读取特定znode。监视发送通知给注册的客户机对任何znode(在其上的客户端寄存器)的变化。
节点改变时znode或子znode变化相关联的数据也会被修改。监视只被触发一次。如果客户想要再次通知,则必须通过另一次读操作来完成。当一个连接会话已过期,客户端会从服务器断开,并在相关的监视也将被删除。
三、Zookeeper工作流
当ZooKeeper集合启动时,它会等待客户端连接。客户端将连接到ZooKeeper的集合的其中一个节点。它可能是一个领导者或跟随者节点。当客户机连接时,该节点分配会话ID给特定的客户端,并发送一个确认消息给客户端。如果客户端没有得到确认,它会尝试连接ZooKeeper集合的另一个节点。当连接到一个节点后,客户端将以规则的间隔发送心跳到节点,以确保连接不会丢失。
- 如果客户想要读取特定的znode,它发送一个读请求使用znode路径的节点,所述节点从其自己的数据库中获取它返回所请求的znode。出于这个原因,读取在动物园管理员集合中速度非常快。
- 如果客户希望将数据存储在ZooKeeper 集合,它发送znode路径和数据到服务器。连接的服务器将请求转发到领导者,那么领导者将重新发出书面请求到所有的追随者。如果只有一个数节点成功响应,接着写请求将成功及一个成功的返回代码将被发送到客户端。否则,写请求将失败。严格大部分节点被称为定额。
ZooKeeper集合的节点
让我们来分析ZooKeeper集合不同数量的节点的作用。
- 如果我们有一个节点,那么当该节点出现故障时ZooKeeper集合失败。它有利于“单一失败教程”,它不建议用在生产环境中。
- 如果我们有两个节点,一个节点出现故障,我们也没有“多数”,因为二分之一并不是一个大多数。
- 如果我们有三个节点及其一个节点发生故障,我们有大多数,因此它是最低要求。它强制 ZooKeeper 集合在实际生产环境中至少有三个节点。
- 如果我们有四个节点及其有当两个节点失败,它类似于有三个节点。额外的节点没有任何作用,因此,最好是单数增加节点,例如,3, 5, 7.
我们知道,写处理它比在 ZooKeeper 集合读过程是昂贵的,由于所有的节点需要写相同的数据在其数据库中。因此,最好是具有节点(3,5或7)比具有大量节点的一个平衡的环境的数量少。
下图描述了ZooKeeper 的工作流程以及在随后的表说明了其不同的组件。
组件 | 描述 |
---|---|
写入 | 写过程是由领导节点处理。领导者转发写请求到所有znodes及其等待来自znodes应答。如果一半的znodes的回复,那么写入过程就完成了。 |
读取 | 读取在内部由特定连接znode进行的,所以没有必要与集群交互。 |
复制数据库 | 它是用来将数据存储在zookeeper。每个znode都有自己的数据库及其每个znode 在一致性的作用下,每次有相同的数据。 |
领导者(节点) | 领导者是由Znode负责处理写请求。 |
追随者(节点) | 追随者收到来自客户端的写请求,并将其转发到领导znode。 |
请求处理器 | 目前仅在领导节点。它从跟随节点的请求支配写入。 |
原子广播 | 负责从领导节点到从节点广播更改。 |
四、Zookeeper领导人选举
让我们来分析一下一个领导节点在ZooKeeper集合的选举。考虑集群中有N多的节点。领导人选举的过程如下:
- 所有节点创建一个顺序,znode具有相同路径,/app/leader_election/guid_。
- ZooKeeper 的集合将追加的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被删除。
- 跟随节点接下来将通过观察者得到关领导去除的通知。
- 跟随节点接下来会检查是否有其他znodes用最小数量。 如果没有,接着它将承担领导者的角色。否则,它会找到哪些用最小数创造了znode作为领导者的节点。
- 同样,其他所有跟随节点选举创造了znode用最小数作为领导者的节点。
领导人选举时,它从头开始做一个复杂的过程。但ZooKeeper服务,使得它非常简单。
五、Zookeeper安装配置
在安装ZooKeeper之前,请确保在以下任何操作系统上运行 −
- 任意Linux OS − 支持开发和部署。它是演示应用程序的首选。
- Windows OS − 仅支持开发。
- Mac OS − 仅支持开发。
ZooKeeper服务器是用Java编写创建,它运行在JVM。所以需要使用JDK 6或更高版本。
第1步:验证安装Java
假设你已经在系统上安装Java环境。只需使用下面的命令验证它。
$ java -version
如果您的计算机上已经安装了Java,那么可以查看已安装的Java版本。否则,请执行以下的简单步骤来安装Java最新版本。
步骤1.1:下载JDK
最新的版本(在写这篇教程)是JDK8U65和文件:“jdk-8u65-linuxx64.tar.gz”,请下载文件到本机。
步骤1.2:提取文件
一般情况下,文件下载到Downloads文件夹中。验证它,使用下面的命令提取 tar 文件。
$ cd /go/to/download/path
$ tar -zxf jdk-8u65-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_65 /opt/jdk/
步骤1.4:设置路径
要设置 JAVA_HOME 变量, 添加下面的命令到~/.bashrc文件。
export JAVA_HOME = /usr/jdk/jdk-1.8.0_65
export PATH=$PATH:$JAVA_HOME/bin
现在,提交所有更改到当前正在运行的系统。
$ source ~/.bashrc
步骤1.5:Java替代方案
使用以下命令来更改Java的替代方案。
update-alternatives --install /usr/bin/java java /opt/jdk/jdk1.8.0_65/bin/java 100
第2步:ZooKeeper框架的安装
步骤2.1:下载ZooKeeper
要在计算机上安装ZooKeeper框架,请访问以下链接并下载ZooKeeper的最新版本:
http://zookeeper.apache.org/releases.html
步骤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 配置文件,并将以下所有参数设置为开始点。
$ 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
六、Zookeeper CLI
ZooKeeper 命令行界面(CLI)是用来与 ZooKeeper 集成作开发进行交互的。这是在调试和使用不同的选项时的工作有用。
为了执行ZooKeeper的CLI操作, ZooKeeper服务器首先要启动 (“bin/zkServer.sh start”) , 然后使用 ZooKeeper 客户端 (“bin/zkCli.sh”). 当客户端启动后,可以执行以下操作
- 创建znodes
- 获取数据
- 监视 znode 变化
- 设置数据
- 创建 znode 的子 znode
- 列出一个 znode 的子 znode
- 检查状态
- 删除一个 znode
创建Znodes
由一个给定的路径来创建znode。flag参数指定了创建的 znode 是否为短暂的,持久的,或连续的。默认情况下,所有的 znodes是持久的。
- 短暂 znodes(flag: e)当会话过期或当客户端断开连接将被自动删除。
- 连续 znodes 保证 znode 路径是唯一的。
- ZooKeeper集成将沿着添加序列号使用10位填充到znode路径。例如,znode路径 /myapp 将被转换为 /myapp0000000001 以及下一个序列号将是 /myapp0000000002. 如果没有指定flag,那么 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 尝试,然后重新打开命令行。
获取数据
它返回 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的数据。当你完成设置操作,就可以使用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命令(之前的命令),则输出将类似如下 -
输出
[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类似于创建新的znodes。唯一的区别在于,子 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
该命令用于列出和显示子 znode 。
语法
ls /path
示例
ls /MyFirstZnode
输出
[zk: localhost:2181(CONNECTED) 2] ls /MyFirstZnode
[mysecondsubnode, myfirstsubnode]
检查状态
状态描述了指定znode的元数据。它包含详细信息,如时间戳,版本号,访问控制列表,数据长度和子znode。
语法
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
删除Znode
删除指定znode和递归删除所有的子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
删除(删除/路径)命令类似remove命令,但它仅适用于无子znode的znode。
七、Zookeeper API
ZooKeeper有一个Java和C绑定的官方API。ZooKeeper社区提供了对于大多数语言(.NET,Python等)的非官方API。使用ZooKeeper的API,应用程序可以连接,互动,操作数据,协调,以及从ZooKeeper集成断开。
ZooKeeper API有一组丰富的功能,在一个简单而安全的方式在ZooKeeper集成获得所有功能。ZooKeeper API提供同步和异步方法。
ZooKeeper的集成和ZooKeeper API 在各个方面完全互补,它有利于开发商在一个简便的方式。 在本章讨论Java绑定。
ZooKeeper的API基础知识
应用程序使用 ZooKeeper 集成的交互称为 ZooKeeper 客户端。
Znode 是 ZooKeeper 集成的核心组件,ZooKeeper API提供一个方法来处理znode所有使用ZooKeeper 集成。
客户端应遵循下面给出带 ZooKeeper 集成一个清晰的交互步骤。
- 连接到ZooKeeper 。ZooKeeper 集成分配客户端的会话ID。
- 定期发送心跳到服务器。否则,ZooKeeper 集成过期的会话ID,那么客户端需要重新连接。
- 获得/设置只要znodes会话ID是活动的。
- 从 ZooKeeper 集成断开,当所有的任务都完成后。如果客户端处于非活动状态较长时间,那么 ZooKeeper 集成会自动断开客户机。
Java绑定
让我们这一章中理解最重要的ZooKeeper API。ZooKeeper API的中心部分是ZooKeeper 类。它提供了一些选项来连接 ZooKeeper 集成在其构造,有以下几种方法
- connect − 连接到 ZooKeeper 的集成
- create − 创建一个 znode
- exists − 检查znode是否存在及其信息
- getData − 从一个特定的znode获取数据
- setData − 设置数据在特定znode
- getChildren − 得到一个特定 znode 的所有可用子节点
- delete − 得到一个特定的 znode 及其所有子节点
- close − 关闭连接
连接到 ZooKeeper 集合
ZooKeeper类通过它的构造函数提供了连接功能。构造函数的签名如下:
ZooKeeper(String connectionString, int sessionTimeout, Watcher watcher)
在这里,
- connectionString − ZooKeeper集合主机。
- sessionTimeout − 以毫秒为单位会话超时。
- watcher − 一个执行对象“观察者”的接口。ZooKeeper 集合返回通过监控器对象的连接状态。
让我们创建一个新的辅助类 ZooKeeperConnection 并添加一个方法连接。在连接方法创建一个 ZooKeeper 对象,连接到 ZooKeeper 集合,然后返回该对象。
这里CountDownLatch用于停止(等待)主进程,直到客户端与 ZooKeeper 集合连接。
ZooKeeper集合通过观察回调回应连接状态。一旦客户与 ZooKeeper 集合连接,观察者回调调用CountDownLatch 释放锁倒计时方法在主进程中等待监视回调会被调用。
下面是完整的代码,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集合。
创建一个Znode
ZooKeeper类提供了一个方法来在集合 ZooKeeper 创建一个新的 znode。创建方法的签名如下:
create(String path, byte[] data, List<ACL> acl, CreateMode createMode)
Where,
- path − Znode的路径。例如 /myapp1, /myapp2, /myapp1/mydata1, myapp2/mydata1/myanothersubdata
- data − 在一个指定的znode路径存储数据
- acl − 要创建节点的访问控制列表。 ZooKeeperAPI提供了一个静态接口ZooDefs.Ids得到一些基本的ACL列表。例如,ZooDefs.Ids.OPEN_ACL_UNSAFE返回ACL开放的 znodes 列表。
- createMode − 节点的类型,可以是短暂的,连续的,或两者。这是一个枚举类型。
让我们创建一个新的Java应用程序来检查 ZooKeeper API 创建功能。创建一个文件ZKCreate.java。在main方法中,创建一个类型ZooKeeperConnection 的对象,并调用connect方法连接到 ZooKeeper 集合。
连接方法将返回 ZooKeeper 对象 zk。 现在,调用自定义路径和数据创建 zk 对象的方法。.
完整的程序代码,以创建一个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
}
}
}
一旦应用程序编译和执行,使用指定数据的 znode 将在ZooKeeper集合创建。您可以使用 ZooKeeper CLI 的 zkCli.sh 来检查它。
cd /path/to/zookeeper
bin/zkCli.sh
>>> get /MyFirstZnode
Exists – 检查一个Znode的存在
ZooKeeper类提供了 exists 方法来检查 znode 的存在。如果指定的 znode 存在它返回一个 znode 元数据。exists 方法的签名如下
exists(String path, boolean watcher)
在这里 ,
- path − Znode 路径
- watcher − 布尔值,指定是否监视指定的znode与否
让我们创建一个新的Java应用程序来检查 ZooKeeper API的 “exists” 功能。创建一个文件 “ZKExists.java”。
在 main 方法,创建 ZooKeeper 对象, “zk” 使用 “ZooKeeperConnection” 对象. 然后再调用zk” 对象的“exists”方法和指定的“path”。完整的列表如下
代码: 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 方法
ZooKeeper类提供 getData方法来获取连接在指定 znode 及其状态的数据。getData方法的签名如下
getData(String path, Watcher watcher, Stat stat)
这里,
- path − Znode 路径.
- watcher − Watcher类型的回调函数。ZooKeeper集合将通知通过观察者回调时指定的节点改变的数据。这是一次性的通知。
- tat − 返回 znode 元数据。
让我们创建一个新的Java应用程序,以了解的ZooKeeperAPI的 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
setData 方法
ZooKeeper类提供SetData方法来修改附着在指定 znode 的数据。SetData方法的签名如下
setData(String path, byte[] data, int version)
在这里,
- path − 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的数据将被改变,并且它可以使用 ZooKeeperCLI,zkCli.sh 进行检查
cd /path/to/zookeeper
bin/zkCli.sh
>>> get /MyFirstZnode
getChildren 方法
ZooKeeper类提供 getChildren方法来得到一个特定的 znode 所有子节点。getChildren 方法的签名如下
getChildren(String path, Watcher watcher)
在这里,
- path − Znode 路径.
- watcher − 调用“Watcher”类型函数. ZooKeeper集合将通知在指定的 znode 被删除或znode以下子节点创建/删除。 这是一次性的通知。
代码: 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());
}
}
}
在运行程序之前,让我们使用 ZooKeeperCLI,zkCli.sh 创建 /MyFirstZnode 的两个子节点。
cd /path/to/zookeeper
bin/zkCli.sh
>>> create /MyFirstZnode/myfirstsubnode Hi
>>> create /MyFirstZnode/mysecondsubmode Hi
现在,编译并运行该程序将输出上面创建znodes。
myfirstsubnode
mysecondsubnode
删除一个Znode
ZooKeeper类提供了 delete 方法来删除指定 znode。delete方法的签名如下
delete(String path, int version)
在这里,
- path − Znode 路径
- version − 当前 znode 的版本
让我们创建一个新的Java应用程序,以了解ZooKeeperAPI的删除功能。
创建一个文件 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应用
Yahoo!
ZooKeeper框架始建于“雅虎”。一个设计良好的分布式应用需求,满足需求,如数据的透明度,更好的性能,稳定性,集中配置和协调性。所以,他们设计了ZooKeeper框架来满足这些要求。
Apache Hadoop
Apache Hadoop是大数据行业的增长背后的驱动力。Hadoop依赖ZooKeeper的配置管理和协调。让我们举一个场景来了解 ZooKeeper 在 Hadoop 中的作用。
假设一个Hadoop集群桥接100以上的商用服务器。因此,有必要协调和命名这些服务器。由于大量节点的计算都参与其中,每个节点都需要互相同步,知道在哪里获得服务,并知道应该如何配置。在这个时间点上,Hadoop集群需要跨节点服务。ZooKeeper提供了跨节点同步的设备,确保整个的Hadoop项目的任务是序列化和同步的。
多重ZooKeeper服务器支持大型 Hadoop 集群。每个客户机与 ZooKeeper 的其中一个服务器通信,以检索和更新其同步信息。一些实时实例如下 -
- 人类基因组计划 − 人类基因组计划中包含TB级别的数据。Hadoop的MapReduce框架可以用来分析数据集,并找到对人类发展有趣的事情。
- 卫生保健 − 医院可以存储,检索和分析大量套门诊病历,通常都在TB级。
Apache HBase
Apache HBase是一个开源的,分布式的, 用于实时NoSQL数据库读取的大型数据集/写访问,并运行在HDFS之上。 HBase如下主从架构,HBase主管理所有的从服务。从服务被称为区域服务器。
HBase 分布式应用程序的安装取决于ZooKeeper运行的集群。Apache HBase 使用ZooKeeper 与集中配置管理,分布式互斥机制的帮助下跟踪分布式数据的整个主区域服务器的状态。下面是一些HBase的用例 -
- 电信业 − 电信业存储数十亿的手机通话记录(大约30TB/月),访问这些通话记录实时成为一项艰巨的任务。HBase可用于所有记录的实时处理,容易且有效。
- 社交网络 − 类似电信业,网站如Twitter,LinkedIn和Facebook通过用户创建的帖子内容接收大量数据。HBase 可以用来找出最近的趋势和其他有趣的事实。
Apache Solr
Apache Solr是用Java编写实现快速,开源的搜索平台。这是一个速度极快,容错分布式搜索引擎。建立在Lucene之上,它是一个高性能,全功能的文本搜索引擎。
Solr被广泛使用在ZooKeeper的每一个功能,如配置管理,领导人选举,节点管理,锁定和数据的同步。
Solr有两个不同的部分,索引和搜索。索引是将数据存储在一个适当的格式,以便它可以在以后检索处理。Solr使用ZooKeeper为索引数据在多个节点,并从多个节点中搜索。ZooKeeper 分布有以下特点-
- 在需要的时候添加/删除节点
- 最小化数据丢失并随后复制节点数据
- 共享多个节点之间的数据,并从多个节点更快的搜索结果