ZooKeeper是一种用于分布式应用程序的分布式开源协调服务。它暴露了一组简单的基元,分布式应用程序可以基于这些基元来实现用于同步,配置维护以及组和命名的更高级别的服务。它被设计为易于编程,并且使用在熟悉的文件系统的目录树结构之后样式化的数据模型。它在Java中运行,并且具有Java和C的绑定。
协调服务是众所周知难以得到正确的。它们特别容易出现错误,例如竞争条件和死锁。ZooKeeper的动机是缓解分布式应用程序从头开始实施协调服务的责任。
ZooKeeper很简单。ZooKeeper允许分布式进程通过共享的层次结构命名空间相互协调,该结构类似于标准文件系统。名称空间由数据寄存器组成 - 称为znode,在ZooKeeper中的用法 - 这些类似于文件和目录。与为存储设计的典型文件系统不同,ZooKeeper数据保存在内存中,这意味着ZooKeeper可以实现高吞吐量和低延迟数字。
ZooKeeper实现了高性能,高可用性,严格排序的访问。ZooKeeper的性能方面意味着它可以在大型分布式系统中使用。可靠性方面使其不会成为单点故障。严格排序意味着复杂的同步原语可以在客户端实现。
ZooKeeper被复制。像它所协调的分布式进程一样,ZooKeeper本身是用来在被称为整体的一组主机上复制的。
ZooKeeper服务组成ZooKeeper服务的服务器必须彼此了解。它们维护一个内存中的状态图像,以及持久存储中的事务日志和快照。只要大多数服务器可用,ZooKeeper服务将可用。
客户端连接到单个ZooKeeper服务器。客户端维护一个TCP连接,通过它来发送请求,获取响应,获取监视事件和发送心跳。如果到服务器的TCP连接断开,客户端将连接到不同的服务器。
ZooKeeper是有序的。ZooKeeper使用反映所有ZooKeeper事务顺序的数字对每个更新进行标记。后续操作可以使用顺序来实现更高级别的抽象,例如同步原语。
ZooKeeper是快速。它在“读优势”工作负载中尤其快。ZooKeeper应用程序在数千台机器上运行,并且在大约10:1的比率下,读取比写入更常见。
ZooKeeper提供的名称空间非常类似于标准文件系统。名称是由斜杠(/)分隔的一系列路径元素。ZooKeeper名称空间中的每个节点都由一个路径标识。
ZooKeeper的分层命名空间与标准文件系统不同,ZooKeeper命名空间中的每个节点都可以具有与其关联的数据以及子级。它就像有一个文件系统允许文件也是一个目录。(ZooKeeper被设计为存储协调数据:状态信息,配置,位置信息等,因此存储在每个节点的数据通常很小,在字节到千字节的范围内)。我们使用 znode项来清楚地表示正在谈论ZooKeeper数据节点。
Znodes维护统计结构,包括数据更改,ACL更改和时间戳的版本号,以允许缓存验证和协调更新。每次znode的数据更改时,版本号增加。例如,每当客户端检索数据时,它也接收数据的版本。
存储在命名空间中每个znode的数据以原子方式读取和写入。读取获取与znode相关联的所有数据字节,写入替换所有数据。每个节点都有一个访问控制列表(ACL),限制谁可以做什么。
ZooKeeper也有临时节点的概念。只要创建znode的会话处于活动状态,这些znode就存在。当会话结束时,znode被删除。当您想实现[ tbd ]时,临时节点是有用的。
ZooKeeper支持手表的概念。客户端可以在znode上设置手表。znode更改时,触发和删除手表。当手表被触发时,客户端接收到一个数据包,说明znode已经改变。如果客户端和某个Zoo Keeper服务器之间的连接断开,客户端将收到本地通知。这些可以用于[tbd]。
ZooKeeper是非常快速和非常简单。因为它的目标,虽然是构建更复杂的服务的基础,如同步,它提供了一套保证。这些是:
顺序一致性 - 来自客户端的更新将按照它们发送的顺序应用。
原子性 - 更新成功或失败。没有部分结果。
单一系统映像 - 客户端将看到服务的相同视图,而不管它连接到的服务器。
可靠性 - 一旦应用更新,它将持续从那个时间直到客户端覆盖更新。
及时性 - 系统的客户端视图保证在一定时间范围内是最新的。
有关这些以及如何使用它们的更多信息,请参见 [tbd]
ZooKeeper的设计目标之一是提供一个非常简单的编程接口。因此,它仅支持以下操作:
创建在树中的位置处创建节点
删除删除节点
存在测试节点是否存在于某个位置
获取数据从节点读取数据
设置数据将数据写入节点
得到 子节点检索节点的子节点的列表
同步等待要传播的数据
有关这些问题的更深入讨论,以及如何利用这些问题实施更高层次的运营,请参见[ tbd ]
ZooKeeper组件显示ZooKeeper服务的高级组件。除了请求处理器,组成ZooKeeper服务的每个服务器复制其自己的每个组件的副本。
ZooKeeper组件复制数据库是包含整个数据树的内存数据库。更新会记录到磁盘以进行可恢复性,并且写入将在应用于内存数据库之前序列化到磁盘。
每个ZooKeeper服务器服务客户端。客户端连接到一个服务器以提交irequest。读取请求从每个服务器数据库的本地副本服务。更改服务状态(写入请求)的请求由协议协议处理。
作为协议协议的一部分,来自客户端的所有写请求被转发到单个服务器,称为 引导者。其余的ZooKeeper服务器,称为 跟随者,从领导者接收消息提议并同意消息传递。消息层负责在失败时替换领导者,并与领导者同步跟随者。
ZooKeeper使用自定义原子消息传递协议。由于消息层是原子的,ZooKeeper可以保证本地副本不会发散。当领导者收到写请求时,它计算要应用写入时系统的状态,并将其转换为捕获此新状态的事务。
ZooKeeper的编程接口故意简单。然而,使用它,您可以实现更高阶操作,例如同步原语,组成员资格,所有权等。一些分布式应用程序已经使用它:[ tbd :从白皮书和视频演示中添加使用。]有关详细信息,请参阅 [tbd]
ZooKeeper设计是高性能的。但是是吗?ZooKeeper在Yahoo! Research的开发团队的结果表明它是。(请参见ZooKeeper吞吐量作为读写比率变化。)在读取超出数量写入的应用程序中,这是特别高的性能,因为写入涉及同步所有服务器的状态。(对协调服务通常是读取数量更多的写入。)
ZooKeeper吞吐量作为读写比率变化图ZooKeeper吞吐量作为读写比率 Varies是在具有双2Ghz Xeon和两个SATA 15K RPM驱动器的服务器上运行的ZooKeeper版本3.2的吞吐量图。一个驱动器用作专用的ZooKeeper日志设备。快照已写入操作系统驱动器。写请求为1K次写入,读取次数为1K次。“服务器”指示ZooKeeper集合的大小,组成服务的服务器的数量。大约30个其他服务器用于模拟客户端。ZooKeeper集合配置为使领导不允许来自客户端的连接。
注意在3.2版本的r / w性能比以前的3.1版本提高了〜2倍。
基准也表明它也是可靠的。存在错误时的可靠性显示了部署如何响应各种故障。图中标记的事件如下:
追随者的失败和恢复
不同的追随者的失败和恢复
领导失败
两个追随者的失败和恢复
另一位领导人失败
为了显示系统在注入失败时的行为,我们运行了一个由7台机器组成的ZooKeeper服务。我们运行了与之前相同的饱和度基准,但是这次我们将写入百分比保持在恒定的30%,这是我们预期工作负载的保守比率。
存在错误时的可靠性这是从这个图的几个重要的意见。首先,如果关注者失败并迅速恢复,则ZooKeeper能够维持高吞吐量,尽管失败。但也许更重要的是,领导选举算法允许系统足够快地恢复,以防止吞吐量大幅下降。在我们的观察中,ZooKeeper花费不到200毫秒选择一个新的领导者。第三,随着追随者恢复,ZooKeeper能够在开始处理请求后再次提高吞吐量。