ZooKeeper:分布式应用的分布协调服务
ZooKeeper是一个为分布式应用提供的分布的开源的协调服务。它暴露一组简单的原子操作,分布式系统可以在这之上为同步,配置管理,和组和命名实现更高级的服务。它被设计来编程简单,并且使用一个跟文件系统相似的数据模型。它在Java中运行并且绑定Java和C。
协调服务众所周知地非常难正确。它们特别易于竞争条件和死锁的错误。ZooKeeper后面的动机是减轻分布式应用
从头开始实现协调服务的责任。
设计目标
ZooKeeper是简单的。ZooKeeper允许分布式的进程彼此协调通过一个共享的结构化的命名空间,这个命名空间组织结构和标准的文件系统相似。这个命名空间包括数据注册器 - 称为znodes,用ZooKeeper的说话 - 并且他们和文件和文件夹相似。和典型的文件系统设计用来存储不同的是,ZooKeeper的数据保存在内存中,这意为着ZooKeeper可以达到很高的吞吐量和低延迟。
ZooKeeper实现给高性能,高可用,严格的顺序访问增加了一个保险。ZooKeeper的高性能方面意为着它可以被用在大的分布式系统。可靠性避免它成为一个失败单点。严格的访问顺序意为复杂的同步原语可以在客户端实现。
ZooKeeper是可复制的。和它协调的分布的进程一样,ZooKeeper本身也打算是可复制的通过一组称为集群的主机。
ZooKeeper Service
组成ZooKeeper的服务的服务器必须都知道彼此。他们维护一个在内存中的状态镜像,和一个事务日志和一个持久存储中的快照。只要大多数是可用的,ZooKeeper服务就是可用的。
客户端连接到一个单独的ZooKeeper服务器。客户端维护一个TCP连接,通过这个链接它发送请求,获取响应,获取watch事件,并且发送心跳。如果到服务器的TCP连接中断,客户端将连到另一个不同的服务器。
ZooKeeper是有序的。ZooKeeper用一个数字来记录每一次更新,这个数字反映了所有ZooKeeper事务的顺序,随后的操作可能使用这个顺序来实现 更高级的抽象,比如同步原语
ZooKeeper是快速的。它相当的快在读为主的工作场景中。ZooKeeper应用运行在成千上万个机器上,并且它表现很好在读比写普遍的,比例大约10:1的时候。
数据模型和分层次的命名空间
ZooKeeper提供的命名空间非常像一个标准的文件系统。一个名字是一系列的被/分开的路径元素,在ZooKeeper中每一个节点被标示为一个路径。
ZooKeeper's Hierarchical Namespace
节点和短暂的节点
和标准的文件系统不同,每一个ZooKeeper中的节点可以有数据和它关联,子节点也一样。就像一个文件系统允许一个文件是一个目录。(ZooKeeper被设计用来存储协调数据:状态信息,配置,地址信息,等等。所以存储在每一个节点的数据通常很小,在字节和千字节的范围。)我们使用znode来使我们清楚我们正在讨论ZooKeeper的数据节点。
Znodes维护一个数据结构包括数据改变,ACL改变,的版本号和时间戳,来允许缓存检验和协调更新。每次一个znode的数据改变,版本号增加。例如,当客户端检索数据,它也收到数据的版本。
在命名空间里的每一个节点存储的数据原子地读和写。读得到一个znode关联的所有的数据字节,并且写替换所有的数据。每一个节点有一个访问控制列表(ACL)来限制谁能做和做什么。
ZooKeeper也有短暂节点的概念。这些节点存在只要创建它们的会话是活跃的。当会话结束,这个节点也就被删除。短暂的节点是有用的当你想实现[tbd]。
条件更新和监视器
ZooKeeper支持监视的概念,客户端可以在znode上设置一个监视器,监视器将被触发和删除当znode改变的时候。当监视器被触发的时候,客户端收到一个数据包说明znode已经被改变。并且如果客户端和zookeeper服务器之间的链接已经断掉,客户端将会收到一个本地的通知,这些可以被用来实现tbd.
担保
ZooKeeper是非常快和简单的。尽管它的目标是构建例如同步这样更复杂服务的基础,它提供了一组保证它们是:
- 顺序的一致性 - 来自客户端的更新将会按照它们发送的顺序应用。
- 原子性 - 更新要么成功要么失败,没有部分结果。
- 单一系统映象 - 客户端将会看到服务端相同的视图,不管它连的是那一个服务端。
- 可靠性 - 一旦应用了一个更新,它将会从更新的时刻持续到另一个更新覆盖它。
- 时效性 - 系统的客户端视图保证是最新的在一定的时间内。
有关这些的更多信息,并且它们怎么被使用,参考[tbd]
简单的API
ZooKeeper的其中一个设计目标就是提供一个简单的编程接口,作为结果,它只提供了这些操作:
create
在树的一个位置上创建一个节点
delete
删除一个节点
exists
检验一个节点是否在这个位置
get data
从节点读取数据
set data
往节点写数据
get children
检索一个节点的孩子节点列表
sync
等待数据被传播
对于这些更深入的讨论,和他们怎么被用来实现更高级的操作,请参考[tbd]
实现
ZooKeeper Components展示了ZooKeepere服务更高级的组件,除了请求处理器,每个组成ZooKeeper服务的服务器上复制自己的每个组件的副本。
ZooKeeper Components
复制的数据库是一个内存数据库,包含了整个数据树。更新被记录到硬盘为了恢复。并且写操作被序列化到磁盘上在它应用到内存数据训之前。
每一个ZooKeeper服务器给客户端提供服务,客户端连到其中一个服务器来提交请求。请请求被每一个服务器的数据库的本地副本提供服务。改变服务器状态的请求,写请求,被一致协议处理。
作为一致性协议的一部分,所有从客户端来的写请求被推送到一个服务器,就是所谓的领导者。剩下的其它ZooKeeper服务器,就是追随者,它们从领导者接收消息提议,并同意消息传递。消息层负责更换失效的领导者并且同步领导者和追随者。
ZooKeeper使用自定义的原子消息协议。因为消息层是原子的,ZooKeeper可以保证本地复制永远不会出现分歧。当领导者收到一个写请求,它计算当更新被应用时系统处于怎么状态,并且转换这个请求为到一个事务中来捕获这个状态。
使用
ZooKeeper的编程接口相当简单,利用它你可以实现更高级的顺序操作,例如同步原语,群成员管理,所有权,等待。一些分布式系统已经使用它来[],更多信息,参考[tbd]。
性能
ZooKeeper是设计为了高性能的。但是是真的么?来自Yahoo! Research的ZooKeeper的开发团队的结果表示它是(参考ZooKeeper Throughput as the Read-Write Ratio Varies.)。在读比写多的应用中它有更高的性能,因为写涉及同步所有所有服务器的状态。
ZooKeeper Throughput as the Read-Write Ratio Varies
图片ZooKeeper Throughput as the Read-Write Ratio Varies是一个运行在dual 2Ghz Xeon和两个15K转的SATA驱动器的3.2版本的ZooKeeper的吞吐图。其中一个驱动器专门用来作为ZooKeeper的日志记录设备,快照被写入到系统驱动器。写请求是1k读请求也是1k."Servers"表示ZooKeeper集群的数量,组成服务的服务器数量。大概30个其它有服务器用来模拟客户端。这个ZooKeeper被配置成领导者不允许和客户端相连。
注意:
3.2版本的读写性能相比之3.1版本的性能提高了大约2倍
基准测试也表示了它是可靠的。Reliability in the Presence of Errors展示了如果对各种故障做出响应。图片中标记的事件是下面这些:
1. 追随者的失效和恢复。
2. 不同的追随都的失效和恢复。
3. 领导者的失效
4. 两个追随者的失效和恢复
5. 另一个领导者的失效
可靠性
为了展示系统随着时间注入失败的的行为,我们运行了一个7台机器组成 的ZooKeeper的服务。我们之前运行过这个基准测试。但是这次我们保持写的比例在30%,这是我们期望的工作负荷的保守比例。
Reliability in the Presence of Errors
从这个图中可以看到几个重要言论。首先,如果追随者失效并且很快地恢复,ZooKeeper可以维持一个高的吞吐量尽管有失效。但是也许更重要的是。领导者的选举逻辑允许系统很快地恢复来避免吞吐量下降的太厉害。在我们的观察中,ZooKeeper消耗低于200ms来选出一个新领导者。当追随者恢复,ZooKeeper可以再次提升吞吐量一旦它们再次处理请求。
The ZooKeeper Project
ZooKeeper已经成功地应用在很多工业应用中。它在Yahoo!为Yahoo!的Message Broker作为协调和失效恢复服务来使用。它是一个高度可扩展的发布订阅系统管理者成千上万个主题为了复制和数据传递。它被用在Yahoo!的Fetech Service,它也管理失效恢复。许多的Yahoo!广告系统也使用ZooKeeper来实现可靠性服务。
所有的用户和开发者被鼓励加入社区并贡献他们的意见。更多信息请参考 Zookeeper Project on Apache