zookeeper:分布式一致性协调服务
zookeeper是开源的分布式一致性协调服务。
- 它提供了一系列简单的原语,能够让分布式应用基于它实现更高层次的分布式锁、配置管理、分组和构建命名空间的功能。
- 它被设计成易于编程,用数据模型的风格实现了我们熟悉的文件系统树状结构组织目录。
- 它是基于Java语言去运行,并且代码是用C和Java实现的。
由于分布式协调服务很难做好,它们特别容易出错,例如竞态条件和很容易出现死锁。所以,zookeeper一开始的设计目标就是把分布式协调服务做为一个公共的组件,从而让分布式应用从一开始就必须构建分布式协调服务中解放出来,聚焦于应用自身的核心逻辑。
设计目标
zookeeper是简单的
zooKeeper允许分布式服务通过与标准文件系统组织类似的共享层次命名空间相互协调。
命名空间由znodes组成,类似于文件和目录。与为存储而设计的传统文件系统不同,zookeeper的数据保存在内存中,这意味着zookeeper可以获得高吞吐量以及更低的延迟。
zookeeper在高性能、高可用性、严格有序访问方面的良好表现使得它可以应用在大规模、分布式系统。可靠性方面使其不会成为单点故障,严格的顺序支持可以给client更多的复杂同步原语。
zookeeper是易复制
像zookeeper一致协调的分布式应用一样,zookeeper也是被设计成在一系列机器中复制,以下是zookeeper service:
组成zookeeper服务的server必须相互知道彼此的存在,它们维护状态的镜像在内存中,以及事务日志和快照持久化存储。只要大多数server可用,zookeeper服务就会可用。
client会连接某个zooKeeper server,并会维护跟server通信的tcp连接。
通过这个连接,它能够向server发送请求、获取响应、获取监听事件并定期向server发送心跳。如果client与当前server的连接断开了,client会连接另一个server。
zookeeper是有序的
为了使得zookeeper所有的事务有序,zookeeper会给每次的更新操作加上时间戳。从而使得后续的操作能够基于事务有序实现高级的抽象,类如同步原语。
zookeeper是非常快的
zookeeper在读比较多的场景下会尤其快。zookeeper能够在几千台机器的集群中运行,在读远远多于写并且在读写比率为10:1的场景下,它表现是最好的。
数据模型和层次命名空间
zookeeper提供的名称空间非常类似于标准文件系统。一个名字是由斜杠(/)分隔的路径元素序列。zookeeper名字里的每个节点空间由路径标识。
zookeeper的层叠命名空间
节点和临时节点
与标准文件系统不同,zookeeper命名空间中的每个节点和孩子节点一样都可以有数据。这就像一个文件系统,它允许文件也同时是一个目录。(zookeeper用于存储协调数据:状态信息、配置、位置信息等,所以在每个节点上存储的数据通常很小,一般在字节到千字节的范围内)
zookeeper用znode来表示数据节点,znodes会维护一个状态结构,其中包含用于数据更改的版本号、ACL改动和时间戳,以允许缓存验证和协调更新。每次znode的数据发生变化,版本号增加。无论什么时候,client检索数据时,它也会接收server返回的数据的版本信息。
存储在命名空间中的每个znode中的数据读写都是原子,能够保证读写都是符合强一致性的。每个znode都维持着一个访问权限控制列表(ACL),限制哪些用户能够读写。
zookeeper也有临时节点的概念。这些znode存在的时间与创建znode的会话是活动的。当会话结束时,删除znode。临时节点可以用于一些你想要自定义的场景。
条件更新和监听
zookeeper支持监听的概念。client可以设置成监听在某个znodes上。
当监听的znode内容被更新后,对应的监听会被触发并被删除。
当监听被触发时,client会接收到表示znode已更改的数据包。
当client与zookeeper集群任何一个server断开连接,client会接收到本地的一些提醒,这个可以被用于待定的场景。
保证
zookeeper是非常快、非常简单。因为zookeeper的目标是要成为构建更复杂服务的基础,类如一致性协调,所以zookeeper提供了一系列的保证:
顺序一致性 - 来自客户端的更新将按发送的顺序实施。
原子性 - 更新要么成功,要么失败,不存在部分更新的情况。
相同系统镜像 - 无论client连接到的是哪个server,它都将看到相同的系统视图。
可靠性 - 一旦更新,就会一直持久化直到有其他的client覆盖了对应的内容。
时效性 - client看到的系统视图在确定的时间限制内保证是最新的。
有关这些的更多信息,以及使用说明,可以参看TBD。
简单的API
zookeeper其中的一个设计目标就是提供非常简单的可编程接口。做为对应的结果,它仅仅只提供如下操作:
create
在目录树的某个位置创建一个节点。
delete
删除某个节点
exists
测试某个路径下的节点是否存在
get data
从某个节点中获取内容
write data
写内容到某个节点
get children
检索当前节点所有的孩子节点列表
sync
等待要传播的数据
有关这些的更深入的讨论,以及如何使用它们实现更高级别操作,可以参阅TBD。
实现
zookeeper组件显示zookeeper服务的高级组件。与请求处理器不同,组成zookeeper服务的每个server都复制它自己的每个组件的副本。
ZooKeeper Components
复制的数据库是一个基于内存的包含所有数据树状结构的数据库,更新的操作会记录到磁盘便于失败后恢复。另外写会先同步到磁盘中,之后才会更新内存中的内容。
每个zookeeper的server都能够服务很多client,一个client任何时候仅仅只能连接一个server去发送请求。
针对读请求,server会直接把内存中的数据返回给client。对于所有涉及改变服务状态、写请求会被一致性协议所处理。
作为一致性协议的一部分,所有来自client的写请求都被转发到leader server。
所有其他follower server接收来自leader的信息建议,并就信息传递达成一致。
消息传递层负责在leader出现故障后选举替代leader,并向所有follower同步最新leader信息。
zookeeper使用自定义原子消息传递协议。由于消息层是原子的,
zookeeper可以保证本地的样本不会出现不一致的情况。当server接收一个写请求时,它会计算当前请求被处理后当前系统的状态,并用事务性的方式获取最新的状态。
使用
虽然zookeeper的编程接口非常简单,但是有了它你可以实现更高阶的操作。类如,同步原语、组成员关系、所有权等等。很多分布式应用已经使用zookeeper了,类如HDFS、YARN等,并且zookeeper已经hadoop社区最核心的组件。
性能
zookeeper是诞生于雅虎的开发团队,在设计之初就追求非常好的性能。,、从测试的结果来看zookeeper的性能确实是非常好的。在读取的场景中,它的性能尤其高超过写操作,因为写操作涉及同步所有服务器的状态。(对于分布式协调场景,读场景会比写场景多很多)
zookeeper吞吐量随着读写比的变化而变化
以上zookeeper的吞吐量为zookeeper发行版3.2版本运行在双核2Ghz Xeon和两个SATA 15K RPM的驱动的服务器上。其中一个驱动器用作专用的管理员日志设备。快照是写好的到操作系统驱动器。写请求为1K写,读请求为1K读。servers表示zookeeper集合的大小,即组成服务的服务器的数量。大约有30台其他服务器用于模拟client。zookeeper被配置为leader server不允许来自client的连接。
In version 3.2 r/w performance improved by ~2x compared to the previous 3.1 release.
可靠性
为了显示系统在故障发生时随时间对应的行为,我们运行了一个zookeeper
服务由7台机器组成的集群。我们运行了与之前相同的饱和度benchmark,但是这次我们把写的百分比保持在30%不变,这是一个保守的比率预期的工作负载。
我们运行了一个由7台机器组成的zookeeper集群,为了显示系统行为随着时间的变化。我们运行了与之前相同的饱和度基准,但是这次我们把写的百分比保持在30%不变,这是一个我们期待的保守的工作负载读写比率。
错误存在时的可靠性
这是这张图中的一些重要观察结果。
如果follower失败并恢复很快,zookeeper依然能维持较高的吞吐量尽管follower失败了。
但也许更重要的是,leader选举算法允许系统恢复足够快,以防止吞吐量大幅下降。在我们的观察中,zookeeper消耗少于200ms来选举新的leader。
随着follower的恢复,zookeeper在有请求需要处理时,吞吐量再次提升。
zookeeper项目
zookeeper已成功应用于许多工业领域。
它被应用于Yahoo!做为Yahoo!的一致性协调和故障恢复服务。
Message Broker,这是一个高度可伸缩的发布-订阅系统,用于管理数千个主题的内容复制和数据的传输。它被Yahoo!爬虫服务使用,它也管理服务失败和恢复。
一些雅虎!广告系统也使用zookeeper来实现可靠服务。
鼓励所有用户和开发人员加入社区并贡献他们的专业知识。有关更多信息,请参见Apache上的zookeeper项目。