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

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

设计目的

ZooKeeper很简单

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

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

ZooKeeper是可冗余的

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

ZooKeeper Service

Zookeeper Tutorial 1 -- Overview

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

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

ZooKeeper是有序的

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

ZooKeeper很快

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

数据模型和分层命名空间

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

ZooKeeper 分层命名空间

 Zookeeper Tutorial 1 -- Overview

节点和临时节点

跟标准文件系统不一样, 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

Zookeeper Tutorial 1 -- Overview

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

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

一致性协议规定, 所有的写请求都会被转发到同一个叫领袖(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 Tutorial 1 -- Overview

 

 

这个图的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 Tutorial 1 -- Overview

 

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

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

相关文章: