我们知道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<String> 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<String> children = zkClient.getChildren(parentNode, true); //先创建一个局部的list来存服务器信息 List<String> 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的小白,博客里只是做一下笔记,分享一下所学,如能帮助各位实属本人荣幸,
如有错误烦请各位指正,多谢!^-^