原文链接

———-

ZooKeeper 概述

ZooKeeper 一种针对分布式程序的分布式协同服务

ZooKeeper是一种分布式的,开源的分布式程序协调服务。他提供了一个简单原语的集合,分布式程序可以根据他实现服务高层次的同步,配置管理,分组和命名。他被设计为易于编程,使用类似文件系统目录树形结构的数据结构。在java中运行,同时有java和c的绑定。 协调器服务臭名昭著的难以做对。特别容易导致竞速条件和死锁。ZooKeeper背后的动机是减少分布式系统从头实现协调器服务。

设计目标

ZooKeeper是简单的。ZooKeeper允许分布式进程通过一个共享的层次分明的命名空间相互协调,这个共享的层次分明的命名空间类似标准的文件系统。命名空间包含数据寄存器-成为 - znodes,在ZooKeeper的说法中 - 类似于文件和目录。不同于典型的文件系统被设计为存储,ZooKeeper的数据存储在内存中,这意味着ZooKeeper可以获得高吞吐量和低延迟。

ZooKeeper的实现注重高性能,高可用,严格有序访问。ZooKeeper性能方向的表现意味着他可以用作大型的,分布式系统。可用性方面保证其不会陷入单点故障。严格有序意味着复杂同步原语可以在客户端实现。

ZooKeeper是主从结构。类似于他所协调的分布式进程,ZooKeeper本身旨在通过一组成为集合(ensemble)的主机进行复制。

ZooKeeper ensemble

组成ZooKeeper服务的各个服务必须都互相知道。他们维护了一个内存的状态镜像,以及持久存储中的事务日志和快照。只要大多数服务器可用,ZooKeeper的服务就可用。

客户端链接一个单独的ZooKeeper服务。这个客户端维护了一个TCP链接,这个TCP链接发送请求,得到回应,得到监视事件,发送心跳。如果这个tcp断掉,他将链接一个不同的服务。

ZooKeeper是有序的。ZooKeeper用一个反映所有ZooKeeper事务的数字来标记每次更新。随后的操作可以使用这个顺序去实现高层次的抽象,比如同步原语。

ZooKeeper是快速的。他在”读取主导(read-dominant)”工作集中特别快。ZooKeeper服务运行在数以千计的机器上,他在读取比写入更频繁的场景下表现最好,比率大概10:1。

数据结构以及分层命名空间

ZooKeeper提供的命名空间和标准文件系统非常像。名字是被斜线(/)分隔的路径元素的一个序列。每个ZooKeeper的命名空间被一个路径标志。

ZooKeeper的分层命名空间。

ZooKeeper's Hierarchical namespace

节点和临时节点

不同于标准的文件系统,ZooKeeper命名空间的每个节点可以附带数据以及子节点。就像是一个文件系统,允许文件作为目录。(ZooKeeper被设计为存储协调数据:状态信息,配置,地点信息等,因此存在于每个节点的信息通常很小,从字节到kb)。我们使用术语znode来明确我们正在讨论的ZooKeeper数据节点。

znode维护了一个状态结构,该状态结构包括数据更改的版本号、接入控制列表(ACL)更改和时间戳,以便进行缓存验证以及协调更新。每次znode数据更改,版本号更新。例如,例如科幻端接收数据时,也接收了这个数据的版本。

在一个命名空间的每个znode所存储的数据读写时原子的。读取获取与znode关联的所有数据字节,写入替换所有的数据。每个节点有一个访问控制列表(ACL)来限制谁可以操作节点。

ZooKeeper同样有临时节点的理念。只要会话创建了znode,znode处于激活状态。当会话结束znode删除。

有条件的更新和监视

ZooKeeper支持监听。客户端可以监听一个znode。znode改变时监听将要被触发或者移除。当一个监听被触发,客户端收到一个包,表明znode改变。如果客户端和ZooKeeper一个服务间的链接断掉,客户端将接收一个本地(?)通知。

3.6.0新特性:客户端可以对znode设置一个永久的,递归的监视。当znode被触发或者注册的znode的递归的孩子节点出现变动时,监视不会被删除。

保证

ZooKeeper很快而且很简单。但是基于这个目标他想成为更加复杂服务的基础,比如同步,他提供了一些列的保证。他们是:

  • 顺序一致性 - 从一个客户端发送的更新将按照客户端发送的顺序应用。
  • 原子性 - 更新要么成功,要么失败,没有中间结果。
  • 单一系统镜像 - 一个客户端将看到相同的服务镜像,无论他链接的是那个服务器。举个例子,一个客户端不会看到一个系统的老的视图,即使这个客户端使用相同的会话被装一道不同的服务器。
  • 可靠性 - 一旦一个更新被应用,他将持续存在直到一个客户端覆盖这个更新。
  • 及时性 - 客户端的视图保证在一定时间范围内是最新的。

简单的API

ZooKeeper的设计目标是提供一个简单的编程接口。因此,他仅提供这些操作:

  • create:在树中的某个位置创建一个节点
  • delete:删除一个节点
  • exists:判断某个位置是否有节点
  • get data:从node中读取数据
  • set data:向node中写数据
  • get children:检索节点的子节点列表
  • sync:等待数据传播

实现

ZooKeeper组件展示了ZooKeeper服务的高层级组件。除了请求处理器外,组成ZooKeeper服务的每个服务器复制每个组件的自己的副本。

ZooKeeper Components

被复制的数据库是一个内存数据库,包含了整个数据树。更新被记录到磁盘为了可恢复性,写入在应用到内存数据库前被序列化到磁盘。

每个ZooKeeper服务器服务客户。客户端链接到一个服务器提交请求。读请求被每个服务器数据库的本地备份服务,改变服务状态的请求,写请求被一个一致协议(agreement protocol)处理。

作为一致协议(agreement protocol)的一部分,从客户端来的所有写请求都被转发给一个单独服务,被叫做领导(leader)。其余的ZooKeeper服务被叫做跟随者(followers),跟随者(followers)接收来自领导(leader)的消息提议,并就消息传达达成一致。消息层(messaging layer)维护领导(leader)失败时的替换,同时同步领导者的跟随者。

ZooKeeper使用了一个原子性的消息协议。由于消息层(messaging layer)是原子性的,ZooKeeper可以保证本地备份不出现偏离。当领导者接收到一个写请求,他计算出当这个写被应用后系统应该处于何种状态,同时将捕获的新状态放到一个事务中传递出去。

用法

ZooKeeper的编程接口是故意设计为如此简单的。但是,使用他你可以实现高层阶的操作,比如同步原语,分组关系,隶属关系等。