ZooKeeper专题(一)——ZK介绍

By timebusker on March 31, 2017

分布式需要解决的问题?

zookeeper是什么?

所谓协调,我首先想到的是很多十字路口的交通协管,他们手握着小红旗,指挥车辆和行人是不是可以通行。如果我们把车辆和行人比喻成运行在计算机中的单元(线程), 那么这个协管是干什么的?很多人都会想到,这不就是锁么?对,在一个并发的环境里,我们为了避免多个运行单元对共享数据同时进行修改, 造成数据损坏的情况出现,我们就必须依赖像锁这样的协调机制,让有的线程可以先操作这些资源,然后其他线程等待。

我们在进程内还有各种各样的协调机制(一般我们称之为同步机制)。现在我们大概了解了什么是协调了,但是上面介绍的协调都是在进程内进行协调。 在进程内进行协调我们可以使用语言,平台,操作系统等为我们提供的机制。那么如果我们在一个分布式环境中呢?也就是我们的程序运行在不同的机器上, 这些机器可能位于同一个机架,同一个机房又或不同的数据中心。在这样的环境中,我们要实现协调该怎么办?那么这就是分布式协调服务要干的事情。

分布式系统中,说往往比做容易得多。在分布式系统中,所有同一个进程内的任何假设都不存在:因为网络是不可靠的。
比如,在同一个进程内,你对一个方法的调用如果成功,那就是成功(当然,如果你的代码有bug那就另说了),如果调用失败,比如抛出异常那就是调用失败。 在同一个进程内,如果这个方法先调用先执行,那就是先执行。但是在分布式环境中呢? 由于网络的不可靠,你对一个服务的调用失败了并不表示一定是失败的, 可能是执行成功了,但是响应返回的时候失败了。还有,A和B都去调用C服务,在时间上A还先调用一些,B后调用,那么最后的结果是不是一定A的请求就先于B到达呢? 这些本来在同一个进程内的种种假设我们都要重新思考,我们还要思考这些问题给我们的设计和编码带来了哪些影响。 还有,在分布式环境中为了提升可靠性,我们往往会部署多套服务,但是如何在多套服务中达到一致性,这在同一个进程内很容易解决的问题,但在分布式环境中确实一个大难题。

所以分布式协调远远比同一个进程里的协调复杂得多,所以类似Zookeeper这类基础服务就应运而生。 这些系统都在各个系统久经考验,它的可靠性,可用性都是经过理论和实践的验证的。所以我们在构建一些分布式系统的时候, 就可以以这类系统为起点来构建我们的系统,这将节省不少成本,而且bug也将更少。

分布式协调服务

zookeeper能够构建一个信息共享平台,保证信息的一致性和可靠性。进而有能力为大型系统业务拆分(分布式)时提供各个业务模块交互的可能, 其主要作用就是能够实现分布式系统模块之间的交互任务协调工作。 同时,它公开了一组简单的原语,分布式应用程序可以基于这些原语实现更高级别的服务,包括同步、维护配置、组和命名。 它的设计易于编程,它使用一个遵循文件系统中常见的目录树结构的数据模型。

  • ZooKeeper的主要目的是为了减轻构建健壮的分布式系统的负担。
  • 使应用的设计者不需要把时间花费在构建分布式系统上,而更加着力于本身的业务逻辑。

设计目标

ZooKeeper很简单,通过共享一个类似于标准文件系统结构的层次名称空间,Zookeeper允许分布式进程进行各自的协调。 名称空间中包含了注册数据——即所谓的znode,它们类似于文件和目录。Zookeeper不像文件系统,目的是数据存储, Zookeeper的数据存放在内存中,这意味着zookeeper具有高吞吐量和低延迟的优点。

ZooKeeper非常注重高性能、高可用性、严格有序的访问。ZooKeeper的性能方面意味着它可以在大型分布式系统中使用。 可靠性方面使它不会出现单点故障。严格有序意味着可以在客户端实现复杂的同步原语。

ZooKeeper是复制的。与它所协调的分布式进程一样,ZooKeeper本身也打算在一组主机上进行复制。

image

组成ZooKeeper集群的每个server之间都必须互相了解(通信同步)。每个server都维护状态信息(state)的内存映像, 以及持久存储中的事务日志和快照。只要ZooKeeper中的大多数server可用,ZooKeeper服务就可用。

每个client连接到ZooKeeper集群中的某个server。客户端会与之建立TCP连接,通过该TCP连接来发送请求、获取响应、获取观察事件(watch events)以及发送心跳。 如果TCP连接中断,客户端将连接到另一个server。

ZooKeeper是有序的。ZooKeeper给每次更新都贴上一个数字(zxid),这个数字反映了所有ZooKeeper事务的顺序。后续操作可以使用这个顺序来实现高级抽象,例如同步原语。

ZooKeeper速度很快,特别是在”以读为主”的工作负载中尤其快速。ZooKeeper应用程序可用在数千台机器上运行,在多读少写(比率在10:1左右)的环境中表现最好。

数据模型和有层次的名称空间

ZooKeeper提供的名称空间非常类似于标准文件系统。名称是由斜线(/)分隔的路径元素序列。在ZooKeeper名称空间中的每一个节点都是通过一条路径来标识的。

image

节点和临时节点

与标准文件系统不同的是,ZooKeeper名称空间中的每个节点(路径)都可以有与之关联的数据,子节点也如此(译注:即节点的路径自身携带数据)。 这就像是一个既文件也目录的文件系统。(ZooKeeper的目的是存储协调数据:状态信息、配置、位置信息等,所以每个节点存储的数据通常都很小,一般都是kb级别的)。 我们使用术语znode来明确说明我们正在讨论ZooKeeper数据节点。

znode维护一个包含数据更改版本号ACL更改版本号时间戳版本号stat结构,以便能够做缓存验证和协调更新。 每当znode的数据发生变化,版本号就会增加。例如,客户端每次检索数据的同时,也会接收数据的版本信息。

名称空间中,每个znode上存储的数据的读、写操作都是原子性的。读操作会获取与znode关联的所有数据,写操作会替换所有数据。 每个节点都有一个访问控制列表(ACL),限制谁可以做什么。

ZooKeeper也拥有临时节点的概念。这些节点一直存在只要创建这些节点的会话还是活跃的。当会话结束时节点被删除。当你想要实现临时节点是有用(待定)。

条件更新和监控

ZooKeeper支持监视点的概念。客户端可以在一个zonde上增加一个监控点,当znode发生变化时,监视点将被触发和删除,监视点被触发后, 客户端接收到一个包,说znode已变化了。如果客户端与ZooKeeper服务器之间的连接断了,客户端会收到一个本地的通知信息。

保证

ZooKeeper快速又简单。但由于它的目标是作为构建更复杂服务(如同步)的基础,所以它要实现一些保证。包括:

  • 序列的一致性:(Sequential Consistency)来自客户端的更新将按照发送的顺序进行应用。
  • 原子性:(Atomicity)更新要么成功,要么失败。没有部分成功、失败的结果。
  • 单系统映像:(Single System Image)无论连接到ZooKeeper中的哪个server,客户端都将看到相同的服务视图。
  • 可靠性:(Reliability)一旦应用了更新,它将一直持续到有客户端对它做了覆盖更新。
  • 时效性:(Timeliness)保证客户端看到的视图在一定的时间范围内是最新的。

Simple API

ZooKeeper的一个设计目标是提供非常简单的编程接口。因此,它只支持以下几个操作:

  • create:在树中的某个位置创建一个节点。
  • delete:删除一个节点。
  • exists:测试一个节点是否存在。
  • get data:读取节点数据。
  • set data:向节点中写入数据。
  • get children:检索某节点的子节点列表。
  • sync:等待要传播的数据。

ZooKeeper专题

ZooKeeper Components 展示了ZooKeeper服务的高级别的组件。除了请求处理器,组成ZooKeeper服务的每个服务器复制它本身每个组件的副本。

副本数据库是一个包含整个数据树的内存数据库。更新信息将被记录在磁盘中保证可恢复性,在它们被写到内存数据库之前序列化写到磁盘中。

每个ZooKeeper server都会为客户端提供服务。客户端连接到一个server来提交request。对于read request,每个server会从本地database的副本中提供服务。 对于write request,这些请求会更改服务状态,它们由协商协议(agreement procotol)来处理。

作为协商协议(agreement procotol)的一部分,所有来自客户端的write request都被转发到一个称为leader的server上。 ZooKeeper中的其它server,都称为follower,它们接收来自leader的消息,并传递那些达成一致的消息。 消息层(messaging layer)负责在leader故障时选举新的leader,并剩余的follower和新的leader保持同步。

ZooKeeper使用一个自定义的原子消息传递协议。由于该消息层是原子性的,ZooKeeper可以保证本地副本永远不会出现分歧。 当leader收到write request时,它会计算当写操作被执行时系统状态,并将其转换为带有该状态的事务

ZooKeepr的特性

  • ZooKeeper使在分布式系统上运行可协调任务成为可能。
  • 可协调任务指的是:一个包含多个处理过程的任务。多个处理过程之间需要相互协作(互斥或者并发执行等)。
  • ZooKeeper被应用在HBase\Solr\Kafka等系统上。
  • ZooKeeper提供:
    • 强一致性、顺序、持久性保证
    • 实现同步原语的能力
    • 通过简单的方式处理并发方面的问题,而这些问题经常导致分布式系统中的不一致行为。