SprongBoot鏈接Zookeeper集羣&節點監聽

前提摘要

      搭建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服務調用

 

本文到這裏就結束了,作爲記錄,方便翻閱,若有錯誤還請指正 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章