ZooKeeper入门
- Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目
- 工作机制
- Zookeeper从设计模式角度来理解:是一个基于观察者设计的分布式管理框架,它负责存储和管理数据,然后接受观察者的注册,一旦这些数据的状态发生变化,zookeeper就负责通知已经注册观察者做出相应的反应
- Zookeeper=文件系统+通知机制
- 特点:
- zookeeper:一个领导者(leader),多个跟随者(follower)组成的集群
- 集群中只要有半数以上的节点存活,zookeeper就能正常工作
- 全局数据一致:每个server保存一份相同的数据副本,client无论连接到哪个server数据都是一致的
- 更新请求顺序进行:来自同一个client的更新请求按其发送的顺序依次执行
- 数据更新原子性,依次数据要么成功要么失败
- 在一定时间范围内,client能读到最新数据;
- 数据结构
- zookeeper跟unix文件系统很类似,整体上可以看成一棵树,每个节点成为ZNode,每个ZNode默认能够存储1M的数据,每个ZNode都可以通过其路径唯一标识;
- zookeeper跟unix文件系统很类似,整体上可以看成一棵树,每个节点成为ZNode,每个ZNode默认能够存储1M的数据,每个ZNode都可以通过其路径唯一标识;
- 应用场景
- 提供的服务包括:统一命名服务,统一配置管理,统一集群管理,服务器节点动态上下线,软负载均衡;
- 统一命名服务:在分布式环境下,对应用或者服务统一进行命名,便于识别;
- 统一配置管理:
- 分布式环境下,配置文件同步非常常见
- 一般要求一个集群中,所有节点的配置信息时一致的比如Kafka集群
- 对配置文件修改后,希望能够快速同步到各个节点上
- 配置文件可以交给zookeeper来实现
- 可将配置文件写到zookeeper的一个znode;
- 各个客户端服务器监听这个znode;
- 一旦znode的数据被修改,zookeeper将通知各个客户端服务器;
- 统一集群管理
- 分布式环境中,实时掌握每个节点的状态是必要的
- 可根据节点实时状态做出一些调整
- zookeeper可以实现实时监控节点状态变化
- 可以将节点信息写入zookeeper上的znode
- 监听这个znode可以获取他的实时状态变化
- 分布式环境中,实时掌握每个节点的状态是必要的
- 服务器动态上下线:客户端能实时洞察到服务器上下线的变化
- 软负载均衡:在zookeeper中记录每台服务器的访问数,让访问数最少的服务器去处理最新的客户端请求
- 分布式环境下,配置文件同步非常常见
安装
- 从https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/或者apache官网https://zookeeper.apache.org/下载zookeeper的tar包;
- 安装jdk:下载解压tar包然后配置环境变量
JAVA_HOME=/opt/jdk1.8.0_211 PATH=$PATH:$JAVA_HOME/bin CLASSPATH=.:$JAVA_HOME/lib/ export PATH JAVA_HOME CLASSPATH
- 将zookeeper的tar包解压到opt目录下,然后将zoo_sample.conf改成zoo.conf(方便修改),然后修改配置中dataDir=/opt/zookeeper-3.4.14/zkData(将数据存储到该目录下,目录需要单独创建)
- 启动zookeeper服务:bin/zkServer.sh start,使用jps可以查看服务是否启动,使用bin/zkServer.sh status可以查看服务状态;
[root@localhost zookeeper-3.4.14]# jps 18227 QuorumPeerMain 19002 Jps root@localhost zookeeper-3.4.14]# bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /opt/zookeeper-3.4.14/bin/../conf/zoo.cfg Mode: standalone
- 启动客户端:bin/zkCli.sh即可
root@localhost zookeeper-3.4.14]# bin/zkCli.sh
Connecting to localhost:2181
2019-05-14 22:23:29,016 [myid:] - INFO [main:Environment@100] - Client environment:zookeeper.version=3.4.14-4c25d480e66aadd371de8bd2fd8da255ac140bcf, built on 03/06/2019 16:18 GMT
2019-05-14 22:23:29,024 [myid:] - INFO [main:Environment@100] - Client environment:host.name=localhost
2019-05-14 22:23:29,024 [myid:] - INFO [main:Environment@100] - Client environment:java.version=1.8.0_211
2019-05-14 22:23:29,026 [myid:] - INFO [main:Environment@100] - Client environment:java.vendor=Oracle Corporation
2019-05-14 22:23:29,027 [myid:] - INFO [main:Environment@100] - Client environment:java.home=/opt/jdk1.8.0_211/jre
2019-05-14 22:23:29,027 [myid:] - INFO [main:Environment@100] - Client environment:java.class.path=/opt/zookeeper-3.4.14/bin/../zookeeper-server/target/classes:/opt/zookeeper-3.4.14/bin/../build/classes:/opt/zookeeper-3.4.14/bin/../zookeeper-server/target/lib/*.jar:/opt/zookeeper-3.4.14/bin/../build/lib/*.jar:/opt/zookeeper-3.4.14/bin/../lib/slf4j-log4j12-1.7.25.jar:/opt/zookeeper-3.4.14/bin/../lib/slf4j-api-1.7.25.jar:/opt/zookeeper-3.4.14/bin/../lib/netty-3.10.6.Final.jar:/opt/zookeeper-3.4.14/bin/../lib/log4j-1.2.17.jar:/opt/zookeeper-3.4.14/bin/../lib/jline-0.9.94.jar:/opt/zookeeper-3.4.14/bin/../lib/audience-annotations-0.5.0.jar:/opt/zookeeper-3.4.14/bin/../zookeeper-3.4.14.jar:/opt/zookeeper-3.4.14/bin/../zookeeper-server/src/main/resources/lib/*.jar:/opt/zookeeper-3.4.14/bin/../conf:.:/opt/jdk1.8.0_211/lib/
2019-05-14 22:23:29,027 [myid:] - INFO [main:Environment@100] - Client environment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
2019-05-14 22:23:29,027 [myid:] - INFO [main:Environment@100] - Client environment:java.io.tmpdir=/tmp
2019-05-14 22:23:29,027 [myid:] - INFO [main:Environment@100] - Client environment:java.compiler=<NA>
2019-05-14 22:23:29,027 [myid:] - INFO [main:Environment@100] - Client environment:os.name=Linux
2019-05-14 22:23:29,027 [myid:] - INFO [main:Environment@100] - Client environment:os.arch=amd64
2019-05-14 22:23:29,027 [myid:] - INFO [main:Environment@100] - Client environment:os.version=3.10.0-957.10.1.el7.x86_64
2019-05-14 22:23:29,028 [myid:] - INFO [main:Environment@100] - Client environment:user.name=root
2019-05-14 22:23:29,028 [myid:] - INFO [main:Environment@100] - Client environment:user.home=/root
2019-05-14 22:23:29,028 [myid:] - INFO [main:Environment@100] - Client environment:user.dir=/opt/zookeeper-3.4.14
2019-05-14 22:23:29,029 [myid:] - INFO [main:ZooKeeper@442] - Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@446cdf90
Welcome to ZooKeeper!
2019-05-14 22:23:29,063 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1025] - Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
JLine support is enabled
2019-05-14 22:23:29,179 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@879] - Socket connection established to localhost/127.0.0.1:2181, initiating session
2019-05-14 22:23:29,189 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1299] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x1000010aee70002, negotiated timeout = 30000
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0]
- 退出客户端:quit
[zk: localhost:2181(CONNECTED) 5] quit
Quitting...
2019-05-14 22:25:20,703 [myid:] - INFO [main:ZooKeeper@693] - Session: 0x1000010aee70002 closed
2019-05-14 22:25:20,705 [myid:] - INFO [main-EventThread:ClientCnxn$EventThread@522] - EventThread shut down for session: 0x1000010aee70002
[root@localhost zookeeper-3.4.14]#
- 退出服务端 :bin/zkServer.sh stop
[root@localhost zookeeper-3.4.14]# bin/zkServer.sh stop
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper-3.4.14/bin/../conf/zoo.cfg
Stopping zookeeper ... STOPPED
[root@localhost zookeeper-3.4.14]#
- 配置参数详解
- tickTime =2000:通信心跳数,Zookeeper服务器与客户端心跳时间,单位毫秒
Zookeeper使用的基本时间,服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个tickTime时间就会发送一个心跳,时间单位为毫秒。
它用于心跳机制,并且设置最小的session超时时间为两倍心跳时间。(session的最小超时时间是2*tickTime) - initLimit =10:LF初始通信时限
集群中的Follower跟随者服务器与Leader领导者服务器之间初始连接时能容忍的最多心跳数(tickTime的数量),用它来限定集群中的Zookeeper服务器连接到Leader的时限。 - syncLimit =5:LF同步通信时限
集群中Leader与Follower之间的最大响应时间单位,假如响应超过syncLimit * tickTime,Leader认为Follwer死掉,从服务器列表中删除Follwer。 - dataDir:数据文件目录+数据持久化路径
主要用于保存Zookeeper中的数据。 - clientPort =2181:客户端连接端口
监听客户端连接的端口。
- tickTime =2000:通信心跳数,Zookeeper服务器与客户端心跳时间,单位毫秒
内部原理
选举机制
- 半数机制:集群中半数以上机器存活,集群可用。所以zookeeper适合安装奇数台服务器(5台服务器挂3台集群不可用,6台服务器挂3台也不可用,因为没有半数以上存活,所以依旧不可用,因此第六台作用没那么大,建议奇数台服务器);
- Zookeeper虽然在配置文件中并没有指定Master和Slave。但是,Zookeeper工作时,是有一个节点为Leader,其他则为Follower,Leader是通过内部的选举机制临时产生的(超过半数以上)。假设有五台服务器组成的Zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的。假设这些服务器依序启动,来看看会发生什么
- 服务器1启动,此时只有它一台服务器启动了,它发出去的报文没有任何响应,所以它的选举状态一直是LOOKING状态。
- 服务器2启动,它与最开始启动的服务器1进行通信,互相交换自己的选举结果,由于两者都没有历史数据,所以id值较大的服务器2胜出,但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是3),所以服务器1、2还是继续保持LOOKING状态。
- 服务器3启动,根据前面的理论分析,服务器3成为服务器1、2、3中的老大,而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的Leader。
- 服务器4启动,根据前面的分析,理论上服务器4应该是服务器1、2、3、4中最大的,但是由于前面已经有半数以上的服务器选举了服务器3,所以它只能接收当小弟的命了。
- 服务器5启动,同4一样当小弟。
节点类型
- 持久:客户端与服务端断开连接之后,创建的节点不删除
- 持久化目录节点:客户端与zookeeper断开连接之后,该节点依旧存在
- 持久化顺序编号目录节点:客户端与zookeeper断开连接之后,该节点依旧存在,知识zookeeper给该节点名称进行顺序编号。好处是:顺序号可以用于被所有事件进行全局排序,这样客户端可以通过顺序号推断时间的顺序;
- 短暂:客户端和服务器断开连接之后,创建的节点自己删除
- 临时目录节点:客户端zookeeper断开连接之后,该节点被删除(例如服务器动态上下线)
- 临时顺序编号目录节点:客户端与zookeeper断开连接之后,该节点被删除,只是zookeeper为该节点名称进行顺序编号;
stat 结构体
[zk: localhost:2181(CONNECTED) 39] get /huzd
huzd
cZxid = 0x27
ctime = Wed May 15 22:02:33 CST 2019
mZxid = 0x27
mtime = Wed May 15 22:02:33 CST 2019
pZxid = 0x27
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
- cZxid:创建节点的事务id,每个修改都有唯一的zxid,如果zxid1小于zxid2,那么zxid1在zxid2之前发生;
- ctime:被创建的时间
- mZxid:最后修改的事务zxid
- mtime:最后修改的时间
- pZxid:最后更新的子节点zxid
- cversion:子节点变化号,znode子节点修改的次数
- dataversion:数据变化号
- aclVesion:访问控制列表变化号
- ephemeralOwner:临时节点的拥有者的sessionid,如果不是临时节点,则为0
- dataLength:节点的数据长度
- numChildren:子节点数目
监听器的原理
原理详解
- 首先有一个main线程
- 在main线程里面创建zookeeper客户端,这时会创建两个线程,一个负责连接,一个负责监听
- 通过连接线程将注册的监听事件发送给zookeeper,包括客户端自身ip跟listener监听端口(订阅)
- zookeeper将注册的监听事件添加的注册监听器列表中
- zookeeper监听该事件是否发送变化,一旦发生变化通过之前注册的客户端ip跟listener端口将变化通知给客户端;
- 客户端监听器线程调用process方法处理变化;
常见的监听
- 监听节点数据的变化:get path watch
- 监听子节点增减的变化:ls path watch
写数据流程
- 客户端向zookeeper的server1上面发起一个写数据请求
- server1接受到请求,如果server1不是leader,则将请求转发给leader,leader在将请求广播给各个server(follower),各个server将写请求执行成功之后会返回通知leader
- 当leader收到一半以上的写成功之后就认为写成功了,然后通知最开始的server1写请求成功
- server1进一步通知客户端写请求成功;
实战
客户端命令行操作
命令行基本语法 | 功能描述 |
---|---|
help | 显示所有操作命令 |
ls path [watch] | 使用ls命令来查看当前znode中所包含的内容 |
ls2 path [watch] | 查看当前节点数据并能看到详细信息 |
create | 普通创建 -s:含有序列 -e:临时(重启或者超时消失) |
get path [watch] | 获得节点的值 |
set | 设置节点的具体值 |
stat | 查看节点状态 |
delete | 删除节点 |
rmr | 递归删除节点 |
- help:显示所有操作命令
[zk: localhost:2181(CONNECTED) 11] help
ZooKeeper -server host:port cmd args
stat path [watch]
set path data [version]
ls path [watch]
delquota [-n|-b] path
ls2 path [watch]
setAcl path acl
setquota -n|-b val path
history
redo cmdno
printwatches on|off
delete path [version]
sync path
listquota path
rmr path
get path [watch]
create [-s] [-e] path data acl
addauth scheme auth
quit
getAcl path
close
connect host:port
[zk: localhost:2181(CONNECTED) 12]
- ls:显示路径下内容,跟linux略微不同的是,末尾不能有/
[zk: localhost:2181(CONNECTED) 12] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 13] ls /zookeeper
[quota]
[zk: localhost:2181(CONNECTED) 14] ls /zookeeper/quota/
Command failed: java.lang.IllegalArgumentException: Path must not end with / character
[zk: localhost:2181(CONNECTED) 15]
- ls2:显示路径下内容详细信息
[zk: localhost:2181(CONNECTED) 15] ls2 /zookeeper
[quota]
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
[zk: localhost:2181(CONNECTED) 16]
- create:创建节点
[zk: localhost:2181(CONNECTED) 16] create /huzd "huzd"
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/
Created /huzd
[zk: localhost:2181(CONNECTED) 17] ls /
[zookeeper, huzd]
[zk: localhost:2181(CONNECTED) 18]
- get:获取节点值
[zk: localhost:2181(CONNECTED) 2] get /huzd
huzd
cZxid = 0x8
ctime = Wed May 15 20:53:21 CST 2019
mZxid = 0x8
mtime = Wed May 15 20:53:21 CST 2019
pZxid = 0x8
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
[zk: localhost:2181(CONNECTED) 3]
- 创建短暂节点(Ephemeral):create -e /huzd/test test
[zk: localhost:2181(CONNECTED) 15] create -e /huzd/test test
Created /huzd/test
[zk: localhost:2181(CONNECTED) 20] ls /huzd
[test]
[zk: localhost:2181(CONNECTED) 21] quit
重新连接
[zk: localhost:2181(CONNECTED) 0] ls /huzd
[]
[zk: localhost:2181(CONNECTED) 1]
- 创建带有序号的节点
[zk: localhost:2181(CONNECTED) 0] create -s /huzd/test test
Created /huzd/test0000000003
[zk: localhost:2181(CONNECTED) 1] create -s /huzd/test test
Created /huzd/test0000000004
[zk: localhost:2181(CONNECTED) 2] create -s /huzd/test test
Created /huzd/test0000000005
[zk: localhost:2181(CONNECTED) 3]
- 修改节点数据 set
[zk: localhost:2181(CONNECTED) 14] create /huzd huzd
Created /huzd
[zk: localhost:2181(CONNECTED) 15] get /huzd
huzd
cZxid = 0x1e
ctime = Wed May 15 21:40:27 CST 2019
mZxid = 0x1e
mtime = Wed May 15 21:40:27 CST 2019
pZxid = 0x1e
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
[zk: localhost:2181(CONNECTED) 16] set /huzd test
cZxid = 0x1e
ctime = Wed May 15 21:40:27 CST 2019
mZxid = 0x1f
mtime = Wed May 15 21:40:52 CST 2019
pZxid = 0x1e
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
[zk: localhost:2181(CONNECTED) 17] get /huzd
test
cZxid = 0x1e
ctime = Wed May 15 21:40:27 CST 2019
mZxid = 0x1f
mtime = Wed May 15 21:40:52 CST 2019
pZxid = 0x1e
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
[zk: localhost:2181(CONNECTED) 18]
- 查看节点状态
[zk: localhost:2181(CONNECTED) 18] stat /huzd
cZxid = 0x1e
ctime = Wed May 15 21:40:27 CST 2019
mZxid = 0x1f
mtime = Wed May 15 21:40:52 CST 2019
pZxid = 0x1e
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
[zk: localhost:2181(CONNECTED) 19]
- 在get或者set命令后面加上watch可以表示监听该节点变化,一旦有变化,会有通知(但是只有一次有效,通知完之后如果需要继续监听需要在watch)
[zk: localhost:2181(CONNECTED) 20] get /huzd watch
test
cZxid = 0x1e
ctime = Wed May 15 21:40:27 CST 2019
mZxid = 0x1f
mtime = Wed May 15 21:40:52 CST 2019
pZxid = 0x1e
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
[zk: localhost:2181(CONNECTED) 21] set /huzd huzd
cZxid = 0x1e
注意下面这里!!
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/huzd
ctime = Wed May 15 21:40:27 CST 2019
mZxid = 0x20
mtime = Wed May 15 21:48:11 CST 2019
pZxid = 0x1e
cversion = 0
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
[zk: localhost:2181(CONNECTED) 22]
- 删除节点
[zk: localhost:2181(CONNECTED) 27] ls /huzd
[huzd]
[zk: localhost:2181(CONNECTED) 28] ls /huzd/huzd
[]
[zk: localhost:2181(CONNECTED) 29] delete /huzd
Node not empty: /huzd
[zk: localhost:2181(CONNECTED) 30] delete /huzd/huzd
[zk: localhost:2181(CONNECTED) 31] ls /huzd
[]
[zk: localhost:2181(CONNECTED) 32] create /huzd/huzd huzd
Created /huzd/huzd
[zk: localhost:2181(CONNECTED) 33] ls /huzd
[huzd]
[zk: localhost:2181(CONNECTED) 34] rmr /huzd
[zk: localhost:2181(CONNECTED) 35]
API应用
- 创建Maven工程
- 引入zookeeper的依赖,同时还需要junit(我这边放到父pom文件中)跟log4j-core;
parent.pom
<dependencyManagement>
<dependencies>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</dependencyManagement>
ZookeeperTest.pom
<parent>
<groupId>com.huzd.study</groupId>
<artifactId>Parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../Parent/pom.xml</relativePath>
</parent>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
</dependency>
</dependencies>
- 关闭虚拟机防火墙或者设置允许的端口
关于centos7的防火墙命令- 启动防火墙:systemctl start firewalld
- 查看防火墙状态:systemctl status firewalld 或者 firewall-cmd --state
- 显示防火墙public区域允许的端口号:firewall-cmd --zone=public --list-ports
- 增加端口到指定区域:firewall-cmd --zone=public --add-port=2181/tcp
- 删除端口:firewall-cmd --zone= public --remove-port=80/tcp --permanent
- 重启服务:firewall-cmd --reload
- 停止防火墙:systemctl disable firewalld
- 禁用防火墙:systemctl stop firewalld
- 在Maven工程的src/main/resources路径下配置log4j.properties
log4j.rootLogger=INFO,CON
#CON
log4j.appender.CON=org.apache.log4j.ConsoleAppender
log4j.appender.CON.layout=org.apache.log4j.PatternLayout
log4j.appender.CON.layout.ConversionPattern=threadId[%X{thId}] logId[%X{bizId}] FunctionId[%X{FunctionId}] %d %p[%C:%L]- %m%n
- 使用api连接
public class TestZk {
private String connectString = "192.168.175.128:2181";
private int sessionTimeout = 2000;
private ZooKeeper zooKeeper = null;
private CountDownLatch countDownLatch = new CountDownLatch(1);
@Before
public void init() throws IOException, InterruptedException {
zooKeeper = new ZooKeeper(connectString, sessionTimeout , new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == KeeperState.SyncConnected) {
System.out.println("连接成功");
countDownLatch.countDown();
}
}
});
if(ZooKeeper.States.CONNECTING.equals(zooKeeper.getState())) {
System.out.println("连接中");
countDownLatch.await();
}
}
@Test
public void createNode() {
try {
String create = zooKeeper.create("/huzd", "huzd".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(create);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Test
public void getDataAndWatch() {
try {
List<String> children = zooKeeper.getChildren("/", false);
for (String child : children) {
System.out.println(child);
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 需要注意的是由于Zookeeper是异步连接,因此如果调用了连接立刻用create方法会报错org.apache.zookeeper.KeeperException$ConnectionLossException,这是因为连接还没有连接上就调用了create,解决办法是配合watch跟CountDownLatch,主方法调用create之前判断zookeeper对象的状态是否是连接中,是的话就调用countDownLatch.await(),让主线程处于等待状态,在连接成功之后watch的even状态会返回KeeperState.SyncConnected,这时候调用countDownLatch.countDown(),通知主线程继续执行,这里也就是执行create;
- 创建节点成功
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,637 INFO[org.apache.zookeeper.Environment:100]- Client environment:zookeeper.version=3.4.14-4c25d480e66aadd371de8bd2fd8da255ac140bcf, built on 03/06/2019 16:18 GMT
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,641 INFO[org.apache.zookeeper.Environment:100]- Client environment:host.name=home-huzd
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,641 INFO[org.apache.zookeeper.Environment:100]- Client environment:java.version=1.8.0_151
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,641 INFO[org.apache.zookeeper.Environment:100]- Client environment:java.vendor=Oracle Corporation
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,641 INFO[org.apache.zookeeper.Environment:100]- Client environment:java.home=H:\software\jdk1.8\jre
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,641 INFO[org.apache.zookeeper.Environment:100]- Client environment:java.class.path=H:\software\newEclipseWork\ZookeeperTest\target\test-classes;H:\software\newEclipseWork\ZookeeperTest\target\classes;H:\software\apache-maven-3.6.1\repository\org\apache\zookeeper\zookeeper\3.4.14\zookeeper-3.4.14.jar;H:\software\apache-maven-3.6.1\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;H:\software\apache-maven-3.6.1\repository\org\slf4j\slf4j-log4j12\1.7.25\slf4j-log4j12-1.7.25.jar;H:\software\apache-maven-3.6.1\repository\com\github\spotbugs\spotbugs-annotations\3.1.9\spotbugs-annotations-3.1.9.jar;H:\software\apache-maven-3.6.1\repository\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;H:\software\apache-maven-3.6.1\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;H:\software\apache-maven-3.6.1\repository\jline\jline\0.9.94\jline-0.9.94.jar;H:\software\apache-maven-3.6.1\repository\org\apache\yetus\audience-annotations\0.5.0\audience-annotations-0.5.0.jar;H:\software\apache-maven-3.6.1\repository\io\netty\netty\3.10.6.Final\netty-3.10.6.Final.jar;H:\software\apache-maven-3.6.1\repository\junit\junit\4.12\junit-4.12.jar;H:\software\apache-maven-3.6.1\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;H:\software\apache-maven-3.6.1\repository\org\apache\logging\log4j\log4j-core\2.11.1\log4j-core-2.11.1.jar;H:\software\apache-maven-3.6.1\repository\org\apache\logging\log4j\log4j-api\2.11.1\log4j-api-2.11.1.jar;H:\software\eclipse\configuration\org.eclipse.osgi\250\0\.cp;H:\software\eclipse\configuration\org.eclipse.osgi\249\0\.cp
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,642 INFO[org.apache.zookeeper.Environment:100]- Client environment:java.library.path=H:\software\jdk1.8\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;H:/software/jdk1.8/bin/../jre/bin/server;H:/software/jdk1.8/bin/../jre/bin;H:/software/jdk1.8/bin/../jre/lib/amd64;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\IDM Computer Solutions\UltraEdit;C:\Users\lenovo\AppData\Local\Microsoft\WindowsApps;H:\software\jdk1.8\bin;H:\software\jdk1.8\jre\bin;H:\software\work\apache-tomcat-6.0.44-windows-x64\apache-tomcat-6.0.44\bin;H:\software\work\apache-tomcat-6.0.44-windows-x64\apache-tomcat-6.0.44\lib;H:\software\apache-maven-3.6.1\bin;C:\Users\lenovo\AppData\Local\Microsoft\WindowsApps;;H:\software\eclipse;;.
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,642 INFO[org.apache.zookeeper.Environment:100]- Client environment:java.io.tmpdir=C:\Users\lenovo\AppData\Local\Temp\
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,642 INFO[org.apache.zookeeper.Environment:100]- Client environment:java.compiler=<NA>
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,642 INFO[org.apache.zookeeper.Environment:100]- Client environment:os.name=Windows 10
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,642 INFO[org.apache.zookeeper.Environment:100]- Client environment:os.arch=amd64
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,643 INFO[org.apache.zookeeper.Environment:100]- Client environment:os.version=10.0
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,643 INFO[org.apache.zookeeper.Environment:100]- Client environment:user.name=lenovo
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,643 INFO[org.apache.zookeeper.Environment:100]- Client environment:user.home=C:\Users\lenovo
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,643 INFO[org.apache.zookeeper.Environment:100]- Client environment:user.dir=H:\software\newEclipseWork\ZookeeperTest
threadId[] logId[] FunctionId[] 2019-05-17 22:05:41,645 INFO[org.apache.zookeeper.ZooKeeper:442]- Initiating client connection, connectString=192.168.175.128:2181 sessionTimeout=2000 watcher=com.huzd.study.TestZk$1@3d04a311
连接中
threadId[] logId[] FunctionId[] 2019-05-17 22:06:00,114 INFO[org.apache.zookeeper.ClientCnxn$SendThread:1025]- Opening socket connection to server 192.168.175.128/192.168.175.128:2181. Will not attempt to authenticate using SASL (unknown error)
threadId[] logId[] FunctionId[] 2019-05-17 22:06:00,116 INFO[org.apache.zookeeper.ClientCnxn$SendThread:879]- Socket connection established to 192.168.175.128/192.168.175.128:2181, initiating session
threadId[] logId[] FunctionId[] 2019-05-17 22:06:00,123 INFO[org.apache.zookeeper.ClientCnxn$SendThread:1299]- Session establishment complete on server 192.168.175.128/192.168.175.128:2181, sessionid = 0x100003429990009, negotiated timeout = 4000
连接成功
/huzd
- 获取节点并监控数据变化
@Before
public void init() throws IOException, InterruptedException {
zooKeeper = new ZooKeeper(connectString, sessionTimeout , new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == KeeperState.SyncConnected) {
System.out.println("连接成功");
countDownLatch.countDown();
}
//监控必须在watch中才能生效,首先使用zooKeeper.getChildren方法,然后监控设置为true
//然后执行getDataAndWatch方法,这里会一直阻塞主线程,让主线程不停止,然后当zookeeper的/根路径有节点变化时,会回调process方法打印变化
try {
List<String> children = zooKeeper.getChildren("/", true);
for (String child : children) {
System.out.println(child);
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
if(ZooKeeper.States.CONNECTING.equals(zooKeeper.getState())) {
System.out.println("连接中");
countDownLatch.await();
}
}
@Test
public void getDataAndWatch() {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}