这里简单模拟了一个分布式应用的主节点HA及客户端动态更新主节点状态的场景。以便于了解Zookeeper的java API。
场景:三台服务器mini1,mini2,mini3,上线后向Zookeeper注册信息,然后客户端实时获取服务器列表。
代码中将服务器注册在Zookeeper的“/servers”路径下,需要先创建这个路径,方法为:
1.在Zookeeper服务端输入zkCli.sh(前提得先将Zookeeper启动,具体方法见【大数据】Zookeeper的学习与安装(附加脚本全启动))
2.关于Zookeeper的命令,可以通过-help
查看了解,这里就不多说了,输入create /servers iii
即可(这里create后面第一个参数是指创建节点名称,第二个参数是指节点内容,这里随便写为iii)
服务器端:
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
public class DistributedServer {
//设置连接路径(我的三台Zookeeper服务器)
private static final String connectString = "zkmini1:2181,zkmini2:2181,zkmini3:2181";
//设置超时时间
private static final int sessionTimeout = 2000;
//父节点路径
private static final String parentNodePath = "/servers";
private static ZooKeeper zk = null;
//获取Zookeeper连接
public void getConnection() throws Exception{
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
//打印监听器事件即发生路径
System.out.println("触发事件:" + event.getType() + "&&触发路径:" + event.getPath());
try {
//重新绑定监听器
zk.getChildren("/", true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
//注册服务器到Zookeeper上
public void registerServer(String hostname) throws Exception{
//在Zookeeper上创建服务器信息节点
//parm1:节点路径
//parm2:节点内容,这里就可以写服务器信息,例如服务器名、ip等信息,可传任何形式参数,byte流。
//parm3:节点的acl策略
//parm4:节点类型,类型定义在枚举CreateMode中:(1)PERSISTENT:持久;(2)PERSISTENT_SEQUENTIAL:持久顺序;(3)EPHEMERAL:临时;(4)EPHEMERAL_SEQUENTIAL:临时顺序。
zk.create(parentNodePath + "/server", hostname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(hostname + " is Online.");
}
//模拟服务期间的业务逻辑
public void hanlerBussiness(String hostname) throws Exception{
System.out.println(hostname + " start working...");
Thread.sleep(Long.MAX_VALUE);
}
//模拟传入参数:服务器名
public static void main(String[] args) throws Exception {
DistributedServer ds = new DistributedServer();
//获取zk连接
ds.getConnection();
//利用zk连接注册服务器信息
ds.registerServer(args[0]);
//处理业务逻辑
ds.hanlerBussiness(args[0]);
}
}
测试运行截图
客户端
客户端主要事实获取服务器列表,当服务器上下线时,客户端均可以感知,并再次获取服务器列表。
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 DistributedClient {
//设置连接路径(我的三台Zookeeper服务器)
private static final String connectString = "zkmini1:2181,zkmini2:2181,zkmini3:2181";
//设置超时时间
private static final int sessionTimeout = 2000;
//父节点路径
private static final String parentNodePath = "/servers";
public volatile List<String> serverList= new ArrayList<>();
private static ZooKeeper zk = null;
public void getConnection() throws Exception{
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("触发事件:" + event.getType() + "&&触发路径:" + event.getPath());
try {
//重新更新服务器列表并绑定监听。
getServerList();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* 获取服务器信息列表
* @throws Exception
*/
public void getServerList() throws Exception{
//获取服务器子节点信息,并对其父节点进行监听
List<String> children = zk.getChildren(parentNodePath, true);
//先创建一个临时的list来存服务器列表信息
List<String> sl = new ArrayList<>();
for(String child : children){
String data = new String(zk.getData(parentNodePath+"/" + child, false, null));
sl.add(data);
}
System.out.println(serverList);
serverList = sl;
}
//模拟客户端的业务逻辑
public void hanlerBussiness() throws Exception{
System.out.println(" clinet start working...");
Thread.sleep(Long.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
DistributedClient dc = new DistributedClient();
//获取zk连接
dc.getConnection();
//获取服务器列表
dc.getServerList();
//业务逻辑
dc.hanlerBussiness();
}
}
测试运行截图:
然后将这两个文件打包,就可以正式模拟场景运行了:(一个cmd窗口模拟一台服务器,下面均在不同cmd窗口,客户端在eclipse上运行)
1.先启动mini1、mini2:
2.启动客户端:(这图其实是最先启动客户端,再启动mini1,mini2)
可以看到现在有两台服务器为[mini1,mini2]
3.启动第三台服务器:
客户端出现:
这样就OK拉