原理就是客户端连接到ZooKeeper集群后,一直监听一个节点,然后服务器连接到Zookeeper集群后,在客户端监听的节点下创建一个代表自身信息的临时节点,这样客户端就可以收到服务器的上下线消息了。
为了您的观看体验,下面的代码需要您有以下前置知识:
- 了解ZooKeeper相关知识及其常用命令;
- 了解Java的ZooKeeper API;
首先创建一个共有的父类AbstractZooKeeper
:
public abstract class AbstractZooKeeper {
//zookeeper集群的服务器地址和端口
private final String CLUSTER = "此处应为你自己的服务器集群对应IP和端口";
//客户端会话超时时间,毫秒
private final int SESSION_TIMEOUT = 2000;
protected ZooKeeper zooKeeper;
public AbstractZooKeeper() {
init(); //初始化zookeeper客户端
}
private void init() {
try {
zooKeeper = new ZooKeeper(CLUSTER, SESSION_TIMEOUT, (watchedEvent) -> {
watcher();
});
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 监视器,留给子类实现
*/
public abstract void watcher();
}
然后写一个客户端继承它:
public class ZKClient extends AbstractZooKeeper {
@Override
public void watcher() {
try {
getServer(); //重写父类的watcher方法,实现自己的逻辑
} catch (InterruptedException | KeeperException e) {
e.printStackTrace();
}
}
/**
* 监听/servers节点的变化
*/
public void getServer() throws InterruptedException, KeeperException {
List<String> children = zooKeeper.getChildren("/servers", true);
System.out.println(children);
}
//模拟客户端运行,直接睡就好
public void business() {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
接着就是服务端,同样继承它:
public class ZKServer extends AbstractZooKeeper {
private String serverName; //服务名
private String hostname; //主机名
private Integer port; //端口号
public ZKServer(String serverName, String hostname, Integer port) {
this.serverName = serverName;
this.hostname = hostname;
this.port = port;
}
/**
* 将server注册到该/servers节点下,使用临时节点并且编号的节点
*/
public void register() throws InterruptedException, KeeperException {
String path = zooKeeper.create("/servers/" + serverName,
(hostname + port).getBytes(StandardCharsets.UTF_8),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(serverName + " is online, zookeeper real path is " + path);
}
//模拟客户端运行,直接睡就好
public void business() {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void watcher() {
//自己的监视逻辑,有需要再自行实现
}
}
接着就是我们的测试类了:
public class ZKDynamicOnlineAndOffline {
@Test
public void client() {
ZKClient zkClient = new ZKClient();
zkClient.business();
}
@Test
public void server1() throws InterruptedException, KeeperException {
ZKServer zkServer = new ZKServer("order", "17.142.153.3", 8080);
zkServer.register();
zkServer.business();
}
@Test
public void server2() throws InterruptedException, KeeperException {
ZKServer zkServer = new ZKServer("order", "17.142.153.4", 8080);
zkServer.register();
zkServer.business();
}
@Test
public void server3() throws InterruptedException, KeeperException {
ZKServer zkServer = new ZKServer("order", "17.142.153.5", 8080);
zkServer.register();
zkServer.business();
}
}
代码供参考阅读,有兴趣的小伙伴可以复制运行看下效果。