zookeeper 入门 (来自官方文档)
来源于:
http://zookeeper.apache.org/doc/trunk/zookeeperProgrammers.html#ch_guideToZkOperations
讲对于 zookeeper 编程的简介:
?
简介:
本文讲了如何应用zk的优势创建分布式应用,包含了如下几个部分.
?
首先4个章节从一个比较抽象的层面讲了zookeeper的概念, 这使读者能够理解zk怎么工作和这样使用zk工作. 他不包括源代码,假像读者熟悉分布式计算的问题. 以下是四个部分:
?
The ZooKeeper Data Model
ZooKeeper Sessions
ZooKeeper Watches
?
?
?
后面的四个部分提供了具体的编程信息:
?
Building Blocks: A Guide to ZooKeeper Operations
Bindings
Program Structure, with Simple Example?[tbd]
?
如果读者是第一次使用zk, 你应该读 Zookeeper Data Model 和 ZooKeeper Basic Operations.还有 Simple Programming Example 会帮助你理解 zk 客户端编程的基本知识.?
?
?
创建一个连接, 应用层必须提供的一个可以连接的以逗号分割的主机列表,zk server的地址:(e.g. "127.0.0.1:4545" or "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"). 客户端将随机选择一个server 尝试连接. 如果连接失败,客户端将重新从list中自动选择下一个server,自动连接建立.
?
Added in 3.2.0:一个可选的"chroot"后缀可以加载一个连接请求后面.用起来像是这样:?"127.0.0.1:4545/app/a" or "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a" 当一个客户端在 /app/a ?是所有者.那请求的路径可以是相对路径,比如 getting "/foo/bar" 将导致一下命令被执行"/app/a/foo/bar".这种特性适用于多租户的环境下,每个租户用 zk的一部分.而编码的时候可以重用一部分代码.
?
当一个客户端得到 zk service 的handle的时候, zk创建一个 zk session, 表现为一个64 bit的数字, ?分配给这个客户端.如果这个客户端连上不同的server, 他会把这个 session的id 作为握手协议的参数传过去, 作为一个安全措施, 这个server会创建这个session的 密码, 所以所有的server都可以验证.这个密码在创建的时候随着 id 一起传给客户端.客户端在重新建立这个session的时候把这个session和密码一起传给新的server.
?
当一个客户端如果被zk 集群划分(比如按照ip 做hash) , 客户端会到list中寻找特定的server 在session创建的时候.最终, 在客户端,服务端在重新建立连接后, session会过度到 "connected"状态(在 session 超时时间内)或者到"expired"状态(在 session 超时时间外). zk并不鼓励在断掉连接的时候创建一个新的对象.(new ?ZooKeeper.class or zookeeper handle). zk 客户端库中会处理重连. 仅当 你被通知 session 超时的时候再去创建新的session.
?
session超时被zk集群自己管理,而不是客户端.当session 建立的时候会提供一个"timeout"的属性.这个属性用在集群中决定什么时候客户端的session 超时.超时发生在集群在特定的时间内没有收到客户端的回应.在session 超时的时候,集群会删除这个session创建的所有的临时节点, 通知所有连着的客户端这个变化. 从这个意义上来讲, 超时的这个session 将不会收到session超时的通知,直到TCP 连接被再次建立.
?
以下是session超时时候的状态转换,被 watcher 看到的:
1. 'connected' session被创立,客户端 ,服务端无障碍交流.
2. ... 客户端被划分,重新选择服务器.
3. 'disconnected' : 客户端跟服务端失去连接.
4. ... 时间流逝,在'timeout'之后 集群将session 超时, 什么都不会被客户端看到.
5. ... 时间流失, 客户端恢复网络,跟集群建立连接.
6. 'expired': 最终客户端重连上 集群,被通知超时.
?
另外session的一个参数是 默认 watcher. 客户端watcher 在状态改变的时候被通知.比如说, 客户端跟集群失去连接会接到通知.在新连接这种情况下, 第一个给watcher的事件往往是 session 连接的时间.
?
这个session 会持续保持活跃. 如果一个session 在一段时间内是闲置的,将会导致这个session 超时,这个时候,客户端就会发送PING 来保持这个连接活跃.这个PING 不光是让服务端知道客户端是活着的, 也让客户端保证服务端的连接是好使的.PING的时间足够让各方来识别这条连接.
?
一旦这条连接建立起来,有两个原因让客户端代码抛出 connectionloss的异常.
1. 客户端想对已经无效的连接做操作.
2. 如果一些异步调用当连接已经断掉的时候.
?
Added in 3.2.0 -- SessionMovedException. ??
这是一种不被客户端发现的内部错误.这种异常在收到在不同server 已经建立连接的情况. 详细点说这种情况, 客户端发送一个请求给一个server , 由于网络堵了, 客户端超时, 连接一个新的server.网络又通了, 请求发送到老的server上,老的server发现session已经转移,关掉这个连接.客户端不会收到这种异常,因为他根本不会从老的连接读东西.一种情况会在客户端出现这种异常,就是两个客户端尝试用重新连接,用这个 session的id, 和 密码.一个将正确的建立连接.另外一个会超时.
?
ZooKeeper Watches
所有从zk读的操作?-?getData(),?getChildren(), and?exists() 都有一个选项,设置一个watch 进去处理副作用.这里是zk定义的watch的定义: 一个监听的 watch是一个一次性的触发器,当客户端设置的数据变化的时候触发.以下三个关键点帮助理解watch:
One-time trigger
一个watch的时间在数据发生变化的时候发送到客户端.举个例子, 如果一个客户端执行 getData("/znode",true)的操作,然后/znode 变化或者被删除的时候,客户端会收到一个事件.如果/znode再次变化, 将不再有事件发送到客户端.除非客户端get了其他数据.
Sent to the client
假设一个事件在到客户端的路上,但是有可能没有达到客户端,在发起改变的客户端收到正确的返回码之前.到watchers 的都是异步的操作. zk保证了顺序: 一个客户端将永远不会在收到watch 事件之前看到变化.网络延迟或者i其他因素可能导致不同的客户端看到返回值和 watch 不一样.关键是被不同客户端看到的将有一致性的顺序.
The data for which the watch was set
一个节点有好几种改变的方式. zk维护两种watch: 数据 watch 和字节点watch. getData 和 exists ?设置数据 watch.getChildren设置 自己点 watch. ? getData 和 exists返回节点的数据, ?getchildren 返回字节点的一个list.因此, setData将触发数据watch.成功的 create 将出发 数据 watch 和 字节点watch. 同理是delete操作.
?
watches 在zk server的本地进行管理.这样方便轻量级的set,维护和分发. 放一个客户端连到一个新的 server,这个watch将包括所有的session 事件.在连接断掉之后就收不到watch了.当一个客户端重连,所有之前注册的 watch将被重新注册和触发.一般来说,这对上层是透明的.
?
?
?有两个包组成了java binding:org.apache.zookeeper?and?org.apache.zookeeper.data.
其他的包组成了server端实现的data包里是一些生成的class 作为容器使用的。
java中main class 是zookeeper?,他的两个不同的构造函数只是 session id 和password的区别,他支持session恢复通过一个process的实例,一个java程序能够吧session id 和 password存储起来,重启之后能用到早期程序中的 session id 和password。
当zookeeper?实例被创建之后,两个线程起来:一个IO线程,一个处理事件线程,所有IO操作都通过IO线程实现(用java nio),所有事件的回调都通过那个事件线程处理。session的维持,包括zk的重连和维持心跳,都通过IO线程完成,所有事件的监听都通过事件线程。这种设计有几个注意点:
所有完整的异步调用和watcher的回调都按照顺序进行,一次一个,但是当线程处理的时候是不会有任何反馈的。
回调不会阻塞IO线程或者其他的同步调用。
同步调用可能不会按照顺序返回。举个例子:如果一个客户端做了如下操作:假设一个异步调用读取 /a 节点,同时设置watch 为true,然后同步去读 /a 的内容。
如果这个时候 改变a的值,在异步调用和同步调用之间,客户端库会收到事件说 /a 已经被改变,时间实在同步调用之前,整个回调在阻塞event queue,这个同步的读将会先返回 a的最新的值。
?
?
?