1.首先编写服务器端程序,原理就是上线一台服务器就会在/server这个组目录下创建一个节点server,节点的值为临时的(session消失后值也会消失)且为序列的(自动分配序列号),这样服务器上线下线,zookeeper的znode中的数据也会同步上创建和删除。
服务器端模拟代码如下:
package com.lijie.zk2;
import java.security.acl.Acl;
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.ACL;
public class MyServer {
private static final String connectString = "hadoop01:2181,hadoop02:2181,hadoop03:2181";
private static final int sessionTimeout = 2000;
private static ZooKeeper zk = null;
private static String group = "/server";
public static void main(String[] args) throws Exception {
//注册
regServer("192.168.80.123");
//执行任务
business("192.168.80.123");
}
/**
* 获取zookeeper实例
* @return
* @throws Exception
*/
public static ZooKeeper getZookeeper() throws Exception {
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 收到watch通知后的回调函数
System.out.println("事件类型" + event.getType() + ",路径" + event.getPath());
//因为监听器只会监听一次,这样可以一直监听,且只监听"/"目录
try {
zk.getChildren("/", true);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
return zk;
}
/**
* 注册到zookeeper
* @param ip
* @throws Exception
*/
public static void regServer(String ip) throws Exception {
zk = getZookeeper();
String create = zk.create(group + "/server", ip.getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(ip + " 上线了!" + ",存储路径:" + create);
}
/**
* 业务
* @throws Exception
*/
public static void business(String ip) throws Exception {
System.out.println(ip + " 处理业务");
Thread.sleep(Long.MAX_VALUE);
}
}
2.然后编写客户端程序,原理就是执行对/server下面子节点的查看并且监听,其实就相当于ls /server watch,并且在process方法中再次调用getList方法更新列表并监听,如果/server子节点有添加或者删除,那么这个监听就会被触发。
package com.lijie.zk2;
import java.util.ArrayList;
import java.util.List;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
public class MyClient {
private static final String connectString = "hadoop01:2181,hadoop02:2181,hadoop03:2181";
private static final int sessionTimeout = 2000;
private static ZooKeeper zk = null;
private static String group = "/server";
private static volatile List<String> ipList = null;
public static void main(String[] args) throws Exception {
//获取列表并且监听
getList();
//执行业务
business();
}
/**
* 获取zookeeper实例
* @return
* @throws Exception
*/
public static ZooKeeper getZookeeper() throws Exception {
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 收到watch通知后的回调函数
System.out.println("事件类型" + event.getType() + ",路径" + event.getPath());
//重新更新列表,并注册监听
try {
getList();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
return zk;
}
/**
* 获取服务器列表,并监听父节点下面的变化
* @return
* @throws Exception
*/
public static void getList() throws Exception {
zk = getZookeeper();
//获取节点名字
List<String> children = zk.getChildren(group, true);
//声明装载服务ip的集合
ArrayList<String> ips = new ArrayList<String>();
for (String path : children) {
//获取数据
byte[] data = zk.getData(group + "/" + path, false, null);
ips.add(new String(data));
}
ipList = ips;
//打印服务器列表
System.out.println("打印服务器列表" + ipList);
}
/**
* 业务
* @throws Exception
*/
public static void business() throws Exception {
System.out.println("客户端处理");
Thread.sleep(Long.MAX_VALUE);
}
}
启动两个服务器端,结果如图
第一个:
第二个:
启动客户端:
注意:上面的服务器端执行没有问题,但是客户端执行的时候其实给我报错了,但是当我debug的时候却又没问题(org.apache.zookeeper.KeeperException$ConnectionLossException: KeeperErrorCode = ConnectionLoss for /server/server0000000002
),如下:
网上查找资料说是因为要先等zookeeper连接之后再执行后面的,可以使用CountDownLatch去控制,但是我这里报这个错之前会有这些ip显示出来,应该不是这个问题,而且使用CountDownLatch去控制了依旧没有效果,这个错误先放这,下次来解决。