zk 01之 ZooKeeper概述

一、Zookeeper产生的背景

ZooKeeper是–个开放源代码的分布式协调服务,由知名互联网公司雅虎创建,是Google Chubby的源实现。ZooKeeper的设计目标是将那些复杂且容易出错的分布式–致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。

   ①Zookeeper是Google的Chubby一个开源的实现,是Hadoop的分布式协调服务
   ②它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名维护等
   ③它的文件系统使用了我们所熟悉的目录树结构。
   ④Zookeeper的设计目的是为了减轻分布式应用程序所承担的协调任务

二、Zookeeper的设计目标

ZooKeeper致力于提供一个高性能、高可用,且具有严格的顺序访问控制能力(主要是写操作的严格顺序性)的分布式协调服务。

高性能使得ZooKeeper能够应用于那些对系统吞吐有明确要求的大型分布式系统中,高可用使得分布式的单点问题得到了很好的解决,而严格的顺序访问控制使得客户端能够基于ZooKeeper实现一些复杂的同步原语。下面我们来具体看一下ZooKeeper的四个设计目标。

目标一:简单的数据模型

Zookeeper使得分布式程序能够通过一个共享的、树形结构的名字空间来进行相互协调。

这里所说的树型结构的名字空间,是指ZooKeeper服务器内存中的一个数据模型,其由一系列被称为ZNode的数据节点组成,总的来说,其数据模型类似于一个文件系统,而ZNode之间的层级关系,就像文件系统的目录结构一样。

不过和传统的磁盘文件系统不同的是,ZooKeeper将全量数据存储在内存中,以此来实现提高服务器吞吐、减少延迟的目的

目标二:可以构建集群
一个Zookeeper集群通常由一组机器组成,一般3~5台机器就可以组成一个可用的Zookeeper集群:

组成ZooKeeper集群的每台机器都会在内存中维护当前的服务器状态,并且每台机器之间都互相保持着通信。值得一提的是,只要集群中存在超过一半的机器能够正常工作,那么整个集群就能够正常对外服务。

ZooKeeper的客户端程序会选择和集群中任意一台机器共同来创建-一个TCP连接,而一旦客户端和某台ZooKeeper服务器之间的连接断开后,客户端会自动连接到集群中的其他机器。

目标三:顺序访问
对于来自客户端的每个更新请求,ZooKeeper 都会分配一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序,应用程序可以使用ZooKeeper的这个特性来实现更高层次的同步原语。

目标四:高性能
由于Zookeeper将全量数据存储在内存中,并直接服务于客户端的所有非事务请求,因此它尤其适用于以读操作为主的应用场景。

三、Zookeeper功能

  Zookeeper是一个典型的分布式数据一致性的解决方案,分布式应用程序可以基于它实现诸如数据发布/订阅负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。

Zookeeper可以保证如下分布式一致性特点:

1、顺序一致性:

从同一个客户端发起的事务请求,最终将会严格地按照其发起顺序被应用到ZooKeeper中去。包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有Server上消息a都将在消息b前被发布;偏序是指如果一个消息b在消息a后被同一个发送者发布,a必将排在b前面。

2、原子性:

更新只能成功或者失败,没有中间状态。所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,也就是说,要么整个集群所有机器都成功应用了某一个事务,要么都没有应用,一定不会出现集群中部分机器应用了该事务,而另外一部分没有应用的情况。

3、最终一致性[单一视图(Single System Image)]:无论客户端连接的是哪个ZooKeeper服务器,其看到的服务端数据模型都是一致的。

4、可靠性:具有简单、健壮、良好的性能,如果消息被到一台服务器接受,那么它将被所有的服务器接受。

5、实时性:

通常人们看到实时性的第一反应是,一旦一个事务被成功应用,那么客户端能够立即从服务端上读取到这个事务变更后的最新数据状态。这里需要注意的是,ZooKeeper仅仅保证在一定的时间段内,客户端最终一定能够从服务端上读取到最新的数据状态。Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。但由于网络延时等原因,Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。

6、等待无关(wait-free):慢的或者失效的client不得干预快速的client的请求,使得每个client都能有效的等待。

四、为什么使用Zookeeper

    ①大部分分布式应用需要一个主控、协调器或者控制器来管理物理分布的子进程(如资源,任务分配等)
    ②目前,大部分应用需要开发私有的协调程序,缺乏一个通用的机制
    ③协调程序的反复编写浪费,且难以形成通用、伸缩性号的协调器。
    ④Zookeeper:提供通用的分布式锁服务,用以协调分布式应用
 

五、Zookeeper的基本概念

Zookeeper的集群通常Zookeeper由2n+1台servers组成,每个server都知道彼此的存在。每个server都维护的内存状态镜像以及持久化存储的事务日志和快照。为了保证Leader选举能过得到多数的支持,所以ZooKeeper集群的数量一般为奇数。对于2n+1台server,只要有n+1台(大多数)server可用,整个系统保持可用。

集群角色
通常在分布式系统中,构成一个集群的每一台机器 都有自己的角色,最典型的集群模式就是Master/Slave 模式(主备模式)。在这种模式中,我们把能够处理所有写操作的机器称为Master机器,把所有通过异步复制方式获取最新数据,并提供读服务的机器称为Slave机器。

而在ZooKeeper中,这些概念被颠覆了。它没有沿用传统的Master/Slave概念,而是引入了Leader、 Follower 和Observer 三种角色。

集群中的服务器角色有两种Leader和Learner,Learner角色又分为Observer和Follower,具体功能如下:

1.领导者(leader),负责进行投票的发起和决议,更新系统状态。(ZooKeeper 集群中的所有机器通过一个Leader选举过程来选定一台被称为“Leader”的机器,Leader服务器为客户端提供读和写服务)

2.学习者(learner),包括跟随者(follower)和观察者(observer),

3.follower用于接受客户端请求并向客户端返回结果,在选主过程中参与投票

4.Observer可以接受客户端请求,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度(Observer机器不参与Leader选举过程,也不参与写操作的“过半写成功”策略,因此Observer可以在不影响写性能的情况下提升集群的读性能)

5. 客户端(client),请求发起方

会话(Session):
Session是指客户端会话,在讲解会话之前,我们首先来了解一下客户端连接。在ZooKeeper中,一个客户端连接是指客户端和服务器之间的一个TCP长连接。ZooKeeper对外的服务端口默认是2181,客户端启动的时候,首先会与服务器建立一个TCP连接,从第一次连接建立开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够向ZooKeeper服务器发送请求并接受响应,同时还能够通过该连接接收来自服务器的Watch事件通知。Session的sessionTimeout值用来设置一个客户端会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在sessionTimeout规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效

数据结点(Znode)
在谈到分布式的时候,我们通常说的“节点”是指组成集群的每一台机器。然而,在ZooKeeper中,“节点”分为两类:

第一类同样是指构成集群的机器,我们称之为机器节点;
第二类则是指数据模型中的数据单元,我们称之为数据节点——ZNode。
ZooKeeper将所有数据存储在内存中,数据模型是一棵树(ZNode Tree), 由斜杠(/)进行分割的路径,就是一个Znode,例如/foo/pathI。每个ZNode上都会保存自己的数据内容,同时还会保存一系列属性信息。

在ZooKeeper中,ZNode可以分为持久节点和临时节点两类。

所谓持久节点是指一旦这个ZNode被创建了,除非主动进行ZNode的移除操作,否则这个ZNode将一直保存在ZooKeeper.上。
而临时节点就不一样了,它的生命周期和客户端会话绑定,一旦客户端会话失效,那么这个客户端创建的所有临时节点都会被移除。
另外,ZooKeeper 还允许用户为每个节点添加一个特殊的属性:SEQUENTIAL。一旦节点被标记上这个属性,那么在这个节点被创建的时候,ZooKeeper会自动在其节点名后面追加上一个整型数字,这个整型数字是一个由父节点维护的自增数字。

版本
在前面我们已经提到,ZooKeeper 的每个ZNode上都会存储数据,对应于每个ZNode,ZooKeeper都会为其维护一个叫作Stat的数据结构,Stat 中记录了这个ZNode的三个数据版本,分别是version (当前ZNode的版本)、cversion (当前ZNode子节点的版本)和aversion(当前ZNode的ACL版本)

Watcher:
Watcher (事件监听器),是ZooKeeper中的一个很重要的特性。ZooKeeper 允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,ZooKeeper服务端会将事件通知到感兴趣的客户端上去,该机制是ZooKeeper实现分布式协调服务的重要特性。

ACL
ZooKeeper采用ACL (Access Control Lists) 策略来进行权限控制,类似于UNIX文件系统的权限控制。ZooKeeper 定义了如下5种权限。

CREATE: 创建子节点的权限
READ: 获取结点数据和子节点列表的权限
WRITE: 更新结点数据的权限
DELETE: 删除子节点的权限
ADMIN: 设置结点ACL的权限
其中尤其需要注意的是,CREATE和DELETE这两种权限都是针对子节点的权限控制。

 

2 数据模型和分层的名字空间

ZooKeeper提供的名字空间与标准文件系统非常相似。名字是一个由斜杠/分隔的路径元素序列。ZooKeeper名字空间中的每个节点都由其路径标识。

Zookeeper中的节点

    与标准文件系统不同,ZooKeeper名字空间中的每个节点都可以有关联的数据以及子节点。这就像一个允许文件也是目录的文件系统。(ZooKeeper设计用于存储协调数据:状态信息、配置、位置信息等,所以通常存储在每个节点中的数据很小,在字节到千字节范围内)讨论ZooKeeper数据节点时,我们用术语znode来明确指示。

Znode会维护一个stat结构体,其中包含数据和ACL的版本号与时间戳,以便于进行缓存验证和协调更新。每次修改znode数据时,版本号会增长。客户端获取数据的时候,也同时获取数据的版本。

对znode数据的读写操作是原子的。读取操作获取节点的所有数据,写入操作替换所有数据。节点的访问控制列表(ACL)控制可以进行操作的用户。

ZooKeeper具有临时节点的概念。只要创建节点的会话是活动的,临时节点就存在。一旦会话终止,临时节点会被删除。临时节点对于实现tbd是很有用的。

Zookeeper中的每一个节点还拥有一些自身的信息,包括数据,数据长度,创建时间,修改时间等。
Znode概念的引用 用来表示所讨论的Zookeeper节点。  具体来说Znode维护着数据、访问控制列表(ACL),时间戳等包含交换版本号信息的数据结构。
 
Zookeeper的数据模型
        在Zookeeper中不准使用相对路径

 一、Znode    Znode是客户端访问Zookeeper的主要实体,每当Znode的数据改变时,他相应的版本号将会增加。        

  • stat: 此为状态信息, 描述该znode的版本, 权限等信息.
  • data: 与该znode关联的数据.
  • children:该znode下的子节点
   它包含以下特征
         (1)Watches
                客户端可以在节点上设置watch(监视器),当节点的状态发生改变时,将会触发watch相应的操作,只会被触发一次
         (2)数据访问
                Zookeeper中的每个节点上存储的数据需要被原子性操作也就是说读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。另外,每一个节点都拥有自己的ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的操作
         (3)临时节点    
                Zookeeper中的节点有两种,分别为临时节点和永久节点。节点的类型在创建是已经被指定,不能被修改。
                ZooKeeper的临时节点:该节点的生命周期依赖于创建它们的会话。一旦会话结束,临时节点将被自动删除,当然可以也可以手动删除。另外,需要注意是,ZooKeeper的临时节点不允许拥有子节点。(zk 07之:zk客户端Curator 示例2和示例3)
     (4)ZooKeeper的永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。
         (5)顺序节点(唯一性保证)
                当创建Znode的时候,用户可以请求在Zookeeper的路径结尾添加一个递增的计数
                这个计数对于此节点的父节点来说是唯一的,它的格式为“%10d”(10位数字,没有数值的数位用0补充,例如“0000000001”)。当计数值大        于232-1时,计数器将溢出。

org.apache.zookeeper.CreateMode中定义了四种节点类型,分别对应:

  1.         PERSISTENT:永久节点
  2.         EPHEMERAL:临时节点
  3.         PERSISTENT_SEQUENTIAL:永久节点、序列化
  4.         EPHEMERAL_SEQUENTIAL:临时节点、序列化

 ZooKeeper 节点是有生命周期的,这取决于节点的类型。在 ZooKeeper 中,节点类型可以分为持久节点(PERSISTENT )、临时节点(EPHEMERAL),以及时序节点(SEQUENTIAL ),具体在节点创建过程中,一般是组合使用,可以生成以下 4 种节点类型。

持久节点(PERSISTENT)
  所谓持久节点,是指在节点创建后,就一直存在,直到有删除操作来主动清除这个节点——不会因为创建该节点的客户端会话失效而消失。
持久顺序节点(PERSISTENT_SEQUENTIAL)
  这类节点的基本特性和上面的节点类型是一致的。额外的特性是,在ZK中,每个父节点会为他的第一级子节点维护一份时序,会记录每个子节点创建的先后顺序。基于这个特性,在创建子节点的时候,可以设置这个属性,那么在创建节点过程中,ZK会自动为给定节点名加上一个数字后缀,作为新的节点名。这个数字后缀的范围是整型的最大值。
临时节点(EPHEMERAL)
  和持久节点不同的是,临时节点的生命周期和客户端会话绑定。也就是说,如果客户端会话失效,那么这个节点就会自动被清除掉。注意,这里提到的是会话失效,而非连接断开。另外,在临时节点下面不能创建子节点。
临时顺序节点(EPHEMERAL_SEQUENTIAL)

分布式系统解决方案之二:(分布式锁一):zookeeper分布式锁1

 
看zookeeper的源码如下:
public enum CreateMode {  
     
    /** 
     * 持久节点:节点创建后,会一直存在,不会因客户端会话失效而删除; 
     */  
    PERSISTENT (0, false, false),  
  
    /** 
    * 持久顺序节点:基本特性与持久节点一致,创建节点的过程中,zookeeper会在其名字后自动追加一个单调增长的数字后缀,作为新的节点名;  
    */  
    PERSISTENT_SEQUENTIAL (2, false, true),  
  
    /** 
     *  临时节点:客户端会话失效或连接关闭后,该节点会被自动删除,且不能再临时节点下面创建子节点,否则报如下错:org.apache.zookeeper.KeeperException$NoChildrenForEphemeralsException; 
     */  
    EPHEMERAL (1, true, false),  
  
    /** 
     * 临时顺序节点:基本特性与临时节点一致,创建节点的过程中,zookeeper会在其名字后自动追加一个单调增长的数字后缀,作为新的节点名;  
     */  
    EPHEMERAL_SEQUENTIAL (3, true, true);  
    private static final Logger LOG = LoggerFactory.getLogger(CreateMode.class);  
    private boolean ephemeral;  
    private boolean sequential;  
    private int flag;  
    CreateMode(int flag, boolean ephemeral, boolean sequential) {  
        this.flag = flag;  
        this.ephemeral = ephemeral;  
        this.sequential = sequential;  
    }  
    public boolean isEphemeral() {  
        return ephemeral;  
    }  
    public boolean isSequential() {  
        return sequential;  
    }  
    public int toFlag() {  
        return flag;  
    }  
    static public CreateMode fromFlag(int flag) throws KeeperException {  
        switch(flag) {  
        case 0: return CreateMode.PERSISTENT;  
        case 1: return CreateMode.EPHEMERAL;  
        case 2: return CreateMode.PERSISTENT_SEQUENTIAL;  
        case 3: return CreateMode.EPHEMERAL_SEQUENTIAL ;  
        default:  
            LOG.error("Received an invalid flag value to convert to a CreateMode");  
            throw new KeeperException.BadArgumentsException();  
        }  
    }  
}  

二、Zxid

            致使Zookeeper节点状态改变的每一次操作都将使节点接收到一个zxid格式的时间戳,并且全局有效,每个Zookeeper都维护者三个zxid值,分别为cZxid,mZxid,pZxid,如下图中所示:

三、版本号

  对节点的每一次操作都将致使整个节点的版本号增加。每个节点维护着三个版本号,他们分别是:dataVersion(节点数据版本号),cversion(子节点版本号),aclVevsion(节点所拥有的ACL版本号)。如下图所示:

四,节点属性结构

      

4 条件更新和观察

ZooKeeper支持观察的概念。客户端可以在znode上设置观察。观察将在znode修改时被触发和移除。观察被触发时客户端会收到一个数据包,指示znode已经被修改。如果与ZooKeeper服务之间的连接断开,客户端会收到一个本地通知。这可用于tbd。

Zookeeper watchs    

读操作exists、getChildren和getData都被设置了watch,并且这些watch都由写操作来触发:create、delete和setData。ACL操作并不参与到watch中。当watch被触发时,watch事件被生成,他的类型由watch和触发他的操作共同决定。ZooKeeper所管理的watch可以分为两类:

1.数据watch(data watches):getData和exists负责设置数据watch;

2.孩子watch(child watches):getChildren负责设置孩子watch;

我们可以通过操作返回的数据来设置不同的watch:

1.getData和exists:返回关于节点的数据信息

2.getChildren:返回孩子列表

因此,一个成功的setData操作将触发Znode的数据watch。

一个成功的create操作将触发Znode的数据watch以及孩子watch。

一个成功的delete操作将触发Znode的数据watch以及孩子watch。

watch由客户端所连接的ZooKeeper服务器在本地维护,因此watch可以非常容易地设置、管理和分派。当客户端连接到一个新的服务器上时,任何的会话事件都将可能触发watch。另外,当从服务器断开连接的时候,watch将不会被接收。但是,当一个客户端重新建立连接的时候,任何先前注册过的watch都会被重新注册。

exists操作上的watch,在被监视的Znode创建、删除或数据更新时被触发。

getData操作上的watch,在被监视的Znode删除或数据更新时被触发。在被创建时不能被触发,因为只有Znode一定存在,getData操作才会成功。

getChildren操作上的watch,在被监视的Znode的子节点创建或删除,或是这个Znode自身被删除时被触发。可以通过查看watch事件类型来区分是Znode还是他的子节点被删除:NodeDelete表示Znode被删除,NodeDeletedChanged表示子节点被删除。

watch设置操作及相应的触发器如图下图所示:

watch事件包括了事件所涉及的Znode的路径,因此对于NodeCreated和NodeDeleted事件来说,根据路径就可以简单区分出是哪个Znode被创建或是被删除了。为了查询在NodeChildrenChanged事件后哪个子节点被改变了,需要再次调用getChildren来获得新的children列表。同样的,为了查询NodeDeletedChanged事件后产生的新数据,需要调用getData。在两种情况下,Znode可能在获取watch事件或执行读操作这两种状态下切换,在写应用程序时,必须记住这一点。

(1)Zookeeper的watch实际上要处理两类事件:

1. 连接状态事件(type=None, path=null)

这类事件不需要注册,也不需要我们连续触发,我们只要处理就行了。

2. 节点事件

节点的建立,删除,数据的修改。它是one time trigger,我们需要不停的注册触发,还可能发生事件丢失的情况。

上面2类事件都在Watch中处理,也就是重载的process(Event event)

(2)节点事件的触发,通过函数exists,getData或getChildren来处理

这类函数,有双重作用:

1. 注册触发事件

2. 函数本身的功能

函数的本身的功能又可以用异步的回调函数来实现,重载processResult()过程中处理函数本身的的功能。

函数还可以指定自己的watch,所以每个函数都有4个版本。根据自己的需要来选择不同的函数,不同的版本。

3.3 ZooKeeper访问控制列表ACL

ZooKeeper使用ACL来对Znode进行访问控制。ACL的实现和Unix文件访问许可非常相似:它使用许可位来对一个节点的不同操作进行允许或禁止的权限控制。但是,和标准的Unix许可不同的是,Zookeeper对于用户类别的区分,不止局限于所有者(owner)、组 (group)、所有人(world)三个级别。Zookeeper中,数据节点没有“所有者”的概念。访问者利用id标识自己的身份,并获得与之相应的 不同的访问权限。

注意:

传统的文件系统中,ACL分为两个维度,一个是属组,一个是权限,子目录/文件默认继承父目录的ACL。而在Zookeeper中一个ACL和一个ZooKeeper节点相对应。并且,父节点的ACL与子节点的ACL是相互独立的。也就是说,ACL不能被子节点所继承,父节点所拥有的权限与子节点所用的权限都没有任何关系。

Zookeeper支持可配置的认证机制。它利用一个三元组来定义客户端的访问权限:(scheme:expression, perms) 。其中:

1.scheme:定义了expression的含义。

如:(host:host1.corp.com,READ),标识了一个名为host1.corp.com的主机,有该数据节点的读权限。

2.Perms:标识了操作权限。

如:(ip:19.22.0.0/16, READ),表示IP地址以19.22开头的主机,有该数据节点的读权限。

Zookeeper的ACL也可以从三个维度来理解:一是,scheme; 二是,user; 三是,permission,通常表示为scheme:id:permissions,如下图所示。

1.world : id格式:anyone。

如:world:anyone代表任何人,zookeeper中对所有人有权限的结点就是属于world:anyone的。

2.auth : 它不需要id。

注:只要是通过authentication的user都有权限,zookeeper支持通过kerberos来进行认证, 也支持username/password形式的认证。

3.digest: id格式:username:BASE64(SHA1(password))。

它需要先通过username:password形式的authentication。

4.ip: id格式:客户机的IP地址。

设置的时候可以设置一个ip段。如:ip:192.168.1.0/16, 表示匹配前16个bit的IP段

5.super: 超级用户模式。

在这种scheme情况下,对应的id拥有超级权限,可以做任何事情

ZooKeeper权限定义如下图所示:

ZooKeeper内置的ACL模式如下图所示:

当会话建立的时候,客户端将会进行自我验证。另外,ZooKeeper Java API支持三种标准的用户权限,它们分别为:

1.ZOO_PEN_ACL_UNSAFE:对于所有的ACL来说都是完全开放的,任何应用程序可以在节点上执行任何操作,比如创建、列出并删除子节点。

2.ZOO_READ_ACL_UNSAFE:对于任意的应用程序来说,仅仅具有读权限。

3.ZOO_CREATOR_ALL_ACL:授予节点创建者所有权限。需要注意的是,设置此权限之前,创建者必须已经通了服务器的认证。

5 保证

ZooKeeper非常高效和简单。基于其目标:成为构建如同步这样的更复杂服务的基础,ZooKeeper提供下述保证:

在ensemble中的领导者和跟随着非常灵活,跟随者通过更新号来滞后领导者11,结果导致了只要大部分而不是所有的ensemble中的元素确认更新,就能被提交了。对于ZooKeeper来说,一个较好的智能模式是将客户端连接到跟着领导者的ZooKeeper服务器上。客户端可能被连接到领导者上,但他不能控制它,而且在如下情况时,甚至可能不知道。参见下图:

每一个Znode树的更新都会给定一个唯一的全局标识,叫zxid(表示ZooKeeper事务“ID”)。更新是被排序的,因此如果zxid的z1<z2,那么z1就比z2先执行。对于ZooKeeper来说,这是分布式系统中排序的唯一标准。

ZooKeeper是一种高性能、可扩展的服务。ZooKeeper的读写速度非常快,并且读的速度要比写快。另外,在进行读操作的时候,ZooKeeper依然能够为旧的数据提供服务。这些都是由ZooKeeper所提供的一致性保证的,它具有如下特点:

(1)顺序一致性

任何一个客户端的更新都按他们发送的顺序排序,也就意味着如果一个客户端将Znode z的值更新为值a,那么在之后的操作中,他会将z更新为b,在客户端发现z带有值b之后,就不会再看见带有值a的z。

(2)原子性

更新不成功就失败,这意味着如果更新失败了,没有客户端会知道。☆☆

(3)单系统映像

无论客户端连接的是哪台服务器,他与系统看见的视图一样。这就意味着,如果一个客户端在相同的会话时连接了一台新的服务器,他将不会再看见比在之前服务器上看见的更老的系统状态,当服务器系统出故障,同时客户端尝试连接ensemble中的其他机器时,故障服务器的后面那台机器将不会接受连接,直到它连接到故障服务器。

(4)容错性

一旦更新成功后,那么在客户端再次更新他之前,他就固定了,将不再被修改,这就会保证产生下面两种结果:

如果客户端成功的获得了正确的返回代码,那么说明更新已经成功。如果不能够获得返回代码(由于通信错误、超时等原因),那么客户端将不知道更新是否生效。

当故障恢复的时候,任何客户端能够看到的执行成功的更新操作将不会回滚。

(5)实时性

在任何客户端的系统视图上的的时间间隔是有限的,因此他在超过几十秒的时间内部会过期。这就意味着,服务器不会让客户端看一些过时的数据,而是关闭,强制客户端转到一个更新的服务器上。

解释一下:

由于性能原因,读操作由ZooKeeper服务器的内存提供,而且不参与写操作的全局排序。这一特性可能会导致来自使用ZooKeeper外部机制交流的客户端与ZooKeeper状态的不一致。举例来说,客户端A将Znode z的值a更新为a',A让B来读z,B读到z的值是a而不是a’。这与ZooKeeper的保证机制是相容的(不允许的情况较作“同步一致的交叉客户端视 图”)。为了避免这种情况的发生,B在读取z的值之前,应该先调用z上的sync。Sync操作强制B连接上的ZooKeeper服务器与leader保 持一致这样,当B读到z的值时,他将成为A设置的值(或是之后的值)

容易混淆的是:

sync操作只能被异步调用12。这样操作的原因是你不需要等待他的返回,因为ZooKeeper保证了任何接下去的操作将会发生在sync在服务器上执行以后,即使操作是在sync完成前被调用的。

这些已执行的保证后,ZooKeeper更高级功能的设计与实现将会变得非常容易,例如:leader选举、队列,以及可撤销锁等机制的实现。

6 简单的API

ZooKeeper的设计目标之一是提供非常简单的编程接口。ZooKeeper仅支持这些操作:

create:在树中某位置创建一个节点。

delete:删除一个节点。

exists:测试某位置是否存在某节点。

get data:读取节点数据。

set data:向节点写入数据。

get children:获取子节点列表。

sync:等待数据传播。

关于这些操作的更深入讨论,以及如何使用它们来实现更高层的操作,请参看tbd。

zk 07之:zk客户端Curator 示例一

 

7 实现

下图显示了ZooKeeper服务的高层组件。除了请求处理器(Request Processor)之外,组成ZooKeeper服务的每个服务器拥有每个组件的自有拷贝。

自我复制数据库(replicated database)是一个包含整个数据树的内存数据库。更新会记录到磁盘中以便可以恢复,并且将写操作应用到内存数据库之前会先写入到磁盘。

每个ZooKeeper服务器都为客户服务。客户端连接到一个服务器,提交请求。读请求由每个服务器数据库的本地拷贝进行服务。改变服务状态的请求和写请求由一致性协议处理。

作为一致性协议的一部分,客户端的所有写请求都被转发到单个服务器,也就是领导者。其他ZooKeeper服务器则是跟随者,它们接收来自领导者的建议,对传递的消息达成一致。消息层考虑了替换失败的领导者和跟随者与领导者同步的问题。

ZooKeeper使用定制的原子消息协议。因为消息层是原子的,ZooKeeper可保证本地拷贝不会发散(diverge)。收到写请求时,领导者计算写入操作后系统的状态,将其转换成一个捕获此状态的事务。

8 使用

ZooKeeper的编程接口非常简单。但是,可将其用于实现高层顺序操作,如同步原语、组成员管理、所有者关系管理等。更多信息请参看tbd。

9 性能

ZooKeeper被设计为高性能的。但它真的是高性能的吗?Yahoo研究中心的ZooKeeper开发团队证实了ZooKeeper的高性能,特别是在读操作比写操作多的应用中(见下图),因为写操作涉及在所有服务器间同步状态。(读操作写操作是协调服务的典型情况)

上图是ZooKeeper 3.2在配置有两个2GHz Xeon处理器和两个SATA 15K RPM驱动器的服务器上运行时的吞吐率图形。一个驱动器配置为ZooKeeper日志专用设备。快照写入到操作系统驱动器。读写操作1KB的数据。“服务器数”指是ZooKeeper集群的大小,即组成服务的服务器个数。大约30个其他服务器用于模拟客户端。ZooKeeper集群配置为不允许客户端连接到领导者。

提示:3.2版的读写性能是3.1版的2倍。

Benchmarks也表明ZooKeeper是可靠的。(第10节的图)显示了ZooKeeper在各种失败情况下的反应。图中标记的各个事件是:

1.跟随者失败和恢复

2.另一个跟随者失败和恢复

3.领导者失败

4.两个跟随者失败和恢复

5.另一个领导者失败

10 可靠性

为揭示在有失败注入时系统的行为,我们一个由7台机器组成的ZooKeeper服务运行和先前一样的benchmark测试,但是让写操作的百分比固定为30%,这是预期负载比例的保守估计

此图有几处值得仔细观察。首先,如果跟随者失败后快速恢复,则ZooKeeper可以维持高吞吐率。但更重要的是,领导者选举算法让系统可以足够快地恢复,以阻止吞吐率有实质性的下降。据我们观察,ZooKeeper选举一个新的领导者的时间小于200ms。第三,一旦跟随者恢复并且开始处理请求,ZooKeeper可以恢复高吞吐率。

11 ZooKeeper工程

ZooKeeper已经在很多工业应用中成功使用。Yahoo!在Yahoo! Message Broker中使用ZooKeeper作为协调和故障恢复服务。Yahoo! Message Broker是一个高度扩展的发布-订阅系统,管理着成千上万个需要拷贝和数据传递的话题。Yahoo!的很多广告系统也使用ZooKeeper来实现可靠服务。

我们鼓励用户和开发者加入社区,贡献技能。更多信息请看Apache的ZooKeeper工程

posted on 2014-06-13 17:59  duanxz  阅读(909)  评论(0编辑  收藏  举报