1.概述
1.1简介
Zookeeper分布式框架,是Google的Chubby一个开源的实现,是Apache Hadoop 的一个子项目,提供了协调分布式应用的基本服务,简化了分布式应用协调及其管理的难度,提供的功能包括统一命名服务、状态同步服务、集群管理等。
Zookeeper serer端基于java开发,client端提供c和java的api接口。包含一个简单的原语集,是Hadoop和Hbase的重要组件(HBase内置有Zookeeper,也可以使用外部Zookeeper),提供了分布式锁、队列、选举的接口,分布式锁和队列有Java和C两个版本,选举只有Java版本。
Zookeeper 作为一个分布式的服务框架,主要用来解决分布式集群中应用系统的一致性问题,它能提供基于类似于文件系统的目录节点树方式的数据存储,但是Zookeeper 并不是用来专门存储数据的,它的作用主要是用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。Zookeeper 主要是用来维护和监控一个目录节点树中存储的数据的状态,所有我们能够操作Zookeeper 的也和操作目录节点树大体一样,如创建一个目录节点,给某个目录节点设置数据,获取某个目录节点的所有子目录节点,给某个目录节点设置权限和监控这个目录节点的状态变化。
Zookeeper可以单机模式安装运行,长处在于通过分布式集群(一个leader,多个follower)实现分布式应用的可靠性。
组成Zookeeper的各个服务器必须要能相互通信。他们在内存中保存了服务器状态,也保存了操作的日志,并且持久化快照。只要大多数的服务器是可用的,那么Zookeeper 就是可用的。
1.2技术定位
Zookeeper可在集权管理,监控,分布式锁等多个场景使用,在开源软件HBase、Solr、Storm、Neo4j等中使用,在Yahoo、Rackspace、Linkedin、Twitter、Taobao、Ctrip 等公司被使用。
设计目的:
●最终一致性。client无论连接到哪个server,展示给它的都是同一个视图。
●可靠性。具有简单、健壮、良好的性能。
●实时性。Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或
者服务器失效的信息。但由于网络延时等原因,Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。
●原子性。更新只能成功,或者失败,没有中间状态。
●顺序性。包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息a在消息b
前发布,则在所有Server上消息a都将在消息b前被发布;偏序是指如果一个消息b 在消息a后被同一个发送者发布,a必将排在b前面。
缺点:
小数据处理、不支持大事务(数据操作保证原子性)。
1.3部署架构
在整个集群中只有一个leader,其他都是follower。如果leader出现问题,系统会采用leader election算法重新选举一个leader,因此,各节点之间要能保证相互连接,每个节点都要配置集群中其他节点的信息。
所有的Sever都保存一份完整数据,所有的Server都能对外服务(包括leader)。客户端随机连接到集群中的一个Server,并且维持TCP连接。并且发送请求,获取回复和事件,并且发送连接信号。如果这个TCP连接断掉了,那么客户端可以连接另外一个服务器。
Client与Servr之间采用长连接,连接建立后,Server产生session ID(64位)返回给Client。如果Client与Server通信足够频繁,不需要额外维护session。否则,Client 每T/3发一次心跳包给Server,如果Client T2/3没收到来自Server的心跳回应,就会切换到新的Server上,T为用户设置的session超时时间(zookeeper_init参数
recv_timeout)。因为Zookeeper集群会将Client的session信息持久化,在session没有超时之前,Client与Server的连接可以在各个Server之间透明的切换。
Zookeeper读写模式:
Replicated Database是包含了所有数据的内存数据库。
可以从任何一个Server读,当有读请求时,处理该请求的Server直接读取本地副本数据返回给Client端,无需转发给leader。
改变Zookeeper服务状态的请求和写请求按照协同协议执行,更新操作都是串行执行的。Server转发所有写请求给leader,然后leader通过原子广播协议(Zookeeper Atomic Broadcast)将请求广播给所有follower,leader收到一半以上的写成功ACK后,就认为写成功了,就会将该写进行持久化,并告诉客户端写成功。
Zookeeper存在WAL(write-ahead-log,预写日志),对于每一个更新操作,先写WAL,再更新内存数据,然后通知Client结果。另外,Zookeeper会定期将内存中的目录树进行Snapshot存到磁盘,一方面数据持久化,另一方面可以加快重启后的恢复速度,比通过执行全部WAL恢复速度快。
FIFO:
对于每一个ZooKeeper客户端而言,所有的操作都是遵循FIFO顺序的,这一特性是由下面两个基本特性来保证的:
●ZooKeeper Client与Server之间的网络通信是基于TCP,TCP保证了Client/Server
之间传输包的顺序。
●ZooKeeper Server执行客户端请求也是严格按照FIFO顺序的。
1.4数据模型
数据结构。Zookeeper名字空间由znode组成,组织方式与文件系统极为相似,其中各个节点相当于目录和文件,通过路径作为唯一标识,与文件系统不同的是,每个节点除了数据,还可以有子节点,除了EPHEMERAL类型的节点不允许有子节点。
节点信息。znode存储协调数据,例如状态、配置、位置等信息,每个节点存储的数据量很小、可配置。每个节点由一个Stat结构(数据变化版本号、ACL变化、时间戳),以允许缓存验证与协调更新。每当节点数据改变时,数据变化版本号递增。节点具有一个访问控制列表ACL约束哪些人能执行哪些操作。节点不支持重命名。
数据版本。节点数据可以有多个版本,比如某一个路径下存有多个数据版本,那么查询这个路径下的数据就需要带上版本。
读写原子性。节点数据以原子方式读写,读操作读取全部内容,写操作替换全部内容。
节点监视。znode可以被监视,包括节点数据的修改,以及子节点的变化等,一旦变化server通知设置监视的客户端,通过该特性可以实现配置集中管理、集群管理、分布式锁等功能。
EPHEMERAL节点。znode可以是临时节点,一旦创建这个znode的客户端与服务器失去联系,session失效,znode将自动删除。
SEQUENCE节点。znode的目录名可以自动编号,如App1已经存在,再创建的话,将会自动命名为App2 。
结构如下图所示:
每个节点由3部分组成:
●stat,状态信息,描述该znode的版本、权限等信息。
●data,与该znode关联的数据。
●children,该znode下的子节点。
1.5软件安装
1、下载安装包:https://www.doczj.com/doc/b73524924.html,/apache/Zookeeper/Zookeeper-3.3.6/
2、tar zxvf Zookeeper-3.3.6.tar.gz
1.5.1单机模式
1、进入Zookeeper目录下的conf子目录,创建zoo.cfg,配置说明参见伪集群模式章节:
tickTime=2000
dataDir=/home/market/Desktop/code/Zookeeper/data3
dataLogDir=/home/market/Desktop/code/Zookeeper/logs3
clientPort=2183
2、启动server。
./bin/zkServer.sh start #连接zoo.cfg
3、启动client。
./bin/zkClient.sh -server localhost:2183
1.5.2伪集群模式
所谓伪集群,是指在单台机器上启动多个Zookeeper进程,组成一个集群,以启动3个Zookeeper进程为例,将Zookeeper目录另外拷贝2份。
|--Zookeeper0
|--Zookeeper1
|--Zookeeper2
1、更改Zookeeper0/conf/zoo.cfg为:
ckTime=2000
initLimit=5
syncLimit=2
dataDir=/home/market/code/Zookeeper/data0
dataLogDir=/home/market/code/Zookeeper/logs0
clientPort=2180
maxClientCnxns=60
server.0=127.0.0.1:8880:7770
server.1=127.0.0.1:8881:7771
server.2=127.0.0.1:8882:7772
●ckTime:毫秒值,Zookeeper的基本时间单位。
●initLimit:集群中包含多台server, 其中一台为leader, 其余的为follower。initLimit
参数配置初始化连接时,follower和leader之间最长能忍受的心跳时间间隔数,如果超过10s(5倍ckTime)leader还没有收到follower的返回信息,就表明这个follower 连接失败。
●syncLimit:leader和follower之间发送消息、请求和应答的最大时间长度。
●dataDir:保存数据的目录,可以是任意目录。
●dataLogDir:log目录,可以是任意目录,如果没有设置该参数,使用和dataDir相
同的设置。
●clientPort:client连接服务器的端口,服务器监听该端口,接受客户端的请求。
●maxClientCnxns:最大客户端连接数。
●server.X=A:B:C 其中X是一个数字,表示这是第几号server,A是该server的IP地
址。B配置该server和集群中的leader交换消息的端口。C配置选举leader时所使用的端口。由于配置的是伪集群模式,所以各个server的B, C参数必须不同。
2、参照Zookeeper0/conf/zoo.cfg,修改Zookeeper1、Zookeeper2的zoo.cfg,只需修改dataDir、dataLogDir、clientPort参数即可。
3、在dataDir中新建myid文件,写入一个数字,该数字表示是第几号server,该数字必须和zoo.cfg文件中的server.X中的X一一对应。
/home/market/code/Zookeeper/data0/myid文件中写入0,
/home/market/code/Zookeeper/data1/myid文件中写入1,
/home/market/code/Zookeeper/data02/myid文件中写入2。
4、分别进入Zookeeper0/bin,Zookeeper1/bin,Zookeeper2/bin三个目录,启动server。
5、任意选择一个server目录,启动客户端连接到其中一个server,bin/zkCli.sh -server localhost:2181,在当前目录下生成zookeeper.out日志文件。
1.5.3集群模式
1、集群模式的配置和伪集群基本一致,由于集群模式下,各server部署在不同的机器上,因此各server的conf/zoo.cfg可以完全一样,例如:
ckTime=2000
initLimit=5
syncLimit=2
dataDir=/home/market/code/Zookeeper/data0
dataLogDir=/home/market/code/Zookeeper/logs0
clientPort=2180
server.43=10.1.39.43:8880:7770
server.47=10.1.39.47:8880:7770
server.49=10.1.39.49:8880:7770
2、需要注意的是各server的dataDir目录下的myid文件中的数字必须不同。10.1.39.43 server的myid为43,10.1.39.47 server的myid为47,10.1.39.49 server的myid为49。
1.5.4C client
Zookeeper C client的实现在src/c目录下,进入到该目录安装Zookeeper C client,步骤如下,3.4.6版本make出错,3.3.6版本没有问题:
(1)./configure
(2)make
(3)make install
(4)
cp include/proto.h /usr/local/include/c-client-src/
cp src/zk_adaptor.h /usr/local/include/c-client-src/.
cp src/zk_hashtable.h /usr/local/include/c-client-src/.
头文件位置:/usr/local/include/c-client-src/
库文件位置:/usr/local/lib/
1.6参考资料
1、AboutYun论坛
https://www.doczj.com/doc/b73524924.html,/forum-149-1.html
2、IBM文章介绍
https://www.doczj.com/doc/b73524924.html,/developerworks/cn/opensource/os-cn-Zookeeper/
3、使用和场景介绍:Zookeeper在携程的使用和场景介绍
https://www.doczj.com/doc/b73524924.html,/slides/8986
4、C API指南
https://www.doczj.com/doc/b73524924.html,/haippy/archive/2013/02/21/2920280.html
5、原理介绍
https://www.doczj.com/doc/b73524924.html,/blog/2014486
6、HBase应用
https://www.doczj.com/doc/b73524924.html,/blog/899632
https://www.doczj.com/doc/b73524924.html,/javaman_chen/article/details/7200405
7、开发常见问题
https://www.doczj.com/doc/b73524924.html,/?p=1189
8、zookeeper编程笔记
https://www.doczj.com/doc/b73524924.html,/caosiyang/archive/2012/11/09/2763190.html
https://www.doczj.com/doc/b73524924.html,/?p=2438
https://www.doczj.com/doc/b73524924.html,/2014/03/zookeeper-in-offline-computing.html 2.指令操作
2.1server操作
2.1.1启停server
若配置为伪集群模式,在不同的zookeeper/bin/目录下启动zkServer.sh,意味着对不同的server(zookeeper/conf/zoo.cfg中配置)进行操作:
1、启动服务器
$ bin/zkServer.sh start
2、检查服务器状态
$ bin/zkServer.sh status
JMX enabled by default
Using config: /home/market/code/soft/Zookeeper3/bin/../conf/zoo.cfg
Mode: standalone
3、停止服务器
$ bin/zkServer.sh stop
4、重启服务器
$ bin/zkServer.sh restart
2.1.2连接server
Zookeeper命令行工具类似于Linux的shell环境,使用它可以简单的对Zookeeper进行访问、创建、修改等操作。伪集群模式下,不同zookeeper/bin/zkCli.sh的使用没有区别。$ bin/zkCli.sh -server localhost:2180
2.1.3server切换
connect host:port:
[zk: localhost:2180(CONNECTED) 1] connect localhost:2181
2015-02-11 05:58:26,840 - INFO [main:Zookeeper@544] - Session:
0x4b78d21ff90002 closed
2015-02-11 05:58:26,840 - INFO
[main-EventThread:ClientCnxn$EventThread@516] - EventThread shut down
2.1.4退出客户端
退出客户端:
[zk: localhost:2180(CONNECTED) 4] quit
关闭当前会话连接,但不退出客户端:
[zk: localhost:2180(CONNECTED) 95] close
2015-02-17 22:11:30,941 - INFO [main:ZooKeeper@544] - Session:
0x4b97b2002b0017 closed
2015-02-17 22:11:30,941 - INFO
[main-EventThread:ClientCnxn$EventThread@516] - EventThread shut down
2.2指令说明
2.2.1指令一览
帮助命令help:
[zk: localhost:2183(CONNECTED) 21] help
Zookeeper -server host:port cmd args
get path [watch]
ls path [watch]
ls2 path [watch]
stat path [watch]
create [-s] [-e] path data acl
delete path [version]
quit
close
connect host:port
set path data [version]
history
redo cmdnum
setAcl path acl
getAcl path
printwatches on|off
delquota [-n|-b] path
listquota path
setquota -n|-b val path
sync path
addauth scheme auth
指令与C API对应关系参见《Zookeeper C API》。
2.2.2创建znode节点
创建节点/hello,并将字符串“world”关联到该节点中,如果没有指定value会创建失败:[zk: localhost:2183(CONNECTED) 4] create /hello world
Created /hello
[zk: localhost:2183(CONNECTED) 5] ls /
[hello, Zookeeper]
3种节点类型:
●persistent节点不和特定的session绑定,不会随着创建该节点的session结束而消失,
而是一直存在,除非该节点被显式删除。
●ephemeral节点是临时性的,如果创建该节点的session结束,该节点就会自动被删
除。ephemeral节点不能拥有子节点。虽然ephemeral节点与创建它的session绑定,但只要该节点没有被删除,其他session就可以读写该节点中关联的数据。使用-e参数指定创建ephemeral节点。
create -e /ephemeral ephe
●sequence节点,严格的说,并非节点类型中的一种。sequence节点既可以是
persistent的,也可以是ephemeral的。创建sequence节点时,server会在指定的节点名称后加上一个数字序列,该数字序列是递增的。因此可以多次创建相同的
sequence节点,而得到不同的节点。使用-s参数指定创建sequence节点。
[zk: localhost:2183(CONNECTED) 40] create /sequence seque
Created /sequence
[zk: localhost:2183(CONNECTED) 41] create -s /sequence/item item
Created /sequence/item0000000000
[zk: localhost:2183(CONNECTED) 42] create -s /sequence/item item
Created /sequence/item0000000001
[zk: localhost:2183(CONNECTED) 43] create -s /sequence/item item
Created /sequence/item0000000002
2.2.3删除znode节点
delete命令可以删除指定znode,当该znode拥有子znode时,必须先删除其所有子znode,否则操作将失败。
rmr命令可用于代替delete命令,rmr是一个递归删除命令,如果发生指定节点拥有子节点时,rmr命令会首先删除子节点。
[zk: localhost:2183(CONNECTED) 18] delete /hello
Node not empty: /hello
[zk: localhost:2183(CONNECTED) 19] rmr /hello
2.2.4设置znode数据
[zk: localhost:2180(CONNECTED) 7] set /hello newworld
2.2.5watch事件
2.2.5.1stat命令
stat命令获取节点的状态信息,第一个参数为znode,若第二个参数为true,则监听该znode的更新和删除事件。
2.2.5.2get命令
get命令返回指定节点的数据和状态信息。
[zk: localhost:2183(CONNECTED) 8] get /hello
zxid:
Zookeeper状态的每一次改变,都对应着一个递增的transaction id,该id称为zxid。由于zxid递增的性质,如果zxid1小于zxid2,那么zxid1肯定先于zxid2发生。创建、更新、删除任意节点的数据,都会导致Zookeeper状态发生改变,从而导致zxid的增加。
session:
在client和server通信之前,先要建立连接,该连接称为session。连接建立后,如果发生连接超时、授权失败或者显式关闭连接,连接便处于close状态,此时session结束。
监视器设置:
get命令第一个参数指定znode,若第二个参数为true,说明监听该znode更新和删除事件,子节点的增删事件不会触发。
[zk: localhost:2183(CONNECTED) 48] get /sequence true
[zk: localhost:2183(CONNECTED) 49] create -s /sequence/item item
Created /sequence/item0000000004
[zk: localhost:2183(CONNECTED) 50] delete /sequence/item0000000004
[zk: localhost:2183(CONNECTED) 51] rmr /sequence
WATCHER::
WatchedEvent state:SyncConnected type:NodeDeleted path:/sequence
2.2.5.3ls和ls2命令
包括ls和ls2,ls显示当前znode下的子节点,ls2显式当前znode下的子节点、以及当前znode的状态信息、不会显示当前znode的值。
[zk: localhost:2180(CONNECTED) 18] ls /
[hello, sequence, Zookeeper]
[zk: localhost:2180(CONNECTED) 19] ls2 /
[hello, sequence, Zookeeper]
cZxid = 0x0
ctime = Wed Dec 31 16:00:00 PST 1969
mZxid = 0x0
mtime = Wed Dec 31 16:00:00 PST 1969
pZxid = 0x100000005
cversion = 2
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 3
设置监视器:
ls命令第一个参数指定znode,若第二个参数为true,说明监听该znode子节点的增删,以及该znode的删除事件,只是一次性触发。
[zk: localhost:2183(CONNECTED) 45] ls /sequence true
[item0000000001, item0000000002, item0000000000]
[zk: localhost:2183(CONNECTED) 46] create -s /sequence/item item WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/sequence Created /sequence/item0000000003
2.2.6查看历史命令
history查看历史命令,以下数字105为命令编号,"ls"为具体命令:
[zk: localhost:2180(CONNECTED) 115] history
105 - ls /
106 - get cn
107 - get /cn
108 - get /cn
2.2.7重做历史命令
109表示"get /cn"的命令编号,redo 109表示重新执行命令"get /cn":
2.2.8监视打印设置
on打开watch打印项,off则关闭:
[zk: localhost:2180(CONNECTED) 147] printwatches on|off
2.2.9设置JVM
Zookeeper进程存在,但是不接受新的请求,log报错如下:
需要配置Zookeeper的JVM,可以在./conf/下配置java.env:
#!/bin/bash
export JAVA_HOME=/usr/java/jdk1.6.0.17/
export JVMFLAGS="-Xms1024m -Xmx1024m $JVMFLAGS"
-Xms1024m是最小内存,-Xmx1024m是最大内存。
或者修改zkServer.sh:
3.开发指南
3.1 C API简介
Zookeeper C API大部分接口以zoo_开头,只有少量接口以zookeeper_开头。
除了zookeeper_init、zoo_multi、zoo_amulti批量操作相关的zoo_op_t初始化外,其他API的第一个参数均是zhandle_t *zh,即Zookeeper句柄的指针。
除了初始化、销毁、设置日志等级、辅助函数外,Zookeeper C API根据同步和异步特性分为两类,同步接口以zoo_*打头,异步接口zoo_a*打头。
同步API:
只有zookeeper_mt库提供,主线程调用API后,等待io线程完成对应操作后再返回。
异步API:
两个库都提供,但调用方式不同。
●zookeeper_mt主线程调用API后立即返回,不是等待异步API实际完成后再返回,
所以在异步API返回时并不能确定异步API执行是否成功,不能依据返回值判断是否执行成功,即使返回OK。在io线程准备好数据后,由completion线程执行回调函数,在回调函数中依据返回值rc判断执行是否成功。
如果会话已经建立,提交了异步API,即使在程序退出时异步API没有执行完成,在程序退出后异步API依然会执行。
如果会话没有建立,提交了异步API,假如在程序退出时会话依然没有建立,则异步API不会执行。
如果会话没有建立,提交了异步API,假如在程序退出时会话已经建立,则异步API会执行。
●zookeeper_st为单线程,提供了对应的事件处理函数:zookeeper_interest、
zookeeper_process,应用程序可以根据这两个API完成对应的事件循环,进行异步API处理。
同步API与异步API区别:
●同步API等待Server端执行完成后再返回,异步API不等待Server端实际完成就立
即返回,所以在同步API返回时可以判断执行是否成功,但是在异步API返回时无法得知是否执行成功。
●异步API比同步API多了异步回调函数,该函数不同于监视回调函数,在异步回调函
数中判断异步API是否执行成功。
●异步API、同步API执行时都可以设置监视回调函数,可以设置监视回调函数的API
包括exists、get、get_children。
异步API回调函数:
●在异步API中使用的回调函数,不是在异步API返回时立即调用,而是在异步API实
际执行完成后或者Zookeeper客户端失去连接时(zookeeper_close)被调用。
●如果异步API提交了操作,但是在Zookeeper客户端断开时,异步API仍然没有执行
完成,此时异步API回调函数会被调用,参数值并不是异步API实际执行的情况。所以在提交异步API后,要预留一段时间等待异步API完成时调用异步回调函数。
●pzk关闭时,以下三个异步API已提交到Server端,但是Server还没有返回执行情
况,此时异步回调函数被调用,参数rc为-116、zookeeper is closing,参数rc并不能反映异步API在Server端的实际执行情况。
char path[32] = "/xyz";
pthread_mutex_lock(&mutex);
zoo_acreate(pzk, path, "value", 5,
&ZOO_OPEN_ACL_UNSAFE, 0, string_completion, "acreate");
zoo_aexists(pzk, path, 1, stat_completion, "aexists");
zoo_adelete(pzk, path, -1, void_completion, "adelete");
zookeeper_close(pzk);
监视回调函数与异步回调函数调用顺序:
先执行完成引起监视事件的动作,再执行监视回调函数,最后执行异步API回调函数。
Zookeeper C API错误码:
3.2编译说明
1、通常需要包含头文件
2、Zookeeper C客户端分别提供了单线程的库与多线程的库:
●zookeeper_st只是为了用于不支持pthread或者支持不完善的系统环境,除此之
外,应使用mt库。zookeeper_st只提供异步操作的API,应用程序需要在自己的事件循环中,调用api进行相关操作。
●zookeeper_mt会启动两个独立线程:网络io线程和completion线程,封装了
内部的事件循环,分别提供了同步和异步的API,采用mt库编译时必须增加宏
-DTHREADED。
网络io线程:负责与服务端通信,包括发送业务层API调用请求、服务端响应数据、服务端watch事件数据。
completion线程:从completion_to_process队列中取出数据,执行异步回调、watch回调,单线程对所有事件按照顺序回调。
编译示例:
g++ -DTHREADED -I/usr/local/include/c-client-src/ -L/usr/local/lib/
-lzookeeper_mt cli.c
3.3初始化与销毁
3.3.1初始化句柄
创建客户端与服务器通信的句柄以及对应于此句柄的会话,会话创建是异步的过程,所
以zookeeper_init返回时,会话不一定建立。zookeeper_init返回非空句柄不代表初始化完成,只是代表句柄创建成功。仅当会话建立成功后,操作才会提交到Server端。
如果使用zookeeper_mt库,zookeeper_init调用成功,会启动io线程、completion 两个线程。
ZOOAPI zhandle_t *zookeeper_init(const char *host, watcher_fn fn,
int recv_timeout,
const clientid_t * clientid,
void *context, int flags);//失败返回NULL
●host为逗号隔开的IP:PORT对,每个IP:PORT对代表一个zookeeper server,例如
"127.0.0.1:2180,127.0.0.1:2181,127.0.0.1:2182"。host可以是Zookeeper集群中的全部或部分Zookeeper实例的IP:PORT对,Client随机连接其中一台Server。
●fn,全局的监视器回调函数,不允许设置为NULL。当zookeeper_init调用后会话建
立时或者发生事件通知时,该函数被调用,fn并不是由销毁句柄或者程序退出触发的。
●recv_timeout,单位毫秒,客户端连接服务器的超时时间。
如果超过recv_timeout客户端没有连接上Zookeeper,则表示连接超时。
另外,如果客户端断开连接recv_timeout,则session失效。
●clientid,客户端尝试重连的先前会话的ID,如果不需要重连先前的会话,则设置为0。
如果clientid对应的会话超时,或者由于某种原因clientid变为无效了,那么
zookeeper_init 将返回一个非法的zhandle_t,通过zhandle_t 的状态可以获知zookeeper_init 调用失败的原因。(通常为ZOO_EXPIRED_SESSION_STATE)。
●context,与zhandle_t 实例相关联的“上下文对象”(可以通过该参数为zhandle_t
传入自定义类型的数据),应用程序可以通过zoo_get_context 访问它(例如在监视器回调函数中),当然zookeeper 内部没有用到该参数,所以context 可以设置为NULL。
●flags,目前为保留参数,设置为0。
zhandle_t* zkhandle = zookeeper_init(host, zktest_watcher_g,
timeout, 0, (void *)"init", 0);
判断成功初始化(句柄创建成功+会话已建立)的3种方法,初始化成功后再使用Zookeeper:
●zoo_state(zkhandle),判断返回的state是否为ZOO_CONNECTED_STATE。
●在zookeeper_init中设置watcher,当zookeeper client与server会话建立后,触
发watcher,当watcher 的state = 3(ZOO_CONNECTED_STATE),type = -1(ZOO_SESSION_EVENT)时,确认会话成功建立,此时zookeeper client 初始化成功,可进行后续操作。
●尝试调用zoo_exists或zoo_get_data等操作,根据操作结果判断初始化是否成功。
zookeeper_init设置recv_timeout 100000ms,但客户端与服务端断开连接30s
就session失效了:
关于session超时时间的确定:zookeeper_init中设置的超时时间并非真正的session 超时时间,session超时时间需要server与client协商,业务通过
zoo_recv_timeout(zhandle_t* zh)获取server与client协商后的超时时间。服务端: minSessionTimeout (默认值为:tickTime * 2) , maxSessionTimeout(默认值
为:tickTime * 20), ticktime的默认值为2000ms。所以session范围为4s ~ 40s 。客户端:sessionTimeout,无默认值,创建实例时设置recv_timeout 值。经常会认为创建zookeeper客户端时设置了sessionTimeout为100s,而没有改变server端的配置,默认值是不会生效的。原因:客户端的zookeeper实例在创建连接时,将sessionTimeout 参数发送给了服务端,服务端会根据对应的minSession/maxSession Timeout的设置,强制修改sessionTimeout参数,也就是修改为4s~40s 返回的参数。所以服务端不一定会以客户端的sessionTImeout做为session expire管理的时间。
3.3.2销毁句柄
销毁句柄,释放资源:
●调用该函数后,会话将不可用。再对句柄进行操作,将产生不确定的结果。
●一个句柄只能调用一次,多次调用将产生不确定的结果。
●函数返回前,会将未发送完的请求发送完,所以可能会阻塞。
ZOOAPI int zookeeper_close(zhandle_t * zh);
3.4创建节点
3.4.1ACL说明
ACL权限说明:
●ACL指定所有用户的访问权限,权限不区分用户。
●节点的ACL没有继承关系,每个节点ACL独立控制。
●每个客户端连接都有一个id,客户端试图访问节点时,用id与ACL比对,以确定客户
端的访问权限。
struct Id 结构为:
struct Id {
char *scheme;
char *id;
};
struct ACL 结构为:
struct ACL {
int32_t perms;
struct Id id;
};
struct ACL_vector 结构为:
struct ACL_vector {
int32_t count;//data元素个数
struct ACL *data;
};
scheme:id:
●world: 它下面只有一个id叫anyone, world:anyone代表任何人,zookeeper中对
所有人有权限的节点就是属于world:anyone的。
●auth: 它不需要id, 只要是通过authentication的user都有权限(zookeeper支持
通过kerberos来进行authencation, 也支持username/password形式的
authentication)。
●digest: 它对应的id为username:BASE64(SHA1(password)),它需要先通过
username:password形式的authentication。
●ip: 它对应的id为客户机的IP地址,设置的时候可以设置一个ip段,比如
ip:192.168.1.0/16, 表示匹配前16个bit的IP段。
●super: 在这种scheme情况下,对应的id拥有超级权限,可以做任何事情(cdrwa)。
与ACL.perms(znode访问权限)相关的常量,
●const int ZOO_PERM_READ=1<<0;//允许客户端读取该节点的值以及子节点列表。
●const int ZOO_PERM_WRITE=1<<1;//允许客户端设置该节点的值。
●const int ZOO_PERM_CREATE=1<<2;//允许客户端在该节点下创建子节点。
●const int ZOO_PERM_DELETE=1<<3;//允许客户端删除该节点及子节点。
●const int ZOO_PERM_ADMIN=1<<4;//允许客户端执行set_acl,设置当前节点权限。
●const int ZOO_PERM_ALL=0x1f;//允许客户端执行所有操作,等价与上述所有标志的或。
与ACL.id相关的常量,
●struct Id ZOO_ANYONE_ID_UNSAFE=("world", "anyone");
●struct Id ZOO_AUTH_IDS=("auth", "");
三种标准的ACL_vector,
●struct ACL_vector ZOO_OPEN_ACL_UNSAFE;
//(ZOO_PERM_ALL,ZOO_ANYONE_ID_UNSAFE)
●struct ACL_vector ZOO_READ_ACL_UNSAFE;
//(ZOO_PERM_READ, ZOO_ANYONE_ID_UNSAFE)