看了之前的教程,会发现多处出现zookeeper,比如hadoop的 auto failover 得用 zookeeper ,Hbase 的 RegionServer 也得用zookeeper。其实不止hadoop,包括现在小有名气的 Storm 用的也是zookeeper。那么zookeeper 究竟是做什么用的?
- ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务。
- ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户
- ZooKeeper包含一个简单的原语集,提供Java和C的接口。
在Zookeeper中,znode是一个跟Unix文件系统路径相似的节点,可以往这个节点存储或获取数据。如果在创建znode时Flag设置为EPHEMERAL,那么当创建这个znode的节点和Zookeeper失去连接后,这个znode将不再存在在Zookeeper里,Zookeeper使用Watcher察觉事件信息。当客户端接收到事件信息,比如连接超时、节点数据改变、子节点改变,可以调用相应的行为来处理数据。Zookeeper的Wiki页面展示了如何使用Zookeeper来处理事件通知,队列,优先队列,锁,共享锁,可撤销的共享锁,两阶段提交。
简单的说: 通过在zookeeper里面注册znode节点,我们可以自动监控这些节点是否活着,而不用手动去实现这些事情,所以zookeeper解决了用户想知道节点“是否活着?”的问题
快速开始
安装
服务端,在每一台需要监控的机器上安装
yum install zookeeper-server
客户端
yum install zookeeper-client
配置
编辑 /etc/zookeeper/conf/zoo.cfg ,所有机器上的 zoo.cfg 这个文件都是一样的
maxClientCnxns=50
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
dataDir=/var/lib/zookeeper
# the port at which the clients will connect
clientPort=2181
server.1=host1:2888:3888
server.2=host2:2888:3888
- tickTime 单位是毫秒,被用作计算会话超时时间和心跳时间的最小单位,这两个时间会被表示成 tickTime 的倍数,而不是直接写上毫秒数,不懂这样设计的好处在哪里,不过至少用户简单了一点,只需要写上倍数就行了
- dataDir 存储内存数据的快照或者关于事务的更新日志
- clientPort 给客户端使用的端口
使用
在所有机器上启动zookeeper
service zookeeper-server start
使用 zookeeper-client 进入客户端
[zk: localhost:2181(CONNECTED) 0]
使用help命令看看有什么可以用的命令
[zk: localhost:2181(CONNECTED) 0] help
ZooKeeper -server host:port cmd args
connect host:port
get path [watch]
ls path [watch]
set path data [version]
rmr path
delquota [-n|-b] path
quit
printwatches on|off
create [-s] [-e] path data acl
stat path [watch]
close
ls2 path [watch]
history
listquota path
setAcl path acl
getAcl path
sync path
redo cmdno
addauth scheme auth
delete path [version]
setquota -n|-b val path
我们来试试看最简单的 ls 命令
[zk: localhost:2181(CONNECTED) 1] ls /
[hadoop-ha, hbase, zookeeper]
[zk: localhost:2181(CONNECTED) 2] ls /hadoop-ha
[mycluster]
可以看到下面有三个节点,hadoop-ha 下面还有一个节点。这都是因为我之前的教程已经安装了hadoop和hbase所以会有这些节点。做到这里,我们明白了,zookeeper维持了一个类似文件夹结构的空间,在这个空间内存储的东西就是znode,znode可以有自己的子节点
创建节点
接下来,我们试试创建一个新的节点
[zk: localhost:2181(CONNECTED) 3] create /zk_test my_data
Created /zk_test
[zk: localhost:2181(CONNECTED) 4] ls /
[hadoop-ha, hbase, zookeeper, zk_test]
这个zk_test 是我们要建立的节点名字,my_data 是节点的数据。
我们可以用get命令看下节点的数据
节点数据
[zk: localhost:2181(CONNECTED) 5] get /zk_test
my_data
cZxid = 0x2200000019
ctime = Sun Jan 18 02:30:56 PST 2015
mZxid = 0x2200000019
mtime = Sun Jan 18 02:30:56 PST 2015
pZxid = 0x2200000019
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 0
在讲解这些参数之前先介绍一下一个概念:
ZooKeeper以多种方式跟踪时间
- zxid:每次修改ZooKeeper状态都会收到一个zxid形式的时间戳,也就是ZooKeeper事务ID。事务ID是ZooKeeper中所有修改总的次序。每个修改都有唯一的zxid,如果zxid1小于zxid2,那么zxid1在zxid2之前发生。
- 版本号:对节点的每次修改将使得节点的版本号增加一。版本号有三种:version(znode数据修改的次数)、cversion(znode子节点修改的次数),以及aversion(znode的ACL修改次数)。
- tick:多服务器ZooKeeper中,服务器使用tick来定义状态上传、会话超时、节点间连接超时等事件的时序。tick仅被最小会话超时(2倍的tick时间)间接使用:如果客户端要求小于最小会话超时的时间,服务器将告知客户端,实际使用的是最小会话超时。
- 真实时间:除了在创建和修改znode时将时间戳放入stat结构体中之外,ZooKeeper不使用真实时间,或者说时钟时间。
get获得的参数的意思是:
- czxid:创建节点的事务的zxid
- mzxid:对znode最近修改的zxid
- ctime:以距离时间原点(epoch)的毫秒数表示的znode创建时间
- mtime:以距离时间原点(epoch)的毫秒数表示的znode最近修改时间
- pzxid:子节点的最后版本
- cversion:znode子节点修改次数
- dataVersion:数据的版本
- aclVersion:znode的ACL修改次数
- ephemeralOwner:如果znode是临时节点,则指示节点所有者的会话ID;如果不是临时节点,则为零。
- dataLength:znode数据长度。
- numChildren:znode子节点个数。
修改节点
不需要完全看懂以上这些参数,我们可以试着修改一下数据再看
[zk: localhost:2181(CONNECTED) 6] set /zk_test junk
cZxid = 0x2200000019
ctime = Sun Jan 18 02:30:56 PST 2015
mZxid = 0x220000001a
mtime = Sun Jan 18 02:55:35 PST 2015
pZxid = 0x2200000019
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
[zk: localhost:2181(CONNECTED) 7] get /zk_test
junk
cZxid = 0x2200000019
ctime = Sun Jan 18 02:30:56 PST 2015
mZxid = 0x220000001a
mtime = Sun Jan 18 02:55:35 PST 2015
pZxid = 0x2200000019
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
再对比一下之前的参数,会发现以下参数改变了
- mZxid :变为另一个数据,表示最后修改的版本
- mtime:最后修改时间变了
- dataVersion:从0变成1,表示数据的版本增加了1
- dataLength:数据长度变化了
什么是ACL
ZooKeeper使用ACL控制对节点的访问。ACL的实现同Unix文件访问权限非常相似:采用权限位来定义允许/禁止的各种节点操作,以及位应用的范围。与标准Unix权限不同的是,ZooKeeper节点不由用户(文件所有者)、组和其他这三个标准范围来限制。ZooKeeper没有节点所有者的概念。取而代之的是,ACL指定一个ID集合,以及与这些ID相关联的权限。
还要注意的是,ACL仅仅用于某特定节点。特别是,ACL不会应用到子节点。比如说,/app只能被ip:172.16.16.1读取,/app/status可以被所有用户读取。ACL不是递归的。ZooKeeper支持可插入式鉴权模式。使用scheme:id的形式指定ID,其中scheme是id对应的鉴权模式。比如说,ip:172.16.16.1是地址为172.16.16.1的主机的ID。
客户端连接到ZooKeeper,验证自身的时候,ZooKeeper将所有对应客户端的ID都关联到客户端连接上。客户端试图存取节点的时候,ZooKeeper会在节点的ACL中校验这些ID。ACL由(scheme:expression,perms)对组成。expression的格式是特定于scheme的。比如说,(ip:19.22.0.0/16,READ)给予任何IP地址以19.22开头的客户端以READ权限。
只需要知道概念就行了,具体的使用等需要的时候再学习。
删除节点
[zk: localhost:2181(CONNECTED) 8] delete /zk_test
[zk: localhost:2181(CONNECTED) 9] ls /
[hadoop-ha, hbase, zookeeper]
可以看到节点被删除了
总结
其实这个教程只是让你知道了zookeeper是如何管理节点的,但是没有说明zookeeper是如何监听节点和标记节点的,因为那些有点复杂并且对我们普通开发者其实作用不太大,这些一般是 hadoop或者storm 的开发者需要考虑的问题。我们作为最终用户只需要对zookeeper是个怎样的东西,究竟是长什么样子有一个感性的认识就可以了。
参考资料
- http://zookeeper.apache.org/doc/trunk/zookeeperStarted.html
- http://zookeeper.apache.org/doc/trunk/zookeeperProgrammers.html#sc_zkDataModel_znodes
- http://blog.163.com/wm_at163/blog/static/132173490201232423051163/