篇幅目录:
Zookeeper:一个分布式应用的分布式协调服务
设计目标
数据模型和分级命名空间
节点和临时节点
条件更新和监控
保证
简单的API
使用
性能
可靠性
Zookeeper项目
--------------------------------------------------------------------
Zookeeper:一个分布式应用的分布式协调服务
Zookeeper是一个分布式,开源的分布式应用协调服务。它暴露出一组简单的接口,分布式应用可以在此之上实现高级服务比如:同步、配置维护、分组和命名。它设计的易于开发,使用的类似文件系统中的目录树结构的数据模型。它运行在java上,并且有针对java和C的绑定。
众所周知,协调服务很难处理正确。它特别容易发生错误诸如竞争条件和死锁。Zookeeper的动机是减轻分布式应用的责任并且从头实现了协调服务。
设计目标
Zookeeper是简单的。它允许分布式程序通过一个类似于标准的文件系统的共享的命名空间体系进行协作。命名空间由被叫做znodes的数据注册者组成,用Zookeeper的说法,它们类似于文件和目录。不像传统的文件系统,znodes被设计用于存储,Zookeeper的数据被保存在内存中,这意味着Zookeeper可以达到高吞吐量和低延迟量。
Zookeeper的实现对高性能、高可用、和严格顺序访问提供了很大的好处。Zookeeper的性能方面意味着它可以被用在大型的分布式系统中,可用性方面使它不能成为单点故障。严格的顺序访问意味着复杂的同步操作可以在客户端进行实现。
Zookeeper是重复的。就像它协调的分布式程序一样,Zookeeper本身的目的是在一组机器集合中进行复制。
组成Zookeeper服务的所有server必须互相知道,它们维持了一个内存中的状态图,在持久存储中和事物日志以及快照一起呗存储。只要大部分server是可用的,那么Zookeeper将会是可用的。
客户端连接单个Zookeeper 服务,客户端会维持一个TCP连接,通过这个连接发送请求,接受responses,获得监视事件,发送心跳。如果这个TCP连接断掉了,客户端将会去连接另外一个服务。
Zookeeper是有序的。Zookeeper用一个数字标记每一个更新,这反映了Zookeeper事物的发生顺序。后续的操作可以根据这个顺序实现更高级别的抽象,比如事物操作。
Zookeeper是快速的。在“读取占主导”的工作负载中,它的速度尤其快。Zookeeper应用运行在数千台机器上,它在读操作比写操作多的情况下性能最好,这个比例为10:1
数据结构&命名空间体系
Zookeeper提供的命名空间和标准的文件系统很类似。名字是一个由/分隔符 分隔的路径。每一个Zookeeper的命名空间中的节点都有这个路劲作为唯一标识。
Nodes and ephemeral nodes
不像标准的文件系统,Zookeeper 命名空间中的每一个节点包括子节点都可以有数据关联。就像是一个文件系统允许文件也可以作为一个目录。(Zookeeper 被设计用来存储数据:状态信息,配置,本地信息,等等..,所以存储在每个节点上的数据通常都很少,从数byte到数kb之间。)我们使用znode这个术语来说明我们正在讨论的是ZooKeeper的数据节点。
znode维护一个统计结构,其中包括用于数据更改、ACL更改和时间戳的版本号,以允许缓存验证和协调更新。每当znode的数据变化,这个版本号增加。例如,每当客户端检索数据时,它也会接收到数据的版本。
数据存储在命名空间中的znode上的读和写都是原子性的。读操作获取znode关联的所有数据,写操作会替换所有数据。每个节点都有一个访问控制列表(ACL)来限制谁能做什么。
Zookeeper也有临时节点的概念。这些节点随着创建znode的session激活开始,当这个session结束后就会被删除掉。当你想实现[tbd]功能时,临时节点是很有用的。
条件更新和监控
Zookeeper支持watches的概念。客户端可以在一个节点上设置一个watch。这个监控会触发和删除当这个节点变化的时候。当这个watch被触发时客户端就会收到节点变化的消息。而且如果这个客户端和Zookeeper服务器的连接断了,那么客户端会收到一个本地通知,这个可以用来实现[tbd]。
保证
Zookeeper是很快和简单的。尽管它的目标是成为构建更复杂的服务的基础,例如同步,它提供了一组保证。他们是:
顺序一致性:来自客户端的更新将按照发送的顺序应用。
原子性:更新要么成功要么失败,不会有中间结果
统一性:而不管它连接到何种服务器,客户端将看到服务的相同视图。
可靠性:一旦应用了更新,它将从那个时间一直持续到客户端重写更新。
及时性:客户端对系统的视图保证在一定的时间内是最新的。
简单的API
Zookeeper的一个设计目标是提供非常简单的编码api,所以,它仅支持下面这些操作
create
在树中的一个位置创建一个节点
delete
删除一个节点
exists
测试某个位置是否存在一个节点
get data
从节点读取数据
set data
向节点设置数据
get children
接受节点的一组子节点数据
sync
等待数据传播(同步)
关于这些问题的更深入的讨论,以及如何使用它们来实现更高级别的操作,请参考[tbd]
实现
Zookeeper组件展示了Zookeeper 服务的高级组件。除了请求处理器之外,组成ZooKeeper服务的每一个服务器都复制自己的每一个组件的副本。
复制的数据库是一个包含整个数据树的内存数据库。更新被记录到磁盘上,以便恢复,并且在将它们应用到内存数据库之前,将其写到磁盘上。
每个Zookeeper服务向客户端提供服务。客户端连接到一个服务器来提交irequest。读取请求是从每个服务器数据库的本地副本提供服务的。请求会改变服务端的状态,写请求会被一个协议处理。
作为协议的一部分,所有来自客户的写请求都被转发到一个名为“leader”的服务器上。其余的Zookeeper服务器,被称为followers,他们接收来自leader的消息命令,通过消息传递命令达成一致。消息传递层负责更换无效的leader,并将followers和leaders同步
动物园管理员使用定制的原子消息传递协议。由于消息传递层是原子的,所以ZooKeeper可以保证本地副本不会有分歧。当领导者收到一个写请求时,它会计算出当写入时系统的状态是什么并传递给一个捕捉新状态的事物。
使用
Zookeeper的编程接口故意设计的很简单。然而,有了它,你可以实现更高级的顺序操作,比如同步、分组成员关系、所属关系等等。一些分布式应用程序已经将其应用于:tbd:要了解更多信息,请参见tbd
性能
Zookeeper被设计成高性能的。但是它真的是吗?Zookeeper在Yahoo!的研发团队给出的结果。研究表明确实如此。(看下图:随着读写比率变化的Zookeeper吞吐量变化图),读操作比写操作多时它表现的特别高效,因为写操作涉及到同步所有服务器的状态。(读操作占主导的经典例子是协调服务。)
上图的特性是Zookeeper的吞吐量图,是由两个2 Ghz Xeon和两个SATA 15 K RPM驱动器运行的Zookeeper3.2版本的吞吐量图。一个驱动器是Zookeeper日志服务专用的。快照被写到OS驱动器。写请求是1 K写,读是1 K读。“services”表明了组成了该服务的服务器数量。大约有30个其它服务器被用来模拟客户端。Zookeeper集群被配置为客户端不可连接到leaders
NOTE:3.2版本的读写性能比3.1版本提升了2倍。
基准还表明,它也是可靠的。在出现错误时的可靠性显示了部署如何响应各种故障。图中所列的事件如下:
1. 发生错误和恢复follower
2. 发生错误和恢复另外一个follower
3. leader节点发生错误
4. 2个followers发生错误和恢复
5. 另外一个leader发生了错误
可靠性
为了显示系统的行为,随着时间的推移,我们运行了一个由7台机器组成的Zookeeper服务。我们运行的饱和度和以前一样,但是这次我们把写的百分比保持在30%不变,这是我们预期的工作负载。
Reliability in the Presence of Errors |
这里有一些很重要的观察结果。第一,follower的失败和恢复是很迅速的,Zookeeper可以保持高的吞吐量尽管发生了错误。但是更重要的是,leader选举算法允许系统快速恢复,以防止吞吐量大幅下降。在我们的观察中,Zookeeper用了少于200ms选举leader。第三,follower 恢复后,Zookeeper就可以再次提高处理请求的吞吐量。
Zookeeper 项目
Zookeeper被成功应用于很多的工业应用中,在雅虎中,作为协调和错误恢复服务。Message Broker,它是一个高度可伸缩的发布-订阅系统,管理数千个用于复制和数据交付的主题。它被Yahoo的抓取服务使用!爬行器,它还管理故障恢复。大量的Yahoo !广告系统还使用Zookeeper来实现可靠的服务。
鼓励所有用户和开发人员加入社区并贡献他们的专业知识。请参阅Apache上的Zookeeper项目以获得更多信息。