`

zookeeper学习

 
阅读更多

简介

ZooKeeper是由Yahoo! Research研发的一个分布式的协调系统,最初是Hadoop下的一个子项目,后来独立出来成为Apache的顶级项目。不了解Zookeeper的人会认为它只是一个分布式锁服务,分布式锁仅仅只是ZooKeeper的其中一个应用场景而已。ZooKeeper的定位是一个coordination kernel,仅仅提供一些简单的API,分布式应用的开发者再根据自己的需要使用这些API来实现自己想要的同步原语(synchronization primitives),比如Lock, Barrier等等。

一、项目目标

一个系统对自己的定位是很重要的。ZooKeeper的目标是解决分布式系统中多个进程相互协作的问题,但是它并不想自己提供那些同步原语,因为这样就把应用开发者限制在了所提供的同步原语中,而且如果要提供多种同步原语的话,那整个系统就会很复杂。于是作者想让应用开发者自己写他们想要的同步原语,ZooKeeper仅仅提供实现这些原语的API就够了,就像是一个kernel一样,仅仅提供最原始的API。

二、系统架构

data_model

ZooKeeper的数据模型类似于Unix文件系统,就是一颗以"/"为根节点的文件系统树,每个节点称为一个znode,节点就是一个名值对,名字是这个znode的路径(path),值是这个节点中存储的一小部分数据。

architecture

ZooKeeper使用集群的方式来达到高可用性,整个集群称为一个ensemble。一个quorum(仲裁集)是使得ZooKeeper能够正常提供服务的,最少正常运转的机器数量,quorum中有且仅有一个Leader。
上面提到的DataTree在ensemble中的每台机器上都会有一份拷贝,所有的server都可以接收client的请求。对于读请求,server会直接使用自己本地的DataTree来serve;而对于写请求,server会把该请求转发给Leader,写请求都是由Ledaer来负责serve的,Leader修改完自己本地的DataTree之后会采用atomic broadcast的方式把修改同步其他的机器。

client的写请求做的修改必须同步到quorum中的所有机器之后,client才会收到server的响应。因此quorum不能太大也不能太小,如果机器太多,那么同步的时间就会变长,响应时间就长;如果太小,不及整个ensemble的半数以上,那么有会有数据不一致的情况发生。最佳实践是取超过整个ensemble的半数即可。即如果N(ensemble)=2f+1,那么N(quorum)最好是f+1,则该集群最多能容忍f台机器宕机。

三、API简介

1. 节点类型

ZooKeeper中的节点有两种类型:Persistent,Ephemeral。Persistent顾名思义就是持久的节点,一旦创建之后就会一直存在于ZooKeeper中,Ephemeral节点只在client与server的会话期间存在,也就说当client主动断开了与server的会话,或者是因为宕机导致会话超时,Ephemeral节点就会被删除。这两种节点都可以设置为是否是Sequential的,Sequential的znode在创建成功后ZooKeeper会在path后面追加一个单调递增的整数。

2. 同步API和异步API

  • create(path, data, flags)
  • delete(path, version)
  • exists(path, watch)
  • getData(path, watch)
  • setData(path, data, watch)
  • getChildren(path, watch)
  • sync(path)

ZooKeeper的API还是比较直观的,path就是znode的全路径;data就是要存放在znode中的数据;flags可以设置节点的类型;watch是一个布尔值,表示是否监听该znode上的事件。(在Java代码中还有一个ACL参数,对znode进行访问控制,本文忽略)
每一个API都会有对应的异步版本,异步API使得client能够同一时间发送多个请求。异步API多了2个参数:

  • cb: callback,当请求在server端处理完成后,本地的一个线程会回调这个函数,所有的callback类型都定义在AsyncCallback这个类里。
  • ctx: 用户自定义的对象,作为回调callback时的参数传入

3. 监视事件

ZooKeeper API的watch参数可以设置是否对某一个znode进行监视,如果设为true,那么监视名字为path的znode的状态。当该znode被创建、删除、值发生变化、孩子发生变化时,server端会向client推送一个事件(WatchedEvent)。这里有一个顺序约束: znode发生了变化即表示Zookeeper的状态发生了变化,Zookeeper会保证client先接收到事件,然后才能看到最新的状态。

四、实现简介

有序性约束

在论文中作者多次强调了 有序性 这一特点,这是ZooKeeper能够实现同步原语的基石。有序性体现在两个方面:

  1. client的请求会被FIFO的执行,即server返回响应的顺序与client发起请求的顺序是一致的。 这里的有序性是针对单个client而言的,是局部有序。
  2. 涉及到更新ZooKeeper状态的操作(写操作),会被串行化的commit, 这个是全局有序的,因为所有的写操作都由Leader来负责执行那个。

多备份的数据

components

client只能与一台server建立会话,如果client发起的是写请求,那么会被转发给Leader,Leader会把请求通过Request Processor转换成一个事务,并且这个事务是 幂等 的。Leader在本地commit完事务之后,会把事务所做的改动通过Zab协议同步给其他的server。Zab保证 修改 在广播给其他server时的顺序与它们发起的顺序是一致的,假设client A和client B分别先后发起了2个请求:

// Client A
create("/master", "", PERSISTENT)  // 对应事务 TXN1: (CreateTXN, /master)

// Client B
create("/worker", "", PERSISTENT)  // 对应事务 TXN2: (CreateTXN, /worker)

那么Leader在广播时的顺序就是TXN1在TXN2之前。

之前说过,client的读请求是由client所连接的那台server使用自己本地的DataTree来serve的,也就是说, 并不是所有的读请求都能读取到最新的状态。 要保证一定能够读到最新的状态,那么就在读请求之前先调用一次sync。server接收到sync之后,Leader会把sync之前尚未完成的事务全部传播给这台server,让这台server的状态追上Leader。

错误容忍

ZooKeeper在修改内存中的DataTree之前会先写一条日志(Write-ahead logging),然后再修改DataTree。Leader在Zab广播时,也会先记录要广播的proposals的日志,然后再进行广播。
在ZooKeeper的运行期间,会周期性的对内存中的DataTree做快照,在做快照时,并不会锁定整个Tree,即在做快照期间也允许对Tree的读写。所以dump到磁盘上的快照并不是某一时间点上的ZooKeeper的状态,有可能是一个无效的状态。在宕机重启之后,会去读取这个快照,并且还会 通过Zab重放快照开始生成到快照生成完成 这个时间段内的proposal日志。因为ZooKeeper的事务都是幂等的,所以某些事务被多次执行并不会有side effect,只要保证了事务执行的顺序就可以恢复到宕机前的有效状态。

五、如何在自己电脑上运行Zookeeper

  1. 先从gihub上获取代码

    git clone https://github.com/apache/zookeeper
    
  2. cd zookeeper; ant eclipse

  3. 导入到IDEA中

  4. 修改配置文件里的dataDir,指向任意一个你自己创建的目录

    cd conf
    cp zoo_sample.cfg zoo.cfg
    vim zoo_sample.cfg
    
  5. 新建一个Run Configuration
    server_run_config
    vm_options

ZooKeeper服务端的主类是:QuorumPeerMain,把上述配置文件的路径作为程序运行的参数。VM options里指定一下日志级别。
6. 因为Zookeeper的log4j配置文件是放在conf目录下的,所以要把conf目录加入到CLASSPATH里,否则运行后会找不到配置文件,然后就没有日志输出了。
Project Structure->Modules->Dependencies,点击下面的"+",选择第一个"JARs or directories",找到conf目录,然后会弹出一个对话框,选择Classes。
project_structure
7. 然后就可以启动ZooKeeper Server了。
8. Client的配置类似,主类是ZooKeeperMain
client_run_config

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics