我们知道ZK有个功能就是 动态感知服务器的正常运行,
比如说:A是客户端,B、C、D都是应用服务器,一共有三台;每次请求,客户端A都去调用BCD里面的一台,如果BCD里有一
台服务器B挂掉的话,ZK感知到并告诉A下次请求C和D服务器,从而保证程序正常运行。
那我们就来用代码简单实现一下功能吧。
/*
* Project: blog.web
*
* File Created at 2018年5月26日
*
* Copyright 2018 CMCC Corporation Limited.
* All rights reserved.
*
* This software is the confidential and proprietary information of
* ZYHY Company. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license.
*/
package com.maple.blog.web.zk;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
/**
* @Type Server.java
* @Desc 我们知道ZK有个功能就是监听,父节点会监听子节点的运行状态,如果有一个子节点挂了,父节点有感知功能。
* 那现在我们就来简单开发一个 动态感知服务器上下线功能
* @author 王弘博
* @date 2018年5月26日 下午8:53:53
* @version
*/
public class DistributedServer {
private static final String connectString = "192.168.116.128:2181,192.168.116.129:2181,192.168.116.130:2181";
private static final int sessionTimeout = 2000;
private static final String parentNode = "/servers";
private static final String parentData = "zkserver";
ZooKeeper zkClient = null;
/**
* 获取到ZK客户端的连接
* @throws Exception
*/
public void getConnect() throws Exception {
zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
//收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
System.out.println(event.getType() + "----" + event.getPath());
try {
zkClient.getChildren("/", true);//再次触发监听
} catch (Exception e) {
}
}
});
}
/**
* 向ZK集群注册服务器信息
* @param hostname
* @throws Exception
*/
public void registerServer(String hostname) throws Exception {
//去根目录下创建一个 /servers
Stat stat = zkClient.exists(parentNode, false);
if(null == stat) {
zkClient.create(parentNode, parentData.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
String create = zkClient.create(parentNode+"/server", hostname.getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(hostname+" is online.... "+create);
}
/**
* 业务功能
* @throws Exception
*/
public void handleBussiness(String hostname) throws Exception {
System.out.println(hostname + " start working.........");
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
DistributedServer server = new DistributedServer();
//获取ZK连接
server.getConnect();
//利用ZK连接注册服务器信息
server.registerServer(args[0]);
//启动业务功能
server.handleBussiness(args[0]);
}
}
/**
* Revision history
* -------------------------------------------------------------------------
*
* Date Author Note
* -------------------------------------------------------------------------
* 2018年5月26日 王弘博 create
*/
我们来测试一下服务端代码是否正确:
Run As --> Run Configurations --> Arguments --> 输入maple1(我的一台服务器的主机名)
观察控制台输出情况
看到我们打印的日志:maple1 is online.... /servers/server0000000004,正常运行
再重复上面操作,运行maple2,观察控制台
因为我在handleBussiness()方法里加了 Thread.sleep(Long.MAX_VALUE),
让我们程序一直睡眠下去方便我们来测试,可以点开控制台里的这个按钮来看一下,我们刚跑的两个程序都在运行中...
我们也可以打开终端里看一下是否创建了两个服务
我们可以发现,在servers下我们注册了两个服务,经测试,我们的服务端代码正确,
关闭着两个进程,开始写客户端代码
/*
* Project: blog.web
*
* File Created at 2018年5月26日
*
* Copyright 2018 CMCC Corporation Limited.
* All rights reserved.
*
* This software is the confidential and proprietary information of
* ZYHY Company. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license.
*/
package com.maple.blog.web.zk;
import java.util.ArrayList;
import java.util.List;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
/**
* @Type Client.java
* @Desc
* @author 王弘博
* @date 2018年5月26日 下午8:54:01
* @version
*/
public class DistributedClient {
private static final String connectString = "192.168.116.128:2181,192.168.116.129:2181,192.168.116.130:2181";
private static final int sessionTimeout = 2000;
private static final String parentNode = "/servers";
private volatile List serverList;
ZooKeeper zkClient = null;
/**
* 获取到ZK客户端的连接
* @throws Exception
*/
public void getConnect() throws Exception {
zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
//收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
try {
getServerList();
} catch (Exception e) {
}
}
});
}
public void getServerList() throws Exception {
//获取服务器子节点信息,并对父节点监听
List children = zkClient.getChildren(parentNode, true);
//先创建一个局部的list来存服务器信息
List servers = new ArrayList<>();
for(String child : children) {
// child 只是子节点的节点名,还要拼上父节点
byte[] data = zkClient.getData(parentNode+"/"+child, false, null);
servers.add(new String(data,"UTF-8"));//把服务器名称maple1 ,maple2等 放进servers里
}
//把servers赋值给成员变量serverList,以提供给各业务线程使用
serverList = servers;
//打印服务器列表
System.out.println(serverList);
}
/**
* 业务功能
* @throws Exception
*/
public void handleBussiness() throws Exception {
System.out.println("client start working.........");
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
DistributedClient client = new DistributedClient();
//获取ZK连接
client.getConnect();
//获取servers的子节点(并监听),从中获取服务器信息列表
client.getServerList();
//启动业务功能
client.handleBussiness();
}
}
/**
* Revision history
* -------------------------------------------------------------------------
*
* Date Author Note
* -------------------------------------------------------------------------
* 2018年5月26日 王弘博 create
*/
先来测试一下 Run As
因为我们服务端没有运行,所以这里打印的是空,不过着也表示我们的代码是正确的。
下面就来系统的测试一下吧。
选中你的工程,右键Export,java --> Runnable JAR file
分别把这个两个类打成jar包,server.jar 和 client.jar,这里我只展示打server.jar了。
做完以上操作,进到文件里找到我们打的两个jar包,在当前目录中打开cmd
我们先来启动maple1,输入 java -jar server.jar maple1
再启动maple2,重新打开一个新的黑窗口,输入 java -jar server.jar maple2
这样我们的服务就注册完成了,我们启动客户端看看服务注册成功没
再打开一个新的黑窗口,输入 java -jar client.jar
可以看到我们在客户端代码里打印的 服务器列表,被打印出来了,为 [maple2, maple1]
看来我们的代码都是正确的,那我们来测试下,如果我们服务端,有一台服务器maple1挂了,那我们的ZK能动态感知到吗?
我们把 maple1 的那一个黑窗口关闭,再观察客户端client的黑窗口的变化
2s之后会变化,因为我们代码里配的 private static final int sessionTimeout = 2000; )
上面的图一目了然,客户端能监听到服务器节点的运行状态,依赖于我们客户端代码里的这个
好了,就到这里了,我也是正在学习ZK的小白,博客里只是做一下笔记,分享一下所学,如能帮助各位实属本人荣幸,
如有错误烦请各位指正,多谢!^-^