Zookeeper ZkClient
1. 前言
無(wú)論是選擇 Zookeeper 的單機(jī)模式還是選擇 Zookeeper 的集群模式,實(shí)際開(kāi)發(fā)中我們都會(huì)使用 Java 客戶端向Zookeeper 服務(wù)端發(fā)送請(qǐng)求。本節(jié)我們就來(lái)學(xué)習(xí)如何使用 Zookeeper 的 Java 客戶端之一 ZkClient。
2. ZkClient 客戶端
ZkClient 是一個(gè)開(kāi)源的客戶端,在 Zookeeper 原生 API 接口的基礎(chǔ)上進(jìn)行了包裝,更便于開(kāi)發(fā)人員使用。內(nèi)部實(shí)現(xiàn)了Session超時(shí)重連,Watcher反復(fù)注冊(cè)等功能。想要使用 ZkClient,我們需要搭建 Java 開(kāi)發(fā)環(huán)境,這里我們使用 IntelliJ IDEA 為開(kāi)發(fā)工具,JDK 我們使用了長(zhǎng)期維護(hù)的版本 JDK-11.0.8 。接下來(lái)我們開(kāi)始搭建 ZkClient 運(yùn)行的環(huán)境。
2.1 ZkClient 的依賴
首先我們新建 Spring Boot 項(xiàng)目,在 pom.xml 文件中加入 zkclient 的依賴:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.cdd</groupId>
<artifactId>zkclient-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>zkclient-demo</name>
<description>zkclient-demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.101tec/zkclient -->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.11</version>
<!-- 排除沖突 -->
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
加入了 ZkClient的依賴,我們就可以編寫(xiě)程序使用 zkclient 的 API 來(lái)向 Zookeeper 服務(wù)端發(fā)送請(qǐng)求了。
2.2 編寫(xiě) ZkClientServer 類(lèi)
我們?cè)?Spring Boot 主函數(shù)的同級(jí)新建目錄 service ,在 service 目錄中新建類(lèi) ZkClientServer :
package cn.cdd.zkclientdemo.service;
import org.I0Itec.zkclient.ZkClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
// 聲明 ZkClientServer 類(lèi)為 Bean,交給 Spring 管理
@Component
public class ZkClientServer {
// Zookeeper 集群的地址
@Value(value = "${zookeeper.address}")
private String address;
// 獲取 ZkClient
public ZkClient getZkClient() {
return new ZkClient(address);
}
}
在 application.properties 配置文件中配置 Zookeeper 集群的地址:
zookeeper.address=192.168.0.77:2181,192.168.0.88:2181,192.168.0.99:2181
配置完成后,我們就可以去測(cè)試類(lèi)中進(jìn)行測(cè)試了。
2.3 測(cè)試
我們?cè)陧?xiàng)目中與 main 目錄同級(jí)的 test 目錄中找到測(cè)試類(lèi) ZkClientDemoApplicationTests ,在其中添加測(cè)試方法。
2.3.1 查詢測(cè)試
package cn.cdd.zkclientdemo;
import cn.cdd.zkclientdemo.service.ZkClientServer;
import org.I0Itec.zkclient.ZkClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class ZkClientDemoApplicationTests {
// 依賴注入
@Autowired
private ZkClientServer zkClientServer;
@Test
void contextLoads() {
// 獲取 ZkClient 對(duì)象
ZkClient zkClient = zkClientServer.getZkClient();
// 獲取子節(jié)點(diǎn)集合
List<String> children = zkClient.getChildren("/");
System.out.println(children);
// 釋放資源
zkClient.close();
}
}
執(zhí)行測(cè)試方法,控制臺(tái)輸出:
[zookeeper, imooc]
我們可以看到控制臺(tái)輸出了在上一節(jié)中我們?cè)黾拥墓?jié)點(diǎn) imooc,說(shuō)明我們的查詢成功執(zhí)行了。接下來(lái)我們測(cè)試其它的 API。
2.3.2 創(chuàng)建持久節(jié)點(diǎn)
創(chuàng)建持久節(jié)點(diǎn)測(cè)試:
@Test
void contextLoads() {
ZkClient zkClient = zkClientServer.getZkClient();
// 在 imooc 節(jié)點(diǎn)下創(chuàng)建持久節(jié)點(diǎn) wiki
zkClient.createPersistent("/imooc/wiki");
// 獲取 imooc 節(jié)點(diǎn)的子節(jié)點(diǎn)集合
List<String> children = zkClient.getChildren("/imooc");
System.out.println(children);
// 釋放資源
zkClient.close();
}
執(zhí)行測(cè)試方法,控制臺(tái)輸出:
[wiki]
2.3.3 刪除節(jié)點(diǎn)
刪除節(jié)點(diǎn)測(cè)試:
@Test
void contextLoads() {
ZkClient zkClient = zkClientServer.getZkClient();
// 刪除 imooc 的 子節(jié)點(diǎn) wiki
boolean delete = zkClient.delete("/imooc/wiki");
System.out.println(delete);
// 釋放資源
zkClient.close();
}
執(zhí)行測(cè)試方法,控制臺(tái)輸出:
true
true 表示刪除 wiki 節(jié)點(diǎn)成功。
2.3.4 讀寫(xiě)數(shù)據(jù)
節(jié)點(diǎn)數(shù)據(jù)讀寫(xiě)測(cè)試:
@Test
void contextLoads() {
ZkClient zkClient = zkClientServer.getZkClient();
// 給 imooc 節(jié)點(diǎn)寫(xiě)入數(shù)據(jù) wiki
zkClient.writeData("/imooc","wiki");
// 讀取 imooc 節(jié)點(diǎn)的數(shù)據(jù)
Object data = zkClient.readData("/imooc");
System.out.println(data);
// 釋放資源
zkClient.close();
}
執(zhí)行測(cè)試方法,控制臺(tái)輸出:
wiki
Tips: 當(dāng)我們使用 API 操作節(jié)點(diǎn)時(shí),節(jié)點(diǎn)參數(shù)必須是全路徑。
測(cè)試完成后,我們來(lái)對(duì) ZkClient 常用的 API 做一下介紹。
3. ZkClient API
- ZkClient 初始化 API
// serverstring:服務(wù)器地址的字符串
// 如:192.168.0.77:2181,192.168.0.88:2181,192.168.0.99:2181
public ZkClient(String serverstring);
// zkServers:服務(wù)器地址的字符串,connectionTimeout:連接超時(shí)時(shí)間,單位:ms
public ZkClient(String zkServers, int connectionTimeout);
// sessionTimeout: session 超時(shí)時(shí)間
public ZkClient(String zkServers, int sessionTimeout, int connectionTimeout);
// ZkSerializer:序列化方式,實(shí)現(xiàn)類(lèi)有:SerializableSerializer、BytesPushThroughSerializer
public ZkClient(String zkServers, int sessionTimeout, int connectionTimeout, ZkSerializer zkSerializer);
- 節(jié)點(diǎn)創(chuàng)建 API
// path:節(jié)點(diǎn)全路徑
public void createPersistent(String path);
// createParents:是否創(chuàng)建父節(jié)點(diǎn)
public void createPersistent(String path, boolean createParents);
// acl:權(quán)限列表
public void createPersistent(String path, boolean createParents, List<ACL> acl);
// data:節(jié)點(diǎn)數(shù)據(jù)
public void createPersistent(String path, Object data);
public void createPersistent(String path, Object data, List<ACL> acl);
// 創(chuàng)建持久順序節(jié)點(diǎn)
public String createPersistentSequential(String path, Object data);
public String createPersistentSequential(String path, Object data, List<ACL> acl);
// 創(chuàng)建臨時(shí)節(jié)點(diǎn)
public void createEphemeral(String path);
public void createEphemeral(String path, List<ACL> acl);
// mode:節(jié)點(diǎn)類(lèi)型
public String create(String path, Object data, CreateMode mode);
public String create(final String path, Object data, final List<ACL> acl, final CreateMode mode);
public void createEphemeral(String path, Object data);
public void createEphemeral(String path, Object data, List<ACL> acl);
// 創(chuàng)建臨時(shí)順序節(jié)點(diǎn)
public String createEphemeralSequential(String path, Object data);
public String createEphemeralSequential(String path, Object data, List<ACL> acl);
- 查詢節(jié)點(diǎn) API
// 獲取子節(jié)點(diǎn)列表
public List<String> getChildren(String path);
// watch:是否開(kāi)啟觀察
protected List<String> getChildren(final String path, final boolean watch);
// 獲取子節(jié)點(diǎn)數(shù)量
public int countChildren(String path);
// 節(jié)點(diǎn)是否存在
protected boolean exists(final String path, final boolean watch);
public boolean exists(String path);
- 刪除節(jié)點(diǎn) API
public boolean delete(String path);
// version:節(jié)點(diǎn)版本
public boolean delete(final String path, final int version);
- 讀寫(xiě)節(jié)點(diǎn)數(shù)據(jù) API
public <T> T readData(String path);
public <T> T readData(String path, boolean returnNullIfPathNotExists);
public <T> T readData(String path, Stat stat);
protected <T> T readData(final String path, final Stat stat, final boolean watch);
public void writeData(String path, Object object);
public void writeData(String path, Object datat, int expectedVersion);
- 觀察節(jié)點(diǎn) API
// 觀察節(jié)點(diǎn)的數(shù)據(jù)變化
public void watchForData(final String path);
// 觀察子節(jié)點(diǎn)的變化
public List<String> watchForChilds(final String path)
4. 總結(jié)
本節(jié)我們學(xué)習(xí)了如何使用 ZkClient 的 API 對(duì) Zookeeper 服務(wù)節(jié)點(diǎn)的操作,還介紹了一些常用的 API。以下是本節(jié)內(nèi)容的總結(jié):
- 使用 Spring Boot 集成 ZkClient 。
- ZkClient 的 API。