基于Zookeeper实现客户端动态监听服务器上下线
一、在具体实现之前,先来了解一下Zookeeper的监听器的原理:

图中Main()线程作为客户端,当在主线程中创建Zookeeper客户端时,会默认创建两个子线程:Listener和connect,connect线程负责将某一操作对应的的监听事件发送给Zookeeper服务集群。Zookeeper收到监听事件后会在该操作对应的监听器列表中注册该事件。
比如图中的获取节点“/”的子节点getChildren这一事件,并设置了true,表示监听此事件。那么Zookeeper就会在监听器列表中注册该事件。一旦“/”节点的子节点发生变化,会导致getChildren发生变化,Zookeeper就会通知客户端的Listener线程,Listener就会去调用process方法对“/”的变化做出应对处理。“/”的变化可能是客户端不能控制的,但是为了适应这种变化,客户端在收到服务器的通知后可根据自身情况做出应对。
二、这样说可能比较抽象,我们用一个案例来说明:
public class ZkDemo {
private String connect = "hadoop101:2181";
private int timeout = 2000;
private ZooKeeper zooKeeper = null;
//1、获取Zookeeper客户端,用于连接Zookeeper集群,其功能类似于Linux中启动./zkCli.sh
@Before
public void getClient() throws Exception{
zooKeeper = new ZooKeeper(connect, timeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println(event.getPath() + "已被修改,是否确定?");
try {
zooKeeper.getChildren("/lsj",true);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
/**
* 查看子节点
*/
@Test
public void testList() throws Exception {
List<String> children = zooKeeper.getChildren("/lsj", true);
for (String str: children) {
System.out.println(str);
}
Thread.sleep(Long.MAX_VALUE);
}
}
附:Thread.sleep()的设置是为了让testList方法从某种意义上更像是客户端,客户端时保持连接的,所以必须让testList方法处在执行的过程中。
默认的监听次数是一次,但是在getClient方法中使了一点小技巧使得监听的次数好像变得无限多了,只是好像稍后再说。只需记住:这段代码启动后对Zookeeper来说也是一个客户端,设为客户端1。在zooKeeper.getChildren("/lsj", true)设置了监听此事件,当/lsj节点下的子节点发生变化时getChildren方法结果会发生变化,会触发监听器Watcher(就是那个内部类)执行process方法,在process方法中执行了应对变化的处理后再次调用testList方法,这才使得监听好像变的无限多了。
下图所示:在Linux中启动的客户端(设为客户端2)每一次对/lsj节点增加节点都会视为getChildren结果的变化,故而Zookeeper会通知客户端1这种变化,进而触发监听器执行process方法。


三、下面进入Zookeeper的正题:如何利用Zookeeper监听服务器集群的变化,并在服务器集群变化时通知客户端呢?
原理和上面一样一样的,只需将客户端1想象成下文的客户端群体,把客户端2想想成服务器群体。如果没有Zookeeper这个角色,让客户端直接和服务器接触,当客户端请求的一台服务器正好宕机时,客户端将无法获取资源,但又不知道这是服务器宕机所造成的问题,也无法改变请求到另一台正常运行的服务器,那么这个问题如何解决呢?
思路是这样的,每一台服务器上线时都会在Zookeeper上的/ServerCluster的节点下创建一个标识本机的子节点(临时的 -e )。当,某一台服务器宕机时,那么其在/ServerCluster下创建的节点就会随之消失。我们让客户端获取服务器信息时,监听/ServerCluster的子节点变化,那么当某一台服务器宕机时(临时节点随之消失),Zookeeper会通知客户端/ServerCluster的变化,客户端也就知道了具体的哪一台服务器当机,哪一台服务器正常运行可以访问了。
在这个过程中,最重要的一点:服务器和客户端对于Zookeeper集群来说,都是客户端。

具体实现:
public class Server {
private static String connect = "hadoop101:2181";
private static int timeout = 2000;
private static ZooKeeper zooKeeper = null;
private static String parentPath = "/ServerCluster";
public static void main(String[] args) throws Exception {
//1、获取一个Zookeeper的客户端对象,用于服务器向Zookeeper集群注册自己。
getClient();
//2、把本服务器的主机名注册到Zookeeper中的特定节点中
registServer(args[0]);
//3、服务器本身的业务逻辑
getBusiness(args[0]);
}
private static void getBusiness(String hostname) throws InterruptedException {
System.out.println(hostname + " is working...");
Thread.sleep(Long.MAX_VALUE);
}
private static void registServer(String hostname) throws KeeperException, InterruptedException {
//创建临时节点
String path = zooKeeper.create(parentPath + "/server",
hostname.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(hostname + " is online...");
}
private static void getClient() throws Exception {
zooKeeper = new ZooKeeper(connect, timeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println(event.getType() + "---" + event.getPath());
}
});
}
}
public class Client {
private static String connect = "hadoop101:2181";
private static int timeout = 2000;
private static ZooKeeper zooKeeper = null;
private static String parentPath = "/ServerCluster";
public static void main(String[] args) throws Exception {
//1、获取一个Zookeeper的客户端对象,用于服务器向Zookeeper集群注册自己。
getClient();
//2、获取服务器列表(主机名),并监听
getServers();
//3、客户端的业务逻辑
getBusiness();
}
private static void getBusiness() throws InterruptedException {
System.out.println("Client is working...");
Thread.sleep(Long.MAX_VALUE);
}
private static void getServers() throws KeeperException, InterruptedException {
//向Zookeeper给getChildren方法注册监听,一旦parentPath节点发生变化,就会通知监听器触发process方法
List<String> children = zooKeeper.getChildren(parentPath, true);
//此集合用于保存服务器主机名
ArrayList<String> hosts = new ArrayList();
for (String child: children) {
byte[] data = zooKeeper.getData(parentPath + "/" + child, false, null);
hosts.add(new String(data));
}
System.out.println(hosts);
}
private static void getClient() throws Exception {
zooKeeper = new ZooKeeper(connect, timeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println(event.getType() + "---" + event.getPath());
//执行到本方法就说明parentPath已经被修改了,即服务器列表发生变化,需要重新获取。
try {
getServers();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
通过配置参数开启三个服务器hadoop101,开启一个Client:

关闭hadoop101:

Client观察到这种变化,打印新的服务器列表:


当然,如果在Linux中加入一个“服务器”,Client也可以监听到:

基于Zookeeper实现客户端动态监听服务器上下线的更多相关文章
- oracle静态与动态监听
在运行lsnrctl命令的status时,常会看到如下返回值: 服务“test”包含1个例程. 例程"mydata",状态 UNKOWN,包含此服务的一个处理程序... 服务 ...
- 详解vuex结合localstorage动态监听storage的变化
这篇文章主要介绍了详解vuex结合localstorage动态监听storage的变化,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 需求:不同组件间共用同一数据,当一个 ...
- js动态监听dom变化
原生js 动态监听dom变化,根据不同的类型绑定不同的处理逻辑 // Firefox和Chrome早期版本中带有前缀 var MutationObserver = window.MutationO ...
- ORACLE动态监听
动态监听的原理 pmon在数据库启动到mount或open时,动态从参数文件中读取service_names值.service_names可以为多值(可以有64个,其中包括两个系统的). servi ...
- Linux下安装oracle遇到启动监听服务器启动失败
1.发现监听服务器没有启动,则 lsntctl start 启动监听服务器: 2.发现TNS-12555问题: 3.查找TNS-12555错误,找到一个满意的答案: chmod 777 /var/ ...
- Jmeter无法监听服务器4444端口
阿里云服务器开放了4444端口 jmeter还是无法监听: 解决方法: 阿里云安全组添加端口5555 服务器中启动监听插件使用5555端口,使用命令:java -jar ./CMDRunner.jar ...
- SpringMVC 监听文件上传进度
Spring MVC 监听文件上传进度 具体实现分三个步骤: 接管CommonsMultipartResolver,重写针对文件上传的请求. 在第一步中写入监听,以获取上传进度. 修改上传部分的配置文 ...
- [Swift通天遁地]三、手势与图表-(1)监听屏幕上触摸事件的各种状态
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...
- zookeeper实现动态感知服务器上下线
在实际的生产环境中我们一般都是集群环境部署的,同一个程序我们会部署在相同的几台服务器中,这时我们可以通过负载均衡服务器去调度,但是我们并不能很快速的获知哪台服务器挂掉了,这时我们就可以使用zook ...
随机推荐
- matplotlib--直线和点
直线和点: import matplotlib.pyplot as plt import numpy as np x=np.linspace(-10,10,10) y=x**2 h=plt.plot( ...
- tensorboard可视化(先写一点点)
在tensorboard上显示运行图: import tensorflow as tf a = tf.constant(10,name="a") b = tf.constant(9 ...
- 笔记-CSS-e:nth-chid
1,CSS 2,
- 怎么测试php代码
没有任何一名程序员可以一气呵成.完美无缺的在不用调试的情况下完成一个功能或模块.调试实际分很多种情况. 暴力调试 这种方式简单粗暴,一般PHP程序员都会用,那就是浏览器调试,在编辑器内写完代码后随后打 ...
- ckeditor如何能实现直接粘贴把图片上传到服务器中?
在之前在工作中遇到在富文本编辑器中粘贴图片不能展示的问题,于是各种网上扒拉,终于找到解决方案,在这里感谢一下知乎中众大神以及TheViper. 通过知乎提供的思路找到粘贴的原理,通过TheViper找 ...
- Curl命令、Elinks命令、lynx命令、Wget命令、lftp命令
一.Curl命令 语法 curl(选项)(参数) 选项 -a/--append 上传文件时,附加到目标文件 -A/--user-agent <string> 设置用户代理发送给服务器 -a ...
- Codeforces Round #585 (Div. 2) E. Marbles (状压DP)
题目:https://codeforc.es/contest/1215/problem/E 题意:给你一个序列,你可以交换相邻的两个数,要达到一个要求,所有相同的数都相邻,问你交换次数最少是多少 思路 ...
- [CSP-S模拟测试]:Dinner(二分)
题目描述 清儿今天请好朋友们吃饭,一共$N$个人坐在坐在圆桌旁.吃饭的第一步当然是点餐了.服务员拿来了$M$份菜单.第$i$个人阅读菜单并点出自己喜欢的菜需要花费时间$T_i$.当一个人点完菜之后,就 ...
- 5 个免费的受欢迎的 SQLite 管理工具
SQLite Expert – Personal Edition SQLite Expert 提供两个版本,分别是个人版和专业版.其中个人版是免费的,提供了大多数基本的管理功能. SQLite Exp ...
- 牛客提高D3t1 破碎的矩阵
分析 我们发现如果行的异或和等于列的异或和那么对于n-1行m-1列的所有数的选择都是任意的 因为一定可以在它的行末/列末选一个合适的数是的整体满足 但是我们发现对于右下角那一个数是否满足存疑 我们设矩 ...