centOS7.0上安装使用zookeeper环境

来源:互联网 发布:javabean连接数据库 编辑:程序博客网 时间:2024/05/17 09:06

一、安装jdk

首先zookeeper是java编写的,需要安装jdk,方法有3种,(以下操作来自:http://www.linuxidc.com/Linux/2016-09/134941.htm)


方法一:手动解压JDK的压缩包,然后设置环境变量

1.在/usr/目录下创建java目录

[root@localhost ~]# mkdir/usr/java
[root@localhost ~]# cd /usr/java

2.下载jdk,然后解压

[root@localhost java]# curl -O http://download.Oracle.com/otn-pub/java/jdk/7u79-b15/jdk-7u79-linux-x64.tar.gz 
[root@localhost java]# tar -zxvf jdk-7u79-linux-x64.tar.gz

3.设置环境变量

[root@localhost java]# vi /etc/profile

在profile中添加如下内容:

#set java environment
JAVA_HOME=/usr/java/jdk1.7.0_79
JRE_HOME=/usr/java/jdk1.7.0_79/jre
CLASS_PATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
export JAVA_HOME JRE_HOME CLASS_PATH PATH

让修改生效:

[root@localhost java]# source /etc/profile

4.验证JDK有效性

[root@localhost java]# java -version
java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)

方法二:用yum安装JDK

1.查看yum库中都有哪些jdk版本(暂时只发现了openjdk)

[root@localhost ~]# yum search java|grep jdk
ldapjdk-javadoc.x86_64 : Javadoc for ldapjdk
java-1.6.0-openjdk.x86_64 : OpenJDK Runtime Environment
java-1.6.0-openjdk-demo.x86_64 : OpenJDK Demos
java-1.6.0-openjdk-devel.x86_64 : OpenJDK Development Environment
java-1.6.0-openjdk-javadoc.x86_64 : OpenJDK API Documentation
java-1.6.0-openjdk-src.x86_64 : OpenJDK Source Bundle
java-1.7.0-openjdk.x86_64 : OpenJDK Runtime Environment
java-1.7.0-openjdk-demo.x86_64 : OpenJDK Demos
java-1.7.0-openjdk-devel.x86_64 : OpenJDK Development Environment
java-1.7.0-openjdk-javadoc.noarch : OpenJDK API Documentation
java-1.7.0-openjdk-src.x86_64 : OpenJDK Source Bundle
java-1.8.0-openjdk.x86_64 : OpenJDK Runtime Environment
java-1.8.0-openjdk-demo.x86_64 : OpenJDK Demos
java-1.8.0-openjdk-devel.x86_64 : OpenJDK Development Environment
java-1.8.0-openjdk-headless.x86_64 : OpenJDK Runtime Environment
java-1.8.0-openjdk-javadoc.noarch : OpenJDK API Documentation
java-1.8.0-openjdk-src.x86_64 : OpenJDK Source Bundle
ldapjdk.x86_64 : The Mozilla LDAP Java SDK

2.选择版本,进行安装

//选择1.7版本进行安装
[root@localhost ~]# yum install java-1.7.0-openjdk
//安装完之后,默认的安装目录是在: /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.75.x86_64

3.设置环境变量

[root@localhost ~]# vi /etc/profile

在profile文件中添加如下内容

#set java environment
JAVA_HOME=/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.75.x86_64
JRE_HOME=$JAVA_HOME/jre
CLASS_PATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
export JAVA_HOME JRE_HOME CLASS_PATH PATH

让修改生效

[root@localhost java]# source /etc/profile

4.验证(同上一方法)

方法三:用rpm安装JDK

1.下载rpm安装文件

[root@localhost ~]$ curl -O http://download.oracle.com/otn-pub/java/jdk/7u79-b15/jdk-7u79-linux-x64.rpm

2.使用rpm命令安装

[root@localhost  ~]# rpm -ivh jdk-7u79-linux-x64.rpm

3.设置环境变量

[root@localhost java]# vi /etc/profile

在打开的profile文件中添加如下内容

#set java environment
JAVA_HOME=/usr/java/jdk1.7.0_79
JRE_HOME=/usr/java/jdk1.7.0_79/jre
CLASS_PATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
export JAVA_HOME JRE_HOME CLASS_PATH PATH

让修改生效

[root@localhost java]# source /etc/profile

4.验证(同上一方法)

注:和yum安装类似,不用设置环境变量就可以运行java命令。rpm安装方式默认会把jdk安装到/usr/java/jdk1.7.0_79,然后通过三层链接,链接到/usr/bin,具体链接如下:

[root@localhost ~]# cd /bin
[root@localhost bin]# ll|grep java
lrwxrwxrwx. 1 root root    25 Mar 28 11:24 jar ->/usr/java/default/bin/jar
lrwxrwxrwx. 1 root root    26 Mar 28 11:24 java -> /usr/java/default/bin/java
lrwxrwxrwx. 1 root root    27 Mar 28 11:24 javac ->/usr/java/default/bin/javac
lrwxrwxrwx. 1 root root    29 Mar 28 11:24 javadoc ->/usr/java/default/bin/javadoc
lrwxrwxrwx. 1 root root    28 Mar 28 11:24 javaws ->/usr/java/default/bin/javaws
lrwxrwxrwx. 1 root root    30 Mar 28 11:24 jcontrol ->/usr/java/default/bin/jcontrol
[root@localhost bin]# cd /usr/java/
[root@localhost java]# ll
total 4
lrwxrwxrwx. 1 root root  16 Mar 28 11:24 default-> /usr/java/latest
drwxr-xr-x. 8 root root 4096 Mar 28 11:24 jdk1.7.0_79
lrwxrwxrwx. 1 root root  21 Mar 28 11:24 latest -> /usr/java/jdk1.7.0_79

方法四:Ubuntu 上使用apt-get安装JDK

1.查看apt库都有哪些jdk版本

root@linuxidc:~# apt-cache search java|grep jdk
default-jdk - Standard Java or Java compatible Development Kit
default-jdk-doc - Standard Java or Java compatible Development Kit (documentation)
gcj-4.6-jdk - gcj and classpath development tools for Java(TM)
gcj-jdk - gcj and classpath development tools for Java(TM)
openjdk-6-dbg - Java runtime based on OpenJDK (debugging symbols)
openjdk-6-demo - Java runtime based on OpenJDK (demos and examples)
openjdk-6-doc - OpenJDK Development Kit (JDK) documentation
openjdk-6-jdk - OpenJDK Development Kit (JDK)
openjdk-6-jre-lib - OpenJDK Java runtime (architecture independent libraries)
openjdk-6-source - OpenJDK Development Kit (JDK) source files
openjdk-7-dbg - Java runtime based on OpenJDK (debugging symbols)
openjdk-7-demo - Java runtime based on OpenJDK (demos and examples)
openjdk-7-doc - OpenJDK Development Kit (JDK) documentation
openjdk-7-jdk - OpenJDK Development Kit (JDK)
openjdk-7-source - OpenJDK Development Kit (JDK) source files
uwsgi-plugin-jvm-openjdk-6 - Java plugin for uWSGI (OpenJDK 6)
uwsgi-plugin-jwsgi-openjdk-6 - JWSGI plugin for uWSGI (OpenJDK 6)
openjdk-6-jre - OpenJDK Java runtime, using Hotspot JIT
openjdk-6-jre-headless - OpenJDK Java runtime, using Hotspot JIT (headless)
openjdk-7-jre - OpenJDK Java runtime, using Hotspot JIT
openjdk-7-jre-headless - OpenJDK Java runtime, using Hotspot JIT (headless)
openjdk-7-jre-lib - OpenJDK Java runtime (architecture independent libraries)

2.选择版本进行安装

root@linuxidc:~# apt-get install openjdk-7-jdk

3.设置环境变量

root@linuxidc:~# vi /etc/profile

在打开的profile文件中添加如下内容

#set java environment
JAVA_HOME=/usr/lib/jvm/java-1.7.0-openjdk-amd64
JRE_HOME=$JAVA_HOME/jre
CLASS_PATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
export JAVA_HOME JRE_HOME CLASS_PATH PATH

让修改生效

root@linuxidc:~# source /etc/profile

4.验证(同上一方法)

二、安装zookeeper

zookeeper有3种使用方法:集群模式、伪集群模式、单机模式。以下以伪集群模式为例(来自:http://blog.csdn.net/catoop/article/details/50848555)

集群部署

1. 下载

官网:http://zookeeper.apache.org/releases.html 
下载:zookeeper-3.4.8.tar.gz

2. 安装

因为资源有限,所以我在同一个服务器上面创建3个目录 server1、server2、server3 来模拟3台服务器集群。 
cd server1 
tar -zxvf zookeeper-3.4.8.tar.gz 
mkdir data 
mkdir dataLog 
data 为数据目录,dataLog 为日志目录。

3. 配置

cd zookeeper-3.4.8/conf 
创建文件 zoo.cfg,内容如下:

tickTime=2000 initLimit=5syncLimit=2dataDir=/opt/zookeeper/server1/datadataLogDir=/opt/zookeeper/server1/dataLogclientPort=2181server.1=127.0.0.1:2888:3888server.2=127.0.0.1:2889:3889server.3=127.0.0.1:2890:3890
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

tickTime:zookeeper中使用的基本时间单位, 毫秒值。 
initLimit:这个配置项是用来配置 Zookeeper 接受客户端(这里所说的客户端不是用户连接 Zookeeper 服务器的客户端,而是 Zookeeper 服务器集群中连接到 Leader 的 Follower 服务器)初始化连接时最长能忍受多少个 tickTime 时间间隔数。这里设置为5表名最长容忍时间为 5 * 2000 = 10 秒。 
syncLimit:这个配置标识 Leader 与 Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是 2 * 2000 = 4 秒。 
dataDir 和 dataLogDir 看配置就知道干吗的了,不用解释。 
clientPort:监听client连接的端口号,这里说的client就是连接到Zookeeper的代码程序。 
server.{myid}={ip}:{leader服务器交换信息的端口}:{当leader服务器挂了后, 选举leader的端口} 
maxClientCnxns:对于一个客户端的连接数限制,默认是60,这在大部分时候是足够了。但是在我们实际使用中发现,在测试环境经常超过这个数,经过调查发现有的团队将几十个应用全部部署到一台机器上,以方便测试,于是这个数字就超过了。

修改zoo.cfg非常简单,然后还需要创建myid文件: 
cd server1/data
echo 1 > myid

然后拷贝server1为server2和server3,并修改其中的zoo.cfg配置,当然也要修改myid的内容为2和3。

下面给出3个server的zoo.cfg 内容:

# server1tickTime=2000initLimit=5syncLimit=2dataDir=/opt/zookeeper/server1/datadataLogDir=/opt/zookeeper/server1/dataLogclientPort=2181server.1=127.0.0.1:2888:3888server.2=127.0.0.1:2889:3889server.3=127.0.0.1:2890:3890
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
# server2tickTime=2000initLimit=5syncLimit=2dataDir=/opt/zookeeper/server2/datadataLogDir=/opt/zookeeper/server2/dataLogclientPort=2182server.1=127.0.0.1:2888:3888server.2=127.0.0.1:2889:3889server.3=127.0.0.1:2890:3890
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
# server3tickTime=2000initLimit=5syncLimit=2dataDir=/opt/zookeeper/server3/datadataLogDir=/opt/zookeeper/server3/dataLogclientPort=2183server.1=127.0.0.1:2888:3888server.2=127.0.0.1:2889:3889server.3=127.0.0.1:2890:3890
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这里做下说明:因为我们是在同一个机器上模拟的集群,所以要注意server端口号和clientPort不要重复了,不然会出现端口冲突。所以,如果我们是3个不同的机器上做的3个server,那么我们的zoo.cfg配置都是一样的(注意server.{myid}=后面的IP地址使用具体的IP地址,如192.168.0.88)。还有就是,每一个server的myid内容都不能一样,这也可以理解为不同server的标识。

4. 启动

进入 zookeeper-3.4.8/bin 目录,使用 ./zkServer.sh start 启动zk服务。(你也可以使用 ./zkServer.sh start myzoo.cfg 指定配置文件启动,这在自动化运维的时候很有用) 
使用 tail -f zookeeper.out 查看日志。 
要说的是:在启动第一个的时候,日志中会出现一堆错误,仔细一看就能明白,是因为另外2个server还没有启动它连接不上的错误。然后当我们启动第二个server的时候,日志中的错误将会减少。最后我们把所有server都启动起来后,日志中便没有错误了。

5. 测试

随便进入一个zk目录,连接一个server测试。 
cd zookeeper-3.4.8/bin 
./zkCli.sh -server 127.0.0.1:2181 
如果你要连接别的服务器,请指定具体的IP地址。

几个基本命令说明: 
ls 查看指定节点中包含的子节点(如:ls / 或 ls /app1/server1) 
create 创建节点并赋值 
get 读取节点内容 
set 改变节点内容 
delete 删除节点 
注意zk中所有节点都基于路径确定,如你要删除 /app1/server1/nodeA 的命令为: 
delete /app1/server1/nodeA

下面是基本操作截图: 
这里写图片描述

Java程序Demo

创建一个Maven工程 
打开pom.xml文件添加zookeeper依赖

        <dependency>            <groupId>org.apache.zookeeper</groupId>            <artifactId>zookeeper</artifactId>            <version>3.4.6</version>        </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

创建Demo.java,代码如下:

package com.shanhy.demo.zookeeper;import java.io.IOException;import org.apache.zookeeper.CreateMode;import org.apache.zookeeper.KeeperException;import org.apache.zookeeper.WatchedEvent;import org.apache.zookeeper.Watcher;import org.apache.zookeeper.ZooDefs.Ids;import org.apache.zookeeper.ZooKeeper;/** * Zookeeper测试 * * @create    2016年3月10日 */public class Test {    // 会话超时时间,设置为与系统默认时间一致    private static final int SESSION_TIMEOUT = 30 * 1000;    // 创建 ZooKeeper 实例    private ZooKeeper zk;    // 创建 Watcher 实例    private Watcher wh = new Watcher() {        /**         * Watched事件         */        public void process(WatchedEvent event) {            System.out.println("WatchedEvent >>> " + event.toString());        }    };    // 初始化 ZooKeeper 实例    private void createZKInstance() throws IOException {        // 连接到ZK服务,多个可以用逗号分割写        zk = new ZooKeeper("192.168.19.130:2181,192.168.19.130:2182,192.168.19.130:2183", Test.SESSION_TIMEOUT, this.wh);    }    private void ZKOperations() throws IOException, InterruptedException, KeeperException {        System.out.println("\n1. 创建 ZooKeeper 节点 (znode : zoo2, 数据: myData2 ,权限: OPEN_ACL_UNSAFE ,节点类型: Persistent");        zk.create("/zoo2", "myData2".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);        System.out.println("\n2. 查看是否创建成功: ");        System.out.println(new String(zk.getData("/zoo2", this.wh, null)));// 添加Watch        // 前面一行我们添加了对/zoo2节点的监视,所以这里对/zoo2进行修改的时候,会触发Watch事件。        System.out.println("\n3. 修改节点数据 ");        zk.setData("/zoo2", "shanhy20160310".getBytes(), -1);        // 这里再次进行修改,则不会触发Watch事件,这就是我们验证ZK的一个特性“一次性触发”,也就是说设置一次监视,只会对下次操作起一次作用。        System.out.println("\n3-1. 再次修改节点数据 ");        zk.setData("/zoo2", "shanhy20160310-ABCD".getBytes(), -1);        System.out.println("\n4. 查看是否修改成功: ");        System.out.println(new String(zk.getData("/zoo2", false, null)));        System.out.println("\n5. 删除节点 ");        zk.delete("/zoo2", -1);        System.out.println("\n6. 查看节点是否被删除: ");        System.out.println(" 节点状态: [" + zk.exists("/zoo2", false) + "]");    }    private void ZKClose() throws InterruptedException {        zk.close();    }    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {        Test dm = new Test();        dm.createZKInstance();        dm.ZKOperations();        dm.ZKClose();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

我想代码不用解释了,该注释的里面都注释了。


下面有一种特殊的情况的处理思路: 
有server1、server2、server3这三个服务,在client去连接zk的时候,指向server1初始化的过程中是没有问题的,然而刚刚初始化完成,准备去连接server1的时候,server1因为网络等原因挂掉了。 
然而对client来说,它会拿server1的配置去请求连接,这时肯定会报连接被拒绝的异常以致启动退出。 
所以优雅的解决这个问题的方法思路就是“在连接的时候判断连接状态,如果未连接成功,程序自动使用其他连接去请求连接”,这样来避开这种罕见的异常问题。

代码如下:

    // 初始化 ZooKeeper 实例    private void createZKInstance() throws IOException {        // 连接到ZK服务,多个可以用逗号分割写        zk = new ZooKeeper("192.168.19.130:2181,192.168.19.130:2182,192.168.19.130:2183", Test.SESSION_TIMEOUT, this.wh);        if(!zk.getState().equals(States.CONNECTED)){            while(true){                if(zk.getState().equals(States.CONNECTED)){                    break;                }                try {                    TimeUnit.SECONDS.sleep(5);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

上面的代码是基于zk提供的库的API来你使用的,为了更易于使用,有人写了开源的zkclient,我们可以直接使用它来操作zk。 
zkclient 开源地址:https://github.com/sgroschupf/zkclient 
maven 依赖配置:

        <!--zkclient -->        <dependency>            <groupId>com.101tec</groupId>            <artifactId>zkclient</artifactId>            <version>0.7</version>        </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

zkClient 针对 zk 的一次性watcher,做了重新封装,然后定义了 stateChanged、znodeChanged、dataChanged 三种监听器。

  1. 监听children变化
  2. 监听节点数据变化
  3. 监听连接状态变化

代码如下:

package com.shanhy.demo.zookeeper;import java.util.List;import java.util.concurrent.TimeUnit;import org.I0Itec.zkclient.DataUpdater;import org.I0Itec.zkclient.IZkChildListener;import org.I0Itec.zkclient.IZkDataListener;import org.I0Itec.zkclient.IZkStateListener;import org.I0Itec.zkclient.ZkClient;import org.apache.zookeeper.Watcher.Event.KeeperState;/** * ZkClient的使用测试 * * @author   单红宇(365384722) * @myblog  http://blog.csdn.net/catoop/ * @create    2016年3月11日 */public class ZkClientTest {    public static void main(String[] args) {        ZkClient zkClient = new ZkClient("192.168.19.130:2181,192.168.19.130:2182,192.168.19.130:2183");        String node = "/myapp";        // 订阅监听事件        childChangesListener(zkClient, node);        dataChangesListener(zkClient, node);        stateChangesListener(zkClient);        if (!zkClient.exists(node)) {            zkClient.createPersistent(node, "hello zookeeper");        }        System.out.println(zkClient.readData(node));        zkClient.updateDataSerialized(node, new DataUpdater<String>() {            public String update(String currentData) {                return currentData + "-123";            }        });        System.out.println(zkClient.readData(node));        try {            TimeUnit.SECONDS.sleep(3);        } catch (InterruptedException e) {            e.printStackTrace();        }    }    /**     * 订阅children变化     *     * @param zkClient     * @param path     * @author SHANHY     * @create  2016年3月11日     */    public static void childChangesListener(ZkClient zkClient, final String path) {        zkClient.subscribeChildChanges(path, new IZkChildListener() {            public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {                System.out.println("clildren of path " + parentPath + ":" + currentChilds);            }        });    }    /**     * 订阅节点数据变化     *     * @param zkClient     * @param path     * @author SHANHY     * @create  2016年3月11日     */    public static void dataChangesListener(ZkClient zkClient, final String path){        zkClient.subscribeDataChanges(path, new IZkDataListener(){            public void handleDataChange(String dataPath, Object data) throws Exception {                System.out.println("Data of " + dataPath + " has changed.");            }            public void handleDataDeleted(String dataPath) throws Exception {                System.out.println("Data of " + dataPath + " has changed.");            }        });    }    /**     * 订阅状态变化     *     * @param zkClient     * @author SHANHY     * @create  2016年3月11日     */    public static void stateChangesListener(ZkClient zkClient){        zkClient.subscribeStateChanges(new IZkStateListener() {            public void handleStateChanged(KeeperState state) throws Exception {                System.out.println("handleStateChanged");            }            public void handleSessionEstablishmentError(Throwable error) throws Exception {                System.out.println("handleSessionEstablishmentError");            }            public void handleNewSession() throws Exception {                System.out.println("handleNewSession");            }        });    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116

ZkClient 做了便捷的包装,对Watch做了增强处理。 
subscribeChildChanges实际上是通过exists和getChildren关注了两个事件。这样当create(“/path”)时,对应path上通过getChildren注册的listener也会被调用。另外subscribeDataChanges实际上只是通过exists注册了事件。因为从上表可以看到,对于一个更新,通过exists和getData注册的watcher要么都会触发,要么都不会触发。

关于session超时的问题,ZkClient 貌似还是有对 Session Expired 处理的,在ZkClient.processStateChanged方法中。虽然能重新连接,但是连接上是一个新的 session,原有创建的ephemeral znode和watch会被删除,程序上你可能需要处理这个问题。


最后说几点关于ZkClient的注意事项: 
1. 创建节点的时候一定要先判断节点是否存在,如果直接使用zkclient创建一个已经存在的节点,则会抛出异常。 
2. 使用zkclient创建节点的时候,path描述的路径,预新增的最终节点之前的所有父节点都必须要存在,否则会抛出异常。所以根据自己需要做好处理。