前期准备
1.VmWare , CentOS-7 ,Linux下JDK ,Zookeeper安装文件(https://zookeeper.apache.org/releases.html),xshell
在VmWare中安装3台CentOS虚拟机,ip分别为192.168.200.129;192.168.200.130;192.168.200.131,设置虚拟机网络模式为NAT,可以和物理主机通信。
安装jdk
使用xshell连接三台虚拟机,将zookeeper文件上传到虚拟机上 ,并解压到/usr/zookeeper 目录
进入/usr/zookeeper/conf目录下,复制zoo_sample.cfg ,新文件名为zoo.cfg
编辑zoo.cfg文件 ,vim zoo.cfg
这是源文件
# 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=/tmp/zookeeper
# the port at which the clients will connect
clientPort=2181
在后面加上如下内容
server.1=192.168.200.129:2888:3888
server.2=192.168.200.130:2888:3888
server.3=192.168.200.131:2888:3888
servie [1,2,3]分别代表三台机器,2888端口号是zookeeper服务之间通信的端口。 3888端口是zookeeper与其他连接应客户端通信的端口
dataDir=/tmp/zookeeper 这个目录下需要创建一个文件 myid ,192.168.200.129 的myid内容1 ,192.168.200.130的myid内容为2 ,192.168.200.131的内容为3
这里主要用于标识集群中的每一台机器。
启动验证
进入/usr/zookeeper/bin 目录
./zkServer.sh start 启动 输入jps查看输出(当看到QuorumPeerMain字样输出,代表启动成功)
./zkServer.sh status 查看集群状态 (当看到master 、follow字样代表集群启动成功 master是调度主节点 ,follow是子节点)
./zkServer.sh stop 是停止服务
安装中出现的问题 类似如下错误
java.net.NoRouteToHostException: No route to host
Cannot open channel to 2 at election address /192.168.200.129:3888
检查防火墙是否关闭
这里可以参考这篇文章(http://blog.youkuaiyun.com/c233728461/article/details/52679558)
使用java客户端连接zookeeper集群
创建一个Maven工程
打开pom.xml文件添加zookeeper依赖
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
<!--zkclient -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.7</version>
</dependency>
这里使用别人写的开源zkclient,来操作zk
以下copy自别处
zkClient 针对 zk 的一次性watcher,做了重新封装,然后定义了 stateChanged、znodeChanged、dataChanged 三种监听器。
public class ZkClientTest {
public static void main(String[] args) {
ZkClient zkClient = new ZkClient("192.168.200.129:2181,192.168.200.130:2181,192.168.200.131:2181");
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(3000000);
} 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 Deleted.");
}
});
}
/**
* 订阅状态变化
*
* @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");
}
});
}
}
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描述的路径,预新增的最终节点之前的所有父节点都必须要存在,否则会抛出异常。所以根据自己需要做好处理。