Zookeeper:简介
本课程介绍 Zookeeper 服务。
Zookeeper
Zookeeper 是企业级大数据部署中不可或缺的关键组件。ZooKeeper 是一个集中式服务,用于维护配置信息、命名、提供分布式同步以及组服务。这些服务均被分布式应用程序所使用。根据官方网站的描述,Zookeeper 的名字来源于“协调分布式系统就像一个动物园”。
从核心来看,Zookeeper 非常易于理解。可以将其想象成一个分层的文件系统或一棵树。Zookeeper 的基本构建单元是 znode。一个 znode 可以存储数据(类似于文件),也可以包含子 znode(类似于目录)。Zookeeper 整体设计实现了高可用的系统,由构成层次化命名空间的 znode 组成。以下是 znode 的示意图:
Zookeeper 可以以单机模式运行在单一服务器上,也可以在多台机器组成的集群中以复制模式运行,这种集群被称为 ensemble。在复制模式下实现高可用性的关键在于:确保对 znode 树的修改被复制到大多数节点上。如果 ensemble 中少数节点发生故障,至少仍有一个存活节点拥有最新的状态。举个例子:假设有五台机器(A、B、C、D 和 E)组成 Zookeeper ensemble。一次更新需要多数节点(称为 quorum)参与。假设 A、C 和 E 成功接收了更新。现在,如果少数节点(例如两台)发生故障,服务仍能正常运行。比如 A 和 E 故障,但 C 仍然保存着最新状态。其余两个存活节点 B 和 D 可以追赶至最新状态。这个看似简单的高可用性算法实际上非常难以正确实现。
Zookeeper 使用一种名为 Zab 的协议来实现上述机制。协议的细节超出了本文范围,但从高层来看,该协议分为两个阶段:第一阶段是选举领导者;其他节点成为跟随者,并与领导者同步状态。第二阶段,所有写请求都转发给领导者,由领导者广播更新给跟随者。一旦变更被多数机器确认提交,发起请求的客户端就会收到成功提交的通知。ensemble 中的所有机器都会先将更新写入磁盘,再更新其内存中的 znode 树副本。客户端可以向任意机器发送读请求,请求从内存中直接响应,从而实现快速查询。
如果领导者失效,将重新进行选举。当原领导者恢复后,它将以跟随者的身份重新加入集群。Zab 协议与另一个著名的领导者选举协议 Paxos 类似,但在多个方面存在差异。
数据模型
每个 znode 都关联一个访问控制列表(ACL),用于控制谁可以读取或写入特定的 znode。此外,对 znode 的读写操作是原子的:读/写请求要么完全成功,要么完全失败,绝不会出现部分读写的情况。znode 通过路径进行引用。例如,“/config/hostname” 指的是位于父节点 “config” 下的子节点 “hostname”。通过路径引用 znode,使得我们可以将 Zookeeper 视为一个分层的文件系统。
除了临时 znode 外,还有 顺序 znode,它们的名称中附带一个递增的序列号。该序列号由父 znode 维护的一个单调递增计数器生成。序列号可用于在分布式系统中对事件施加全局顺序。我们将在实现分布式锁时看到它的实际应用。Zookeeper 支持在 znode 上设置监听器(watches),当 znode 发生变化时触发。一个典型的应用场景是监控应用程序配置的变化。应用程序可以在存放配置的 znode 上设置监听器,一旦被其他程序修改,即可立即收到通知。
快速入门
为了感受 Zookeeper 的工作方式,我们将逐步执行启动 Zookeeper 服务并使用 telnet 查询版本号的命令。
1## 查看配置文件 2cat /opt/zookeeper/conf/zoo.cfg 3 4## 启动 Zookeeper 服务 5zkServer.sh start 6 7## 使用 telnet 连接 Zookeeper 服务 8telnet 127.0.0.1 2181 9 10## 当 telnet 提示符出现后,输入以下命令 11srvr 12 13## 查看所有四字母命令列表 14curl http://localhost:8080/commands
Zookeeper 的配置文件位于 zoo.cfg。其中的 tickTime 设置是 ZooKeeper 使用的基本时间单位(以毫秒为单位),用于控制心跳和超时。例如,客户端与 Zookeeper 服务器之间的最小会话超时时间为两个 tick。默认情况下,Zookeeper 服务器在端口 接受客户端连接。存在一些四字母命令(如 srvr),用于管理 Zookeeper 服务。默认仅允许执行 srvr 命令。可以通过运行在 8080 端口的内建 Web 服务器获取所有管理命令的列表。但未来,四字母命令将被弃用,推荐使用 AdminServer。
连接终端后,请按顺序在下方代码框中执行以下命令:
1# 查看配置文件 2cat /opt/zookeeper/conf/zoo.cfg 3 4# 启动 Zookeeper 服务 5zkServer.sh start 6 7# 使用 telnet 连接 Zookeeper 服务 8telnet 127.0.0.1 2181 9 10# 当 telnet 提示符出现后,输入以下命令 11srvr 12 13# 查看所有四字母命令列表 14curl http://localhost:8080/commands
一致性
在本节课结束前,我们来讨论一下一致性问题。所有对 znode 树的更改都会被分配一个全局唯一的标识符,称为 Zookeeper 事务 ID(zxid)。更新操作按照 zxid 的顺序进行,若 zxid A 小于 zxid B,则 A 必然发生在 B 之前。这带来了以下数据一致性的保证:
-
顺序一致性:来自同一客户端的更新按顺序应用。如果一个客户端将某个 znode 从状态 s 修改为 s',那么在该客户端观察到 s' 之后,其他任何客户端都不会看到该 znode 处于状态 s。
-
原子性:修改操作要么全部成功,要么全部失败,不存在部分成功或失败的情况。
-
单一系统视图:无论客户端连接到 ensemble 中哪一台服务器,看到的系统视图都是一致的。设想一种情况:客户端断开了与已失效服务器的连接,尝试连接到另一台尚未追赶到失效服务器状态的服务器。由于该服务器尚未同步最新状态,它将拒绝客户端连接,直到完成追赶。
-
持久性:一旦更新成功,数据将被持久化保存。
-
及时性:客户端所看到的系统状态在一定时间范围内(通常为几十秒)始终是最新的。系统变更要么在此时间内被客户端感知,要么客户端会检测到服务不可用。
Zookeeper 不支持 跨客户端的同时一致视图。这意味着,如果客户端 X 将某个 znode 从 s 更新为 s',并通知客户端 Y 读取新值,Y 仍可能读取到过期的旧值 s 而非 s'。为了让客户端 Y 能读取到最新值,必须对该 znode 执行 sync 操作(Zookeeper API 的一部分)。ZooKeeper 不保证不同客户端始终拥有完全相同的数据视图。由于网络延迟等因素,一个客户端可能在另一个客户端收到变更通知之前就完成了更新。