ZooKepper: 一个分布式应用的分布式协调服务(Distributed Coordination Service)

分布式服务难以管理, 他们容易造成死锁和竞争, ZooKepper的动机就是为了让分布式应用的责任从协调服务中解脱出来.

设计目的

ZooKeeper很简单

ZooKeeper通过共享一个类似于标准文件系统的命名空间(namespace)来协调分布式进程. 命名空间包括数据注册器(data registers) - 用ZooKeeper的语法说, 叫做 znode - 他们类似于文件系统中得文件和目录. 和典型文件系统不一样的是, ZooKeeper设计不是用来做存储的, 它把数据都保存在内存中, 这意味着它可以达到高吞吐量和低延迟的效果.

ZooKeeper的实现致力于高性能, 高可用, 严格的顺序访问. ZooKeeper的性能让他可以用于大型分布式系统.它的可靠性防止出现单点故障. 严格的顺序意味着复杂的同步原语也可以在客户端实现.

ZooKeeper是可冗余的

ZooKeeper跟他协调的分布式进程一样, 它自己也是个冗余结构, 分布在多个主机上, 这叫做一个"集群"(ensemble)

ZooKeeper Service

这些组成ZooKeeper服务的服务器必须彼此之间知道对方的存在. 他们维护了一个内存中的状态图, 并将事务日志以及快照做持久化存储. 只要大部分的服务器是可用的, ZooKeeper的服务就可用

客户端连接到单独的ZooKeeper服务器. 客户端维护了一个TCP连接, 它用来发送请求, 获取响应, 获取观察时间(watch events), 发送心跳检测. 如果这个TCP连接中断了, 客户端会连接到另一个服务器.

ZooKeeper是有序的

ZooKeeper为每次更新都标上一次数字标签, 这些数字代表ZooKeeper事务的顺序. 后续操作可以利用这个顺序是先更高层次的抽象, 例如同步原语.

ZooKeeper很快

在"以读为主"(read-dominant)的工作场合速度特别快. ZooKeeper 应用跑在成千上万台机器上, 它的度性能比写性能好得多, 大约为 10:1

数据模型和分层命名空间

ZooKeeper提供的命名服务很像标准的文件系统. 一个名字就是一个由斜杠(/)分隔的路径(sequence of path).

ZooKeeper 分层命名空间

 

节点和临时节点

跟标准文件系统不一样, ZooKeeper命名空间中的检点可以和数据相关联. 就像是一个允许一个文件同时是一个目录的文件系统. (ZooKeeper是设计用来存储协调数据的: 状态信息, 配置, 位置信息, 等等. 所以每个节点中得数据通常都很小, 从字节到KB之间都有). 我们用术语 znode 来说明谈论是ZooKeeper的数据节点.

Znode维护了一个状态结构, 它包括数据变更的版本号, ACL变化, 时间戳, 用于缓存验证和协调更新. 每当znode的数据改变, 版本号都会增加. 例如, 每当客户端获取数据时, 他同事也会获取数据的版本号.

Znode中的数据是自动可读可写的. 读操作获得跟一个znode相关的数据字节, 写操作覆盖所有的数据. 每个节点都有一个访问控制列表(ACL)用来限制谁可以读写. 

ZooKeeper也有临时节点的概念. 这些znode只在创建它的会话期间存在. 当会话结束, 节点就会被删除. 临时节点在你想实现[tbd]的时候非常有用.

条件更新和监视器

ZooKeeper有监视器(watches). 客户端可以在znode上设置一个监视器. 一个监视器在znode变化时会被触发并删除. 当一个监视器被触发, 客户端会收到一条znode变化的消息. 如果客户端和ZooKeeper服务器间的连接中断了, 客户端会收到一个本地通知. 这些可以用于[tbd]

保证

ZooKeeper很快很简单. 因此他的目的, 思想, 都是作为更复杂的服务的构建基础, 例如同步, 它提供了一系列的保证. 如下:

  • 顺序一致性 - 客户端的更新会按照他们发送的顺序应用.
  • 原子性 - 更新不是完全成功就是完全失败. 没有部分结果.
  • 单一系统映像 - 无论连接到哪个服务器, 客户端都会看到相同的服务视图.
  • 可靠性 - 当一次更新执行了, 它将会永远存在, 直到客户端覆盖这次更新.
  • 及时性 - 客户端看到的系统肯定是最新的

简单的API

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

create: 创建一个节点

delete: 删除一个节点

exists: 测试节点是否存在

get data: 从节点中读取数据

set data: 将数据写入到一个节点中

get children: 获取节点的子节点

sync: 等待数据传输

实现

ZooKeeper组件图展现了ZooKeeper服务的高级组件. 通过请求处理器, 组成ZooKeeper服务的每个服务器都会复制组件的副本.

ZooKeeper Components

这个冗余数据库是一个包含了整个数据树的内存数据库. 更新日志会被记录到硬盘中, 以用于恢复时使用, 而写操作在他们被应用到内存数据库之前, 会被序列化到硬盘中.

客户端的读请求服务是由服务器数据库的本地副本提供的.改变服务状态的请求, 写请求, 则由一个一致性协议进行处理.

一致性协议规定, 所有的写请求都会被转发到同一个叫领袖(Leader)的服务器. 剩下的服务器叫做随从(Follower), 他们从领袖接收命令, 并进行一致性的消息传递. 消息层在出故障时会重新选举领袖, 并将随从与领袖同步.

ZooKeeper使用一个自定义的原子消息协议.因为消息层是原子的, 所以ZooKeeper保证了本地副本的正确性. 当领袖接收到一个写请求, 他会计算系统状态来决定何时执行写请求, 并将其转化为一个捕获这个新状态的事务.

使用

ZooKeeper的编程接口非常简单, 你可以同他们实现更高层次的操作, 例如同步原语, 组员关系, 所有权, 等等. 有些分布式应用已经用它来: [tbd]

性能

ZooKeeper性能很高. 从ZooKeeper开发组在雅虎的调查就能看出来(参见 ZooKeeper Throughput as the Read-Write Ratio Varies). 特别是在读操作远大于写的情况下性能特别好, 这是因为写操作包含了同步所有服务器状态.(读远大于写是协调服务的典型应用)

ZooKeeper Throughput as the Read-Write Ratio Varies

 

 

这个图的ZooKeeper是3.2版本, 运行环境是至强双核2Ghz, 两块SATA 15K RPM硬盘. 其中一块硬盘专门用于存储ZooKeeper日志. 快照是写入到OS驱动器中. 写和读请求都是1K的大小. "服务器"(Servers)指的是ZooKeeper集群的大小, 他们共同组成了ZooKeeper服务. 另外还有大约30台服务器用来模拟客户端. ZooKeeper集群被设置为领袖不接收客户端请求.

注意: 3.2 版本和 3.1 版本相比, 读/写 性能提高了~2x

标准测试程序也表明ZooKeeper是可靠地. Reliability in the Presence of Errors 显示了部署是如何响应不同的故障的. 如下:

  1. 随从的故障与恢复
  2. 不同随从的故障与恢复
  3. 领袖的故障与恢复
  4. 两个随从的故障与恢复
  5. 另一个领袖的故障与恢复

可靠性

为了展示系统在遇到故障时的行为, 我们跑了一个7台机器组成的ZooKeeper服务. 我们跑了一个和原来一样的标准测试程序, 但这次将写请求保持在固定的30%, 这是一个保守的期望负载.

Reliability in the Presence of Errors

 

这个图中有几个很重的关注点. 第一, 假如随从出故障并且很快恢复, 尽管出故障了ZooKeeper也能承受很高的吞吐量. 更重要的是系统的领袖选举算法保证了故障恢复够快, 这样吞吐量就不会有实质的下降. 在我们的观察中, ZooKeeper选举领袖的时间低于200ms.最后, 因为随从故障恢复了, ZooKeeper在重新开始处理请求后, 吞吐量又开始提高.

原文: http://zookeeper.apache.org/doc/trunk/zookeeperOver.html

posted on 2014-02-18 16:57  ZimZz  阅读(722)  评论(2编辑  收藏  举报