前言
GitHub:https://github.com/yihonglei/ZooKeeper-Study
本文采用zk原生客戶端方式對zk進行操作,對應github的zk-client項目。
maven引入jar包:
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.14</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
一 zkClient
ZkClient 是由 Datameer 的工程師開發的開源客戶端,對 Zookeeper 的原生 API 進行了包 裝,實現了超時重連、
Watcher 反覆註冊等功能。
二 zkClient操作znode
實例代碼:
package com.lanhuigu.zookeeper.znode;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;
import java.io.Serializable;
import java.util.List;
/**
* ZkClient 進行CRUD操作。
*
* @auther: yihonglei
* @date: 2019-05-14 17:09
*/
public class ZkClientCrud<T> {
private String connectString = "127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183";
private ZkClient zkClient;
public ZkClientCrud() {
this.zkClient = new ZkClient(connectString, 5000,
5000, new SerializableSerializer());
}
/**
* 創建持久節點
*/
public void createPersistent(String path, Object data) {
zkClient.createPersistent(path, data);
}
/**
* 遞歸創建持久節點
*/
public void createPersistent(String path, boolean createParents) {
zkClient.createPersistent(path, createParents);
}
/**
* 讀取信息
*/
public T readData(String path) {
return zkClient.readData(path);
}
/**
* 獲取子節點列表
*/
public List<String> getChildren(String path) {
return zkClient.getChildren(path);
}
/**
* 數據寫入節點
*/
public void writeData(String path, Object object) {
zkClient.writeData(path, object);
}
/**
* 刪除節點
*/
public void delete(String path) {
zkClient.delete(path);
}
/**
* 判斷節點是否存在
*/
public boolean exists(String path) {
return zkClient.exists(path);
}
/**
* 遞歸刪除節點
*/
public void deleteRecursive(String path) {
zkClient.deleteRecursive(path);
}
/**
* 用戶實體,需要序列化
*/
public static class User implements Serializable {
private String userName;
private String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", password='" + password + '\'' +
'}';
}
}
/**
* 測試
*/
public static void main(String[] args) {
ZkClientCrud client = new ZkClientCrud();
// ------ 1、存儲單個值 -------
String path = "/lanhuiguZkClient";
// 判斷節點是否存在
if (client.exists(path)) {
client.delete(path);
}
// 創建節點
client.createPersistent(path, "2019");
// 讀取數據
String data = client.readData(path).toString();
System.out.println("存儲單個值readData:" + data);
// ------ 2、存儲對象 -------
String pathObj = "/lanhuiguZkClientObj";
// 判斷節點是否存在
if (client.exists(pathObj)) {
client.delete(pathObj);
}
// 創建節點
User user = new User();
user.setUserName("root");
user.setPassword("123456");
client.createPersistent(pathObj, user);
// 讀取數據
System.out.println("存儲對象readData:" + client.readData(pathObj));
}
}
三 zkClient操作watcher
zkClient封裝了三種重要的註冊監聽。
接口類 | 註冊監聽方法 | 解除監聽方法 |
IZkChildListener (子節點) |
ZkClient 的 subscribeChildChanges 方法 |
ZkClient 的 unsubscribeChildChanges 方法 |
IZkDataListener (數據) |
ZkClient 的 subscribeDataChanges 方法 |
ZkClient 的 unsubscribeDataChanges 方法 |
IZkStateListener (客戶端狀 態) |
ZkClient 的 subscribeStateChanges 方 法 |
ZkClient 的 unsubscribeStateChanges 方法 |
在 ZkClient 中客戶端可以通過註冊相關的事件監聽來實現對 Zookeeper 服務端時間的訂閱。
package com.lanhuigu.zookeeper.watcher;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.IZkStateListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;
import org.apache.zookeeper.Watcher;
import java.util.List;
/**
* ZkClient 進行watcher操作。
*
* @auther: yihonglei
* @date: 2019-05-14 17:09
*/
public class ZkClientWatcher<T> {
private String connectString = "127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183";
private ZkClient zkClient;
public ZkClientWatcher() {
this.zkClient = new ZkClient(connectString, 5000, 5000, new SerializableSerializer());
}
/**
* 讀取信息
*/
public T readData(String path) {
return zkClient.readData(path);
}
/**
* 獲取子節點列表
*/
public List<String> getChildren(String path) {
return zkClient.getChildren(path);
}
/**
* 數據寫入節點
*/
public void writeData(String path, Object object) {
zkClient.writeData(path, object);
}
/**
* 創建持久節點
*/
public void createPersistent(String path, Object data) {
zkClient.createPersistent(path, data);
}
/**
* 遞歸創建持久節點
*/
public void createPersistent(String path, boolean createParents) {
zkClient.createPersistent(path, createParents);
}
/**
* 判斷節點是否存在
*/
public boolean exists(String path) {
return zkClient.exists(path);
}
/**
* 刪除節點
*/
public void delete(String path) {
zkClient.delete(path);
}
/**
* 遞歸刪除節點
*/
public void deleteRecursive(String path) {
zkClient.deleteRecursive(path);
}
/**
* 節點添加監聽
*/
public void listener(String path) {
// 對當前節點數據改變進行監聽
zkClient.subscribeDataChanges(path, new IZkDataListener() {
@Override
public void handleDataChange(String dataPath, Object data) throws Exception {
System.out.println("【變更節點】dataPath: " + dataPath + ", data: " + data);
}
@Override
public void handleDataDeleted(String dataPath) throws Exception {
System.out.println("【刪除節點】dataPath: " + dataPath);
}
});
// 對當前節點的子節點改變進行監聽
zkClient.subscribeChildChanges(path, new IZkChildListener() {
@Override
public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
System.out.println("【子節點改變監聽】parentPath: " + parentPath + ", currentChilds: " + currentChilds);
}
});
// 對當前節點狀態變化進行監聽
zkClient.subscribeStateChanges(new IZkStateListener() {
@Override
public void handleStateChanged(Watcher.Event.KeeperState state) throws Exception {
if (state == Watcher.Event.KeeperState.SyncConnected) {
// 當重新啓動後start,監聽觸發
System.out.println("連接成功");
} else if (state == Watcher.Event.KeeperState.Disconnected) {
System.out.println("連接斷開");// 當在服務端將zk服務stop時,監聽觸發
} else {
System.out.println("其他狀態" + state);
}
}
@Override
public void handleNewSession() throws Exception {
System.out.println("重建session");
}
@Override
public void handleSessionEstablishmentError(Throwable error) throws Exception {
}
});
}
/**
* 測試代碼
*/
public static void main(String[] args) throws InterruptedException {
ZkClientWatcher zkClientWatcher = new ZkClientWatcher();
String path = "/lanhuiguZkClientWatcher";
// 啓動監聽
zkClientWatcher.listener(path);
// 刪除節點
if (zkClientWatcher.exists(path)) {
zkClientWatcher.deleteRecursive(path);
}
// 創建節點
zkClientWatcher.createPersistent(path, "2019");
// 休眠
Thread.sleep(2000);
// 寫操作
zkClientWatcher.writeData(path, "2019-new");
// 讓主線程一直休眠,不斷掉
Thread.sleep(Integer.MAX_VALUE);
}
}
監聽處理註冊後,節點發生改變,觸發相應的事件。