|NO.Z.00009|——————————|BigDataEnd|——|Hadoop&Zookeeper.V09|——|Zookeeper.v09|应用实践|服务器动态上下线.v01|
一、Zookeeper应用实践
### --- [zookeeper]
~~~ [zookeeper应用实践之服务器动态上下线监听]
~~~ [zookeeper应用实践之服务器动态上下线总结]
### --- Zookeeper实践
~~~ ZooKeeper是一个典型的发布/订阅模式的分布式数据管理与协调框架,
~~~ 我们可以使用它来进行分布式数据的发布与订阅。
~~~ 另一方面,通过对ZooKeeper中丰富的数据节点类型进行交叉使用,
~~~ 配合Watcher事件通知机制,可以非常方便地构建一系列分布式应用中都会涉及的核心功能,
~~~ 如数据发布/订阅、命名服务、集群管理、Master选举、分布式锁和分布式队列等。
~~~ 那接下来就针对这些典型的分布式应用场景来做下介绍
### --- Zookeeper的两大特性:
~~~ # 客户端如果对Zookeeper的数据节点注册Watcher监听,
~~~ 那么当该数据节点的内容或是其子节点列表发生变更时,
~~~ Zookeeper服务器就会向订阅的客户端发送变更通知。
~~~ # 对在Zookeeper上创建的临时节点,
~~~ 一旦客户端与服务器之间的会话失效,那么临时节点也会被自动删除
~~~ # 利用其两大特性,
~~~ 可以实现集群机器存活监控系统,
~~~ 若监控系统在/clusterServers节点上注册一个Watcher监听,那么但凡进行动态添加机器的操作,
~~~ 就会在/clusterServers节点下创建一个临时节点:/clusterServers/[Hostname],
~~~ 这样,监控系统就能够实时监测机器的变动情况。
二、 服务器动态上下线监听
### --- 服务器动态上下线监听
~~~ # 思路分析
~~~ 分布式系统中,主节点会有多台,主节点可能因为任何原因出现宕机或者下线,
~~~ 而任意一台客户端都要能实时感知到主节点服务器的上下线。

### --- 具体实现:
~~~ # 服务端
package com.yanqi.zk.onoffline;
import org.I0Itec.zkclient.ZkClient;
//服务端主要提供了client需要的一个时间查询服务,服务端向zk建立临时节点
public class Server {
//获取zkclient
ZkClient zkClient = null;
private void connectZk() {
// 创建zkclient
zkClient = new ZkClient("linux121:2181,linux122:2181");
//创建服务端建立临时节点的目录
if (!zkClient.exists("/servers")) {
zkClient.createPersistent("/servers");
}
}
//告知zk服务器相关信息
private void saveServerInfo(String ip, String port) {
final String sequencePath = zkClient.createEphemeralSequential("/servers/server", ip + ":" + port);
System.out.println("----->>> ,服务器:" + ip + ":" + port + ",向zk保存信息成功,成功上线可以接受client查询");
}
public static void main(String[] args) {
//准备两个服务端启动上线(多线程模拟,一个线程代表一个服务器)
final Server server = new Server();
server.connectZk();
server.saveServerInfo(args[0], args[1]);
//提供时间服务的线程没有启动,创建一个线程类,可以接收socket请求
new TimeService(Integer.parseInt(args[1])).start();
}
}
~~~ # 服务端提供时间查询的线程类
package com.yanqi.zk.onoffline;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
//提供时间查询服务
public class TimeService extends Thread {
private int port = 0;
public TimeService(int port) {
this.port = port;
}
@Override
public void run() {
//通过socket与client进行交流,启动serversocket监听请求
try {
//指定监听的端口
final ServerSocket serverSocket = new ServerSocket(port);
//保证服务端一直运行
while (true) {
final Socket socket = serverSocket.accept();
//不关心client发送内容,server只考虑发送一个时间值
final OutputStream out = socket.getOutputStream();
out.write(new Date().toString().getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
### --- client端
package com.yanqi.zk.onoffline;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
// 注册监听zk指定目录,
//维护自己本地一个servers信息,收到通知要进行更新
//发送时间查询请求并接受服务端返回的数据
public class Client {
//获取zkclient
ZkClient zkClient = null;
//维护一个serversi 信息集合
ArrayList<String> infos = new ArrayList<String>();
private void connectZk() {
// 创建zkclient
zkClient = new ZkClient("linux121:2181,linux122:2181");
//第一次获取服务器信息,所有的子节点
final List<String> childs = zkClient.getChildren("/servers");
for (String child : childs) {
//存储着ip+port
final Object o = zkClient.readData("/servers/" + child);
infos.add(String.valueOf(o));
}
//对servers目录进行监听
zkClient.subscribeChildChanges("/servers", new IZkChildListener() {
public void handleChildChange(String s, List<String> children) throws Exception {
//接收到通知,说明节点发生了变化,client需要更新infos集合中的数据
ArrayList<String> list = new ArrayList<String>();
//遍历更新过后的所有节点信息
for (String path : children) {
final Object o = zkClient.readData("/servers/" + path);
list.add(String.valueOf(o));
}
//最新数据覆盖老数据
infos = list;
System.out.println("--》接收到通知,最新服务器信息为:" + infos);
}
});
}
//发送时间查询的请求
public void sendRequest() throws IOException {
//目标服务器地址
final Random random = new Random();
final int i = random.nextInt(infos.size());
final String ipPort = infos.get(i);
final String[] arr = ipPort.split(":");
//建立socket连接
final Socket socket = new Socket(arr[0], Integer.parseInt(arr[1]));
final OutputStream out = socket.getOutputStream();
final InputStream in = socket.getInputStream();
//发送数据
out.write("query time".getBytes());
out.flush();
//接收返回结果
final byte[] b = new byte[1024];
in.read(b);//读取服务端返回数据
System.out.println("client端接收到server:+" + ipPort + "+返回结果:" + new String(b));
//释放资源
in.close();
out.close();
socket.close();
}
public static void main(String[] args) throws InterruptedException {
final Client client = new Client();
client.connectZk(); //监听器逻辑
while (true) {
try {
client.sendRequest(); //发送请求
} catch (IOException e) {
e.printStackTrace();
try {
client.sendRequest();
} catch (IOException e1) {
e1.printStackTrace();
}
}
//每隔几秒中发送一次请求到服务端
Thread.sleep(2000);
}
}
}
Walter Savage Landor:strove with none,for none was worth my strife.Nature I loved and, next to Nature, Art:I warm'd both hands before the fire of life.It sinks, and I am ready to depart
——W.S.Landor
分类:
bdv009-zookeeper
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」