基本是 http://zookeeper.apache.org/doc/trunk/zookeeperOver.html 的翻译,应用场景摘抄:http://www.wuzesheng.com/?p=2609

1)简介
zookeeper是个分布式开源协作服务。暴露一系列原语给分布式系统,用于构建更高层次的同步,配置维持,分组和命名服务。
解决协作服务常出现的竞争状况和死锁问题。
zk的数据是存储在内存里,高吞吐、低延时

2)quick start:
3.4.6为例
1、下载,解压
2、配置:
conf/zoo.cfg
tickTime=2000
dataDir=/home/shenguanpu/zookeeper-3.4.6/data
clientPort=2181
initLimit=5
syncLimit=2
server.1=zoo1:2888:3888
server.2=zoo2:2888:3888
server.3=zoo3:2888:3888

2888 3888 用于节点间对话,连接follower和leader。3888用于leader的选举。
三台机器 data/myid  为1 2 3 
3、启动:bin/zkServer.sh start

跟动物园管理员问好:

echo ruok | nc 127.0.0.1 5111

他要是心情好,会回复你 imok

观察集群状态
echo mntr | nc localhost 2181
leader会显示当前follower个数,状态,客户端的连接,数据,以及其他性能指标

更多命令:http://zookeeper.apache.org/doc/trunk/zookeeperAdmin.html#sc_zkCommands

也可安装admin web看系统状态
4、bin/zkCli.sh  进入并创建节点

create /www 123
ls /
get /www
create /www/a 1
delete /www
注意有子节点时无法直接删除,提示非空

3)原理分析
架构图:

server可感知到彼此,在内存中维护一个状态镜像,并且有事务日志和持久性存储镜像。只要主server在,zk即可用。

znode树形结构,类似文件目录,但目录也可以存数据;小数据存储,单kv是KB规模;节点更新同时更新版本;ACL限定谁可以更新;
客户端可设置观察者到znode,当znode变化时,watch会触发并移除。当watch被触发时,客户端会收到数据包说明znode的变更。服务端和客户端连接断掉时,客户端会有本地通知。

Znode特征:

1)watches 客户端在节点上设置watch,当节点状态改变时,会触发watch对应的操作,当watch被触发时,zk向客户端发送且仅发送一次通知,watch只能被触发一次,继续监控则需需要重新设置watch。

2)临时节点:不能有子节点,依赖会话

3)数据访问: 读写原子,每个节点有自己的acl

4)顺序节点: 路径结尾添加唯一递增计数,节点最大值2的32次方-1 ,更大时会溢出

zk的一致性保证策略:
序列一致性:客户端发起的更新会按发送时的时序进行
原子操作:不会有部分结果。在ZooKeeper集群中,读可以从任意一个ZooKeeper Server读,这一点是保证ZooKeeper比较好的读性能的关键;写的请求会先Forwarder到Leader,然后由Leader来通过ZooKeeper中的原子广播协议,将请求广播给所有的Follower,Leader收到一半以上的写成功的Ack后,就认为该写成功了,就会将该写进行持久化,并告诉客户端写成功了。

单系统镜像:无论客户端连哪个server,都能看到同样的数据
及时更新:客户端看到的视图按指定周期更新
 
zookeeper的服务组件:

复制库时个包含整个数据树的内存数据库,更新操作记录到磁盘以便数据恢复,写操作在应用到内存时会序列化到磁盘。
客户端只连接到一个服务端上,读请求时服务端从本地复制库里返回结果。改变服务状态的请求和写操作,通过同意协议来处理。
同意协议里规定,写操作会转发到单一的leader服务器上,其他的fllower服务器接受leader的消息提案,通过消息传递表示同意。消息层管理在失败状况下leader的替换,同步leader和follower的状态。
zk使用定制化的原子消息协议。消息层时原子的,zk就能保证本地副本不会分岔。当leader收到写请求,它计算系统状态,并转移这个状态到一个事务中,以处理新的状态。

4)API

  • create(path, data, flags): 创建一个ZNode, path是其路径,data是要存储在该ZNode上的数据,flags常用的有: PERSISTEN, PERSISTENT_SEQUENTAIL, EPHEMERAL, EPHEMERAL_SEQUENTAIL
  • delete(path, version): 删除一个ZNode,可以通过version删除指定的版本, 如果version是-1的话,表示删除所有的版本
  • exists(path, watch): 判断指定ZNode是否存在,并设置是否Watch这个ZNode。这里如果要设置Watcher的话,Watcher是在创建ZooKeeper实例时指定的,如果要设置特定的Watcher的话,可以调用另一个重载版本的exists(path, watcher)。以下几个带watch参数的API也都类似
  • getData(path, watch): 读取指定ZNode上的数据,并设置是否watch这个ZNode
  • setData(path, watch): 更新指定ZNode的数据,并设置是否Watch这个ZNode
  • getChildren(path, watch): 获取指定ZNode的所有子ZNode的名字,并设置是否Watch这个ZNode
  • sync(path): 把所有在sync之前的更新操作都进行同步,达到每个请求都在半数以上的ZooKeeper Server上生效。path参数目前没有用
  • setAcl(path, acl): 设置指定ZNode的Acl信息
  • getAcl(path): 获取指定ZNode的Acl信息

5)使用
编程接口很友好。你也可以实现更高级的操作,如同步原语,群组成员,所有权管理等。

6)性能
读操作占比高则性能好,占比100%的话能到14w qps;占比0时也能到1w qps。server较少时读性能会差。
leader出错,重新选举时会很影响读写性能,但也在2w qps以上。

7) acl 访问控制列表

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

8)wal和snapshot

WAL和Snapshot
和大多数分布式系统一样,ZooKeeper也有WAL(Write-Ahead-Log),对于每一个更新操作,ZooKeeper都会先写WAL, 然后再对内存中的数据做更新,然后向Client通知更新结果。另外,ZooKeeper还会定期将内存中的目录树进行Snapshot,落地到磁盘上,这个跟HDFS中的FSImage是比较类似的。这么做的主要目的,一当然是数据的持久化,二是加快重启之后的恢复速度,如果全部通过Replay WAL的形式恢复的话,会比较慢。

9)zookeeper典型应用场景

1. 名字服务(NameService)
分布式应用中,通常需要一套完备的命令机制,既能产生唯一的标识,又方便人识别和记忆。 我们知道,每个ZNode都可以由其路径唯一标识,路径本身也比较简洁直观,另外ZNode上还可以存储少量数据,这些都是实现统一的NameService的基础。下面以在HDFS中实现NameService为例,来说明实现NameService的基本布骤:

  • 目标:通过简单的名字来访问指定的HDFS机群
  • 定义命名规则:这里要做到简洁易记忆。下面是一种可选的方案: [serviceScheme://][zkCluster]-[clusterName],比如hdfs://lgprc-example/表示基于lgprc ZooKeeper集群的用来做example的HDFS集群
  • 配置DNS映射: 将zkCluster的标识lgprc通过DNS解析到对应的ZooKeeper集群的地址
  • 创建ZNode: 在对应的ZooKeeper上创建/NameService/hdfs/lgprc-example结点,将HDFS的配置文件存储于该结点下
  • 用户程序要访问hdfs://lgprc-example/的HDFS集群,首先通过DNS找到lgprc的ZooKeeper机群的地址,然后在ZooKeeper的/NameService/hdfs/lgprc-example结点中读取到HDFS的配置,进而根据得到的配置,得到HDFS的实际访问入口

2. 配置管理(Configuration Management)
在分布式系统中,常会遇到这样的场景: 某个Job的很多个实例在运行,它们在运行时大多数配置项是相同的,如果想要统一改某个配置,一个个实例去改,是比较低效,也是比较容易出错的方式。通过ZooKeeper可以很好的解决这样的问题,下面的基本的步骤:

  • 将公共的配置内容放到ZooKeeper中某个ZNode上,比如/service/common-conf
  • 所有的实例在启动时都会传入ZooKeeper集群的入口地址,并且在运行过程中Watch /service/common-conf这个ZNode
  • 如果集群管理员修改了了common-conf,所有的实例都会被通知到,根据收到的通知更新自己的配置,并继续Watch /service/common-conf

3. 组员管理(Group Membership)
在典型的Master-Slave结构的分布式系统中,Master需要作为“总管”来管理所有的Slave, 当有Slave加入,或者有Slave宕机,Master都需要感知到这个事情,然后作出对应的调整,以便不影响整个集群对外提供服务。以HBase为例,HMaster管理了所有的RegionServer,当有新的RegionServer加入的时候,HMaster需要分配一些Region到该RegionServer上去,让其提供服务;当有RegionServer宕机时,HMaster需要将该RegionServer之前服务的Region都重新分配到当前正在提供服务的其它RegionServer上,以便不影响客户端的正常访问。下面是这种场景下使用ZooKeeper的基本步骤:

  • Master在ZooKeeper上创建/service/slaves结点,并设置对该结点的Watcher
  • 每个Slave在启动成功后,创建唯一标识自己的临时性(Ephemeral)结点/service/slaves/${slave_id},并将自己地址(ip/port)等相关信息写入该结点
  • Master收到有新子结点加入的通知后,做相应的处理
  • 如果有Slave宕机,由于它所对应的结点是临时性结点,在它的Session超时后,ZooKeeper会自动删除该结点
  • Master收到有子结点消失的通知,做相应的处理

4. 简单互斥锁(Simple Lock)
我们知识,在传统的应用程序中,线程、进程的同步,都可以通过操作系统提供的机制来完成。但是在分布式系统中,多个进程之间的同步,操作系统层面就无能为力了。这时候就需要像ZooKeeper这样的分布式的协调(Coordination)服务来协助完成同步,下面是用ZooKeeper实现简单的互斥锁的步骤,这个可以和线程间同步的mutex做类比来理解:

  • 多个进程尝试去在指定的目录下去创建一个临时性(Ephemeral)结点 /locks/my_lock
  • ZooKeeper能保证,只会有一个进程成功创建该结点,创建结点成功的进程就是抢到锁的进程,假设该进程为A
  • 其它进程都对/locks/my_lock进行Watch
  • 当A进程不再需要锁,可以显式删除/locks/my_lock释放锁;或者是A进程宕机后Session超时,ZooKeeper系统自动删除/locks/my_lock结点释放锁。此时,其它进程就会收到ZooKeeper的通知,并尝试去创建/locks/my_lock抢锁,如此循环反复

5. 互斥锁(Simple Lock without Herd Effect)
上一节的例子中有一个问题,每次抢锁都会有大量的进程去竞争,会造成羊群效应(Herd Effect),为了解决这个问题,我们可以通过下面的步骤来改进上述过程:

  • 每个进程都在ZooKeeper上创建一个临时的顺序结点(Ephemeral Sequential) /locks/lock_${seq}
  • ${seq}最小的为当前的持锁者(${seq}是ZooKeeper生成的Sequenctial Number)
  • 其它进程都对只watch比它次小的进程对应的结点,比如2 watch 1, 3 watch 2, 以此类推
  • 当前持锁者释放锁后,比它次大的进程就会收到ZooKeeper的通知,它成为新的持锁者,如此循环反复

这里需要补充一点,通常在分布式系统中用ZooKeeper来做Leader Election(选主)就是通过上面的机制来实现的,这里的持锁者就是当前的“主”。

6. 读写锁(Read/Write Lock)
我们知道,读写锁跟互斥锁相比不同的地方是,它分成了读和写两种模式,多个读可以并发执行,但写和读、写都互斥,不能同时执行行。利用ZooKeeper,在上面的基础上,稍做修改也可以实现传统的读写锁的语义,下面是基本的步骤:

  • 每个进程都在ZooKeeper上创建一个临时的顺序结点(Ephemeral Sequential) /locks/lock_${seq}
  • ${seq}最小的一个或多个结点为当前的持锁者,多个是因为多个读可以并发
  • 需要写锁的进程,Watch比它次小的进程对应的结点
  • 需要读锁的进程,Watch比它小的最后一个写进程对应的结点
  • 当前结点释放锁后,所有Watch该结点的进程都会被通知到,他们成为新的持锁者,如此循环反复

7. 屏障(Barrier)
在分布式系统中,屏障是这样一种语义: 客户端需要等待多个进程完成各自的任务,然后才能继续往前进行下一步。下用是用ZooKeeper来实现屏障的基本步骤:

  • Client在ZooKeeper上创建屏障结点/barrier/my_barrier,并启动执行各个任务的进程
  • Client通过exist()来Watch /barrier/my_barrier结点
  • 每个任务进程在完成任务后,去检查是否达到指定的条件,如果没达到就啥也不做,如果达到了就把/barrier/my_barrier结点删除
  • Client收到/barrier/my_barrier被删除的通知,屏障消失,继续下一步任务

8. 双屏障(Double Barrier)
双屏障是这样一种语义: 它可以用来同步一个任务的开始和结束,当有足够多的进程进入屏障后,才开始执行任务;当所有的进程都执行完各自的任务后,屏障才撤销。下面是用ZooKeeper来实现双屏障的基本步骤:

  • 进入屏障:
  • Client Watch /barrier/ready结点, 通过判断该结点是否存在来决定是否启动任务
  • 每个任务进程进入屏障时创建一个临时结点/barrier/process/${process_id},然后检查进入屏障的结点数是否达到指定的值,如果达到了指定的值,就创建一个/barrier/ready结点,否则继续等待
  • Client收到/barrier/ready创建的通知,就启动任务执行过程
    • 离开屏障:
    • Client Watch /barrier/process,如果其没有子结点,就可以认为任务执行结束,可以离开屏障
    • 每个任务进程执行任务结束后,都需要删除自己对应的结点/barrier/process/${process_id}

zookeeper学习系列:一、入门的更多相关文章

  1. zookeeper学习系列:三、利用zookeeper做选举和锁

    之前只理解zk可以做命名,配置服务,现在学习下他怎么用作选举和锁,进一步还可构建master-slave模式的分布式系统. 为什么叫Zoo?“因为要协调的分布式系统是一个动物园”. ZooKeeper ...

  2. MongoDB学习系列(1)--入门介绍

    MongoDB是一款为Web应用程序设计的面向文档结构的数据库系统. MongoDB贡献者是10gen公司.地址:http://www.10gen.com 1.MongoDB主要特性: 1.1文档数据 ...

  3. zookeeper学习系列:四、Paxos算法和zookeeper的关系

    一.问题起源 淘宝搜索的博客 http://www.searchtb.com/2011/01/zookeeper-research.html  提到Paxos是zookeeper的灵魂 有一篇文章标题 ...

  4. zookeeper学习系列:二、api实践

    上一章我们知道zookeeper的简介,启动,设置节点以及结构性能.本小节我们来玩玩api,获取下数据. php版本: http://anykoro.sinaapp.com/2013/04/05/%E ...

  5. shell脚本学习系列之一---入门

    参考:http://me.52fhy.com/shell-book/ 待后续整理...

  6. 分布式学习系列【dubbo入门实践】

    分布式学习系列[dubbo入门实践] dubbo架构 组成部分:provider,consumer,registry,monitor: provider,consumer注册,订阅类似于消息队列的注册 ...

  7. Gradle学习系列之一——Gradle快速入门

    这是一个关于Gradle的学习系列,其中包含以下文章: Gradle快速入门 创建Task的多种方法 读懂Gradle语法 增量式构建 自定义Property 使用java Plugin 依赖管理 构 ...

  8. Gradle学习系列之一——Gradle快速入门(转)

    这是一个关于Gradle的学习系列,其中包含以下文章: Gradle快速入门 创建Task的多种方法 读懂Gradle语法 增量式构建 自定义Property 使用java Plugin 依赖管理 构 ...

  9. Asp.Net MVC5入门学习系列③

    原文:Asp.Net MVC5入门学习系列③ 添加一个视图(View) 接着上篇的入门系列,上面解说添加一个简单Controller(控制器),这里我们简单的在来添加一个View(视图)来展示我们Co ...

随机推荐

  1. Unity3D 装备系统学习Inventory Pro 2.1.2 总结

    前言 写在最前面,本文未必适合纯新手,但有一些C#开发经验的还是可以看懂的,虽然本人也是一位Unity3D新人,但是本文只是自己在学习Inventory Pro的学习总结,而不是教程,本人觉得要读懂理 ...

  2. 起因:dell超级密码算号器 1F66

    班里有几个比较调皮的同学~(显然不包括我[开玩笑的]) 当初班长设置了密码,结果那几个玩拳皇的想玩,就找我删除了密码~ 最后有个大神就过来设置了BIOS.有种终于出动了的样子.有种我看你怎么办的样子. ...

  3. PHP Apache服务配置

    第一步:在C:\Windows\System32\drivers\etc中找到hosts配置服务 127.0.0.1 localhost 127.0.0.1 www.psq.com 保存即可 第二步: ...

  4. [转] java集合详解

    结合框架体系应该最重要的是如何灵活利用三种接口,set,map,list,他们如何遍历,各有什么特征,怎么样去处理,这是关键,在这个基础上再去掌握在什么场合用什么类型的接口.比如说list和set,s ...

  5. sql server 中一次insert 多条的写法

    1.SELECT INTO FROM语句 注意此处 要求目标表Table2不存在,因为在插入时会自动创建表Table2,并将Table1中指定字段数据复制到Table2中.示例如下 2.INSERT ...

  6. jquery-mobile的页面跳转和iscroll之间的兼容解决方法

    有一项目需要用到滚动效果,最后选择了iscroll插件,代码写好后chrome测试一切正常(直接查看用到滚动效果的页面以下统称当前页面),运行APP应用一步步跳转到当前页面的时候,滚动效果和滚动条等死 ...

  7. [attribute^=value] 匹配给定的属性是以某些值开始的元素

    描述: 查找所有 name 以 'news' 开始的 input 元素 HTML 代码: <input name="newsletter" /> <input n ...

  8. HTML5 aria- and role

    HTML5 aria-* and role 在video-js的demo中看到了很多aria-*,不知道干嘛的.google一下,发现aria的意思是Accessible Rich Internet ...

  9. android 常见的泄漏内存方法和 leakcanary 使用方法

    虽然VM接管了内存分配和回收,但是人类在解决问题的同时也会重新创造出一些新的问题,所以问题永远都解决不了,就产生各种稀奇古怪的就业机会了(跑题跑不停). 无论各种VM用什么算法管理内存, 造成内存泄漏 ...

  10. 我的android学习经历36

    最近把android的基础知识都学的差不多了,也写了许多demo,就想自己写一个app,可是写到后面的时候发现很混乱,所以还是得写一些文档,用xml语言写一下基础的类以及一些其他的东西.所以要想写一个 ...