ZooKeeper学习笔记(四)之基本概念
ZooKeeper数据模型
ZooKeeper拥有一个层级结构的目录空间,非常类似分布式文件系统,唯一的不同是每个node可同时拥有数据和子node,即文件同时也是目录。只使用绝对路径,总的来说任何Unicode字符都可以作为路径的一部分,但遵循以下约束:
null(\u0000)不能用在path中,因为在C中会导致Binding异常。
以下字符因为不能很好显示或易让人迷惑,不可用在path中:\u0001 - \u0019 and \u007F - \u009F.
以下字符也不允许使用: \ud800 -uF8FFF, \uFFF0-uFFFF, \uXFFFE - \uXFFFF (X 代表 1 - E), \uF0000 - \uFFFFF.
.和..可用,但不能单独用来表示node, 亦即/a/b/./c” or “/a/b/../c都是非法的。
zookeeper是保留字
ZNodes
ZooKeeper tree中的每一个Node都指向一个znode, znode维护一个状态结构,结构中包含数据/acl 更新的版本号
和时间戳
,二者共同用于验证缓存和同步更新。znode每更新一次 ,版本号增加,读取数据时会得到相应的版本号,更新数据时也应提供版本号,如果版本号不符,操作失败(注:提供强制模式,会忽略版本号)。
Znode是应用访问最多的实体,有以下两个特性值得关注。
Watch
Client可在znode上设置watch,znode更新触发并移除watch,通知信息将发送给设置了watch的Client.
数据存取Data Access
namespace中znode存储的数据的读写是原子的,读写都作用到数据的全体,每个Node都有ACL控制访问权限。
ZooKeeper并不是为通用数据库或对象存储设计的,相对地,它管理同步数据。数据可以是配置,状态信息,集结地等,它们的共同点是体积小(KB级)。Server和Client会确认数据大小不超过1M,实际平均值比这更小。操作相对较大的数据会导致一些操作用时大大增加,因为需要在网络间传输大量的数据到存储设备上。大文件存储可以使用NFS或者HDFS,同时在ZooKeeper中保存上述存储位置即可。
瞬时节点Ephemeral Nodes
这种Node的生存期等于创建该它的会话的存活期,会话结束节点即被删除,因此这种node不能有子node.
序列节点Sequence Nodes
创建node时可以请ZooKeeper在path结尾添加递增的计数器。每个父node拥有唯一的计数器,格式为%010d,即总长为10,用0填充。
注:计数值为int, 4字节,溢出时会出现负数。
ZooKeeper中的时间。
ZooKeeper用多种方式跟踪时间。
Zxid
对ZooKeeper状态的任何更改都会收到一个zxid,使得ZooKeeper的更改是完全有序的,较小的zxid对应的更改一定在较大的zxid操作之前。
版本号Version numbers
对znode的每次更改都会导致node的某一版本号增加,一共有三种版本号,对应不同实体:
version 数据
cversion 子node
aversion acl
计时Ticks
当使用ZooKeeper的多Srever系统时,Server用tick计量事件时间,比如一次状态上传,会话超时,peer间连接超时等。tick time只通过会话超时
(2倍tick time)来非直接地对外表示。如果Client请求的会话超时比小于系统最小值,Server将告诉它使用的是最小超时时间。
时钟时间Real time
ZooKeeper只在znode创建和更改时向状态结构中添加的时间戳中使用时钟时间。
状态结构Stat Structure
znode的状态结构stat structure包含以下域:
czxid
创建该Node的zxid.
mzxid
znode最后更改的zxid.
ctime
创建时的毫秒值
mtime
最后更改的毫秒值
version
对znode数据更改的number.
cversion
对子node更改的number.
aversion
acl更改number.
ephemeralOwner
拥有者的会话ID,非瞬时node值为0
dataLength
znode数据的长度.
numChildren
子node数.
会话ZooKeeper Sessions
client创建handle来建立与ZooKeeper服务的会话,会话刚建立时,handle状态为CONNECTING
,client库会连接组成服务众多Server中的一个,连接成功后状态转换为CONNECTED
。正常操作中会保持上述状态中的一个,如果遇到不可恢复的错误,如会话超时,授权失败或者主动关闭handle,状态会转换至CLOSED
。
创建会话时需要提供一个包含server对应的ip:port
字符串的List,Client库会随机选择一下连接,如果连接失败或者被断开,client会使用另一个地址,直到(重)连接成功。
3.2.0中添加的特性:可选择添加chroot到地址末,用来更改初始root目录,例如”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”,client将自动位于/app/a下,任何更改(如创建node)也是在此目录下。
Client handle连接到服务后,ZooKeeper创建会话并分配一个64位整数(Session ID)给Client,如果Client要连接到另一个Server,握手时会提供这个Number。安全起见,ZooKeeper为会话创建所有Server能认证的password,并在会话建立时一起发送给Client,重连时Client将SessionID和password一并提供。
Cient建立会话时可设置超时时间,服务能提供的最小值为tickTime的2倍,最大值为20倍。
会话重连如果超时,Client状态会转为expired
,一般来说不需要重新创建一个会话,因为Client库设计了相应的自动重连机制,但超时后可以重新建立会话。
超时是由服务集群管理的,超时后所有eppheminal node被删除并通知设置了watch的进程,而原client只会在重新建立TCP连接后收到session expired”的notification。
创建会话时可选用default watcher
参数,Client的任何状态变化都会通知client,如会话超时,建立和断开。
闲置状态时,Client可发送Ping请求来保持连接。
连接建立后,一般来说以下两种情况执行同步/异步操作时会导致连接丢失:
1. 在失效的会话上进行操作
2. Clinet在有尚未完成的异步操作时从某一Server断开
Added in 3.2.0 — SessionMovedException. Client通常不会看到,发生于从某个连接收到请求,但会话已经转换到另一个Server时,通常是网络延时导致的。
监听/ZooKeeper Watches
所有的读操作getData(), getChildren(), and exists()
都可以设置watch. Watch触发是一次性的,当指定的数据更改时,消息发发送给Watch设置者。有以下三点需要关注。
One-time trigger
例如client1设置了getData(“/znode1”, true),另一个Client修改或删除了znode1,Client1将收到消息,但之后znode1的修改就不会通知Client1了,除非再次设置Watch.
Sent to the client
意味着消息可能在操作者收到返回码(操作完成)时还没有被设置watch的Client收到。watch消息是异步发送给Client的,ZooKeeper提供顺序保障,Client不会在收到消息之前看到它设置了watch的对象的变化。由于网络延时的其它因素的影响,不同的Client在收到消息和返回码上有先后,但是对同一个Client是保证有序的。
watch对象
可理解为有两种watch,数据和子node. getData() 和 exists() 设置数据watch, getChildren()设置子node watch,因此也可以理解为返回数据类型决定watch类型,所以setData()会触发数据Watch(操作失败则不会)。成功的create()会同时触发父node的子node watch和被创建node的数据watch,其它的类推。
watch是由与client连接的Server本地维护的,有助于watch设置维护和分发的轻量化。当连接到另一个Server时,任何的session事件都会触发watch。断线重连后,之前注册的的watch会重新注册,需要的话也可触发。总之一切都是透明的,但有一种情况会丢失watch:watch一个尚不存在的znode,断线期间该node创建又被删除。
Watch有哪些保证
对于watch,ZooKeeper保证以下三点:
watch相对其它事件、watch和异步回复都是有序的,client库保证所有事情都是有序分发的。
Client在看到新数据前收到watch
watch事件的顺序和server看到的事件发生顺序一致。
关于Watch的须知
由于watch的一次性,如果想持续watch一个对象,那么每次收到消息后重新设置watch。
由于一次性和延迟,你可能不会收到所有的更改,准备好处理两次消息之间znode已多次更改的情况,至少头脑中要有这个概念。
同一类watch只会触发一次,即使之前设置了多次。
连接断开后,只有在连接重新建立时才能收到watch,For this reason session events are sent to all outstanding watch handlers. Use session events to go into a safe mode: you will not be receiving events while disconnected, so your process should act conservatively in that mode.