前提摘要
搭建zookeeper集羣服務,推薦博客:https://blog.csdn.net/qq_37936542/article/details/107096985
zk1 -- 192.168.0.211:2181
zk2 -- 192.168.0.212:2181
zk3 -- 192.168.0.213:2181
♦ ♦ ZkClient依賴
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
♦ ♦ 鏈接zk集羣
// zk集羣服務器地址
String serverUrl = "192.168.0.211:2181,192.168.0.212:2181,192.168.0.213:2181";
// 連接超時時間
int timeout = 5000;
// 建立連接
ZkClient zk = new ZkClient(serverUrl, timeout);
♦ ♦ 基操勿6
import java.util.List;
import org.I0Itec.zkclient.ZkClient;
public class ZkOperater {
public static void main(String[] args) throws Exception {
ZkOperater sc = new ZkOperater();
// 獲取zk鏈接
ZkClient zk = sc.getConnection();
// 創建持久節點
zk.createPersistent("/pit", "v1");
// 創建持久序列化節點
zk.createPersistentSequential("/pits", "v2");
// 創建臨時節點
zk.createEphemeral("/erl", "v3");
// 創建臨時序列化節點
zk.createEphemeralSequential("/erls", "v4");
// 獲取節點數據
String v1 = zk.readData("/pit").toString();
System.out.println(v1);
// 向節點寫數據
zk.writeData("/pit", "v5");
// 獲取節點下的所有子節點
List<String> childs = zk.getChildren("/");
for (String child : childs) {
System.out.println(child);
}
}
/**
* 獲取zk鏈接
*
* @return
*/
public ZkClient getConnection() {
// zk集羣服務器地址
String serverUrl = "192.168.0.211:2181,192.168.0.212:2181,192.168.0.213:2181";
// 連接超時時間
int timeout = 5000;
// 建立連接
ZkClient zk = new ZkClient(serverUrl, timeout);
return zk;
}
}
♦ ♦ 實現監聽子節點變化 應用場景:監聽服務器動態上下線
import java.util.List;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
public class SyncServer {
private static String nodePath = "/servers";
public static void main(String[] args) throws Exception {
SyncServer ss = new SyncServer();
// 獲取zk鏈接
ZkClient zk = ss.getConnection();
// 監聽節點下子節點的變化
ss.childListener(zk, nodePath);
// 讓服務一直運行
System.in.read();
}
/**
* 獲取zk鏈接
*
* @return
*/
public ZkClient getConnection() {
// zk集羣服務器地址
String serverUrl = "192.168.0.211:2181,192.168.0.212:2181,192.168.0.213:2181";
// 連接超時時間
int timeout = 5000;
// 建立連接
ZkClient zk = new ZkClient(serverUrl, timeout);
return zk;
}
/**
* 監聽父節點下子節點的變化,經典案例:監聽服務器動態上下線
*
* @param zk
*/
public void childListener(ZkClient zk, String nodePath) {
// 如果父節點不存在,創建父節點
if (!zk.exists(nodePath)) {
zk.createPersistent(nodePath);
}
// 監聽/server下子節點的變化
zk.subscribeChildChanges(nodePath, new IZkChildListener() {
/**
* parentPath:父節點 childs:所有子節點的集合
*/
@Override
public void handleChildChange(String parentPath, List<String> childs) throws Exception {
System.out.println("=================");
System.out.println("父節點:" + parentPath);
for (String child : childs) {
System.out.println("子節點:" + child);
}
}
});
}
}
測試:鏈接客戶端,在/servers下動態添加、刪除節點,查看打印
♦ ♦ 實現監聽節點數據變化 應用場景:協同配置管理
import java.util.UUID;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
public class SyncConfig {
private static String configPath = "/config";
public static void main(String[] args) throws Exception {
SyncConfig sc = new SyncConfig();
ZkClient zk = sc.getConnection();
// 創建配置節點
if (!zk.exists(configPath)) {
zk.createPersistent(configPath);
}
// 定時改變節點數據
sc.changeNode(zk, configPath);
// 監聽節點數據變化
sc.dataListener(zk, configPath);
// 讓服務一直運行
System.in.read();
}
/**
* 獲取zk鏈接
*
* @return
*/
public ZkClient getConnection() {
// zk集羣服務器地址
String serverUrl = "192.168.0.211:2181,192.168.0.212:2181,192.168.0.213:2181";
// 連接超時時間
int timeout = 5000;
// 建立連接
ZkClient zk = new ZkClient(serverUrl, timeout);
// 自定義zk序列化
zk.setZkSerializer(new ISerializer());
return zk;
}
/**
* 監聽節點數據的變化,經典案例:協同配置
*
* @param zk
*/
public void dataListener(ZkClient zk, String configPath) {
// 監聽/servers下子節點的變化
zk.subscribeDataChanges(configPath, new IZkDataListener() {
// 監聽數據被被刪除
@Override
public void handleDataDeleted(String dataPath) throws Exception {
System.out.println("節點數據被刪除:" + dataPath);
}
// 監聽節點數據被修改
@Override
public void handleDataChange(String dataPath, Object data) throws Exception {
System.out.println("節點數據被修改:" + dataPath);
System.out.println("修改後的數據爲:" + String.valueOf(data));
}
});
}
public void changeNode(ZkClient zk, String configPath) {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
// 向節點寫入數據
zk.writeData(configPath, UUID.randomUUID());
// 睡眠5秒
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
這裏我們需要自定義zk序列化,不然寫入數據會報:org.I0Itec.zkclient.exception.ZkMarshallingError: java.io.EOFException
import java.io.UnsupportedEncodingException;
import org.I0Itec.zkclient.exception.ZkMarshallingError;
import org.I0Itec.zkclient.serialize.ZkSerializer;
public class ISerializer implements ZkSerializer {
@Override
public byte[] serialize(Object obj) throws ZkMarshallingError {
try {
return String.valueOf(obj).getBytes("utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
@Override
public Object deserialize(byte[] bytes) throws ZkMarshallingError {
try {
return new String(bytes, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return bytes;
}
}
啓動程序,查看打印
♦ ♦ 案例:模擬實現服務器動態上下線
功能圖解:
ServerA
import org.I0Itec.zkclient.ZkClient;
public class ServerA {
private static String parentNodePath = "/servers";
public static void main(String[] args) throws Exception {
ServerA A = new ServerA();
ZkClient zk = A.getConnection();
// 判斷父節點是否存在,不存在則創建
if (!zk.exists(parentNodePath)) {
zk.createPersistent(parentNodePath);
}
// 向/server節點下注冊服務,值爲服務器ip+端口號
// 節點類型是臨時節點
zk.createEphemeral(parentNodePath + "/serverA", "ip1:port1");
System.out.println("服務A啓動服務");
// 讓程序一直運行
System.in.read();
}
/**
* 獲取zk鏈接
*
* @return
*/
public ZkClient getConnection() {
// zk集羣服務器地址
String serverUrl = "192.168.0.211:2181,192.168.0.212:2181,192.168.0.213:2181";
// 連接超時時間
int timeout = 5000;
// 建立連接
ZkClient zk = new ZkClient(serverUrl, timeout);
return zk;
}
}
ServerB
import org.I0Itec.zkclient.ZkClient;
public class ServerB {
private static String parentNodePath = "/servers";
public static void main(String[] args) throws Exception {
ServerB B = new ServerB();
ZkClient zk = B.getConnection();
// 判斷父節點是否存在,不存在則創建
if (!zk.exists(parentNodePath)) {
zk.createPersistent(parentNodePath);
}
// 向/server節點下注冊服務,值爲服務器ip+端口號
// 節點類型是臨時節點
zk.createEphemeral(parentNodePath + "/serverB", "ip2:port2");
System.out.println("服務B啓動服務");
// 讓程序一直運行
System.in.read();
}
/**
* 獲取zk鏈接
*
* @return
*/
public ZkClient getConnection() {
// zk集羣服務器地址
String serverUrl = "192.168.0.211:2181,192.168.0.212:2181,192.168.0.213:2181";
// 連接超時時間
int timeout = 5000;
// 建立連接
ZkClient zk = new ZkClient(serverUrl, timeout);
return zk;
}
}
ServerC
import org.I0Itec.zkclient.ZkClient;
public class ServerC {
private static String parentNodePath = "/servers";
public static void main(String[] args) throws Exception {
ServerC C = new ServerC();
ZkClient zk = C.getConnection();
// 判斷父節點是否存在,不存在則創建
if (!zk.exists(parentNodePath)) {
zk.createPersistent(parentNodePath);
}
// 向/server節點下注冊服務,值爲服務器ip+端口號
// 節點類型是臨時節點
zk.createEphemeral(parentNodePath + "/serverC", "ip3:port3");
System.out.println("服務C啓動服務");
// 讓程序一直運行
System.in.read();
}
/**
* 獲取zk鏈接
*
* @return
*/
public ZkClient getConnection() {
// zk集羣服務器地址
String serverUrl = "192.168.0.211:2181,192.168.0.212:2181,192.168.0.213:2181";
// 連接超時時間
int timeout = 5000;
// 建立連接
ZkClient zk = new ZkClient(serverUrl, timeout);
return zk;
}
}
客戶端 IClient
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
public class IClient {
private static String parentNodePath = "/servers";
private static List<String> servers = new ArrayList<String>();
public static void main(String[] args) throws Exception {
IClient client = new IClient();
ZkClient zk = client.getConnection();
// 動態監聽服務列表
client.childListener(zk, parentNodePath);
// 一直調用服務
while (true) {
client.getServer();
Thread.sleep(2000);
}
}
/**
* 獲取zk鏈接
*
* @return
*/
public ZkClient getConnection() {
// zk集羣服務器地址
String serverUrl = "192.168.0.211:2181,192.168.0.212:2181,192.168.0.213:2181";
// 連接超時時間
int timeout = 5000;
// 建立連接
ZkClient zk = new ZkClient(serverUrl, timeout);
return zk;
}
/**
* 監聽某節點下子節點的變化,經典案例:監聽服務器動態上下線
*
* @param zk
*/
public void childListener(ZkClient zk, String parentNodePath) {
// 可用的服務列表
servers = zk.getChildren(parentNodePath);
// 監聽/servers下子節點的變化
zk.subscribeChildChanges(parentNodePath, new IZkChildListener() {
@Override
public void handleChildChange(String parentPath, List<String> childs) throws Exception {
servers = childs;
}
});
}
private void getServer() {
// 模擬隨機負載均衡算法
if (servers.size() > 0) {
int index = new Random().nextInt(servers.size());
System.out.println("調用了服務:" + servers.get(index));
} else
System.out.println("沒有可提供的服務列表");
}
}
測試:依次啓動服務A、B、C,最後啓動IClient,查看打印發現A、B、C都有被調用
關閉A服務,由於網絡原因,這個監聽過程可能需要幾秒鐘,然後觀察打印,發現只有B、C服務調用
本文到這裏就結束了,作爲記錄,方便翻閱,若有錯誤還請指正