Apache > ZooKeeper
 

ZooKeeper

ZooKeeper:面向分布式应用的分布式协调服务

ZooKeeper是一个面向分布式应用的分布式开源协调服务。它提供了一组简单的原语,分布式应用可以基于这些原语实现更高层次的服务,如同步、配置维护以及组和命名。其设计目标是易于编程,并采用了类似文件系统目录树结构的熟悉数据模型。它运行在Java环境中,同时支持Java和C语言的绑定。

众所周知,协调服务很难正确实现。它们特别容易出现诸如竞态条件和死锁等错误。ZooKeeper背后的动机是减轻分布式应用程序从头实现协调服务的责任。

设计目标

ZooKeeper非常简单。 ZooKeeper允许分布式进程通过共享的层次化命名空间相互协调,该命名空间的组织方式类似于标准文件系统。命名空间由数据寄存器(在ZooKeeper术语中称为znodes)组成,这些znodes类似于文件和目录。与为存储而设计的典型文件系统不同,ZooKeeper数据保存在内存中,这意味着ZooKeeper可以实现高吞吐量和低延迟。

ZooKeeper的实现高度重视高性能、高可用性和严格有序的访问。ZooKeeper的性能特性使其能够应用于大型分布式系统。其可靠性特性避免了单点故障问题。严格的顺序性意味着可以在客户端实现复杂的同步原语。

ZooKeeper具备复制能力。 就像它所协调的分布式进程一样,ZooKeeper本身设计为在一组称为集群的主机上实现复制。

ZooKeeper Service

构成ZooKeeper服务的服务器必须彼此知晓。它们在内存中维护状态映像,同时在持久存储中保存事务日志和快照。只要大多数服务器可用,ZooKeeper服务就会保持可用状态。

客户端连接到单个ZooKeeper服务器。客户端通过维护一个TCP连接来发送请求、接收响应、获取监视事件以及发送心跳。如果与服务器的TCP连接中断,客户端将连接到其他服务器。

ZooKeeper是有序的。 ZooKeeper会为每个更新操作打上一个反映所有ZooKeeper事务顺序的编号。后续操作可以利用这个顺序来实现更高层次的抽象,如同步原语。

ZooKeeper速度极快。 它在"读多写少"的工作负载中表现尤为出色。ZooKeeper应用程序运行在数千台机器上,在读写比例约为10:1(读操作远多于写操作)的场景中性能最佳。

数据模型与层级命名空间

ZooKeeper提供的命名空间与标准文件系统非常相似。名称是由斜杠(/)分隔的路径元素序列。ZooKeeper命名空间中的每个节点都由一个路径标识。

ZooKeeper的层级命名空间

ZooKeeper's Hierarchical Namespace

节点与临时节点

与标准文件系统不同,ZooKeeper命名空间中的每个节点既可以关联数据,也可以拥有子节点。这就像允许文件同时作为目录存在的文件系统。(ZooKeeper被设计用于存储协调数据:状态信息、配置、位置信息等,因此每个节点存储的数据通常很小,范围在字节到千字节之间。)我们使用术语znode来明确表示我们讨论的是ZooKeeper数据节点。

Znode节点维护一个状态结构,其中包含数据变更、ACL变更的版本号和时间戳,以实现缓存验证和协调更新。每次znode的数据发生变化时,版本号都会递增。例如,客户端每次获取数据时,也会接收到数据的版本信息。

命名空间中每个znode节点存储的数据都是原子性读写。读取操作会获取与znode关联的所有数据字节,而写入操作则会替换全部数据。每个节点都有一个访问控制列表(ACL),用于限制不同用户的操作权限。

ZooKeeper 还支持临时节点的概念。这些 znode 仅在创建它们的会话处于活动状态时存在。当会话结束时,znode 将被删除。

条件更新与监视

ZooKeeper支持监视器的概念。客户端可以在znode上设置监视器。当znode发生变化时,监视器将被触发并移除。当监视器被触发时,客户端会收到一个数据包,通知znode已发生变化。如果客户端与ZooKeeper服务器之间的连接中断,客户端将收到本地通知。

3.6.0 新特性: 客户端现在可以为 znode 设置永久性递归监视器,这些监视器在触发后不会被移除,并且会递归触发注册 znode 及其所有子节点上的变更通知。

保证

ZooKeeper非常快速且非常简单。然而,由于它的目标是作为构建更复杂服务(如同步)的基础,它提供了一系列保证。这些保证包括:

简单API

ZooKeeper的设计目标之一是提供非常简单的编程接口。因此,它仅支持以下操作:

实现

ZooKeeper Components展示了ZooKeeper服务的高层组件架构。除请求处理器外,组成ZooKeeper服务的每台服务器都会复制自身包含的各个组件副本。

ZooKeeper Components

复制数据库是一个包含完整数据树的内存数据库。更新操作会被记录到磁盘以确保可恢复性,并且在应用到内存数据库之前,写入操作会被序列化到磁盘。

每个ZooKeeper服务器都为客户端提供服务。客户端会连接到具体的某一台服务器来提交请求。读取请求由每个服务器数据库的本地副本来处理。而会改变服务状态的请求,即写入请求,则通过一致性协议来处理。

作为协议的一部分,所有来自客户端的写请求都会被转发到一个称为leader的服务器。其余的ZooKeeper服务器称为followers,它们接收来自leader的消息提案并就消息传递达成一致。消息层负责在故障时替换leader,并将followers与leader同步。

ZooKeeper使用一种自定义的原子消息协议。由于消息传递层具有原子性,ZooKeeper能够确保本地副本永远不会出现分歧。当领导者接收到写入请求时,它会计算写入操作应用后系统的状态,并将其转换为捕获这一新状态的事务。

用途

ZooKeeper的编程接口设计得非常简洁。尽管如此,您仍可利用它实现更高阶的操作,如同步原语、组成员管理、所有权控制等功能。

性能

ZooKeeper旨在实现高性能。但它真的做到了吗?雅虎研究院ZooKeeper开发团队的测试结果表明确实如此。(参见读写比例变化时的ZooKeeper吞吐量)在读操作远多于写操作的应用场景中,其性能表现尤为出色,因为写操作需要同步所有服务器的状态。(对于协调服务来说,读操作多于写操作是典型情况。)

ZooKeeper Throughput as the Read-Write Ratio Varies

ZooKeeper读写比例变化时的吞吐量展示了ZooKeeper 3.2版本在双核2Ghz至强处理器和两块SATA 15K RPM硬盘服务器上的吞吐量图表。其中一块硬盘专用于ZooKeeper日志设备,快照则写入操作系统盘。写入请求为1K大小的写操作,读取为1K大小的读操作。"Servers"表示ZooKeeper集群规模,即构成服务的服务器数量。另有约30台服务器用于模拟客户端。ZooKeeper集群配置为领导者节点不接受客户端连接。

注意

之前的3.1版本相比,3.2版本的读写性能提升了约2倍。

基准测试也表明它非常可靠。Reliability in the Presence of Errors展示了部署环境对各种故障的响应情况。图中标注的事件如下:

  1. 跟随者的故障与恢复
  2. 不同跟随者的故障与恢复
  3. 领导者故障
  4. 两个跟随者的故障与恢复
  5. 另一个领导者的失败

可靠性

为了展示系统在注入故障时的行为随时间的变化,我们运行了一个由7台机器组成的ZooKeeper服务。我们运行了与之前相同的饱和基准测试,但这次将写入百分比保持在恒定的30%,这是我们预期工作负载的保守比例。

Reliability in the Presence of Errors

从这张图中可以得出几个重要观察结果。首先,如果跟随节点快速故障并恢复,那么ZooKeeper仍能保持高吞吐量。但更重要的是,领导者选举算法使系统能够快速恢复,从而避免吞吐量大幅下降。根据我们的观察,ZooKeeper选举新领导者耗时不到200毫秒。第三,当跟随节点恢复后,一旦它们开始处理请求,ZooKeeper就能再次提升吞吐量。

ZooKeeper项目

ZooKeeper已在众多工业应用中成功应用。在雅虎,它被用作雅虎消息代理的协调和故障恢复服务,该系统是一个高度可扩展的发布-订阅系统,管理着数千个主题以实现数据复制和传输。雅虎爬虫的抓取服务也使用ZooKeeper来管理故障恢复。雅虎的多个广告系统同样利用ZooKeeper来实现可靠服务。

我们鼓励所有用户和开发者加入社区并贡献他们的专业知识。更多信息请参阅Zookeeper Project on Apache