目录
一、概述
Zookeeper 的Java客户端API使我们更轻松的去对zookeeper进行各种操作,如创建节点、修改节点值、删除节点等等。
客户端应该遵循以下几个步骤:
- 连接到zookeeper服务器。zookeeper服务器为客户端分配会话ID。
- 定期向服务器发送心跳。否则,zookeeper服务器将过期会话ID,客户端需要重新连接。
- 只要会话ID处于活动状态,就可以获取/设置znode。
- 所有任务完成后,断开与zookeeper服务器的连接。如果客户端长时间不活动,则zookeeper服务器将自动断开客户端。
本篇文章将会搭建一个简单的示例,来看看如何通过zookeeper客户端实现对服务端的操作。
二、环境搭建
创建一个springboot项目【zookeeper-api-demo】,导入zookeeper依赖。
【a】pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.8</version>
</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>
【b】连接zookeeper服务器
因为连接zk服务器可能需要一些时间,我们可以使用 countDownLatch 阻塞,等待连接成功,控制台输出连接状态。
zookeeper一共有四种构造方法,根据方法参数不同:
- public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd, boolean canBeReadOnly)
- public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd)
- public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly)
- ZooKeeper(String connectString, int sessionTimeout, Watcher watcher)
参数说明如下:
- connectSstring:连接服务器列表,以“,”分割;
- sessionTimeout:心跳检测时间周期(毫秒);‘
- wather:事件监听处理通知器;
- canBeReadOnly:标识当前会话是否支持只读;
- sessionld和sessionPasswd:提供连接zookeeper的sessionld和密码, 通过这两个确定唯一一台客户端,目的是可以提供重复会话;
这里我们使用最简单的带三个参数的构造方法创建一个zookeeper对象:
package com.wsh.zookeeper.zookeeperapidemo.zookeper;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
/**
* @Description: zookeeper java客户端测试
* @author: weishihuai
*/
public class ZookeeperDemo {
/**
* zookeeper服务器地址
*/
private static final String CONNECT_STRING = "192.168.179.133:2181";
/**
* 会话超时时间
*/
private static final int SESSION_TIMEOUT = 3000;
/**
* 日志
*/
private static final Logger logger = LoggerFactory.getLogger(ZookeeperDemo.class);
public static void main(String[] args) {
//门闩
CountDownLatch countDownLatch = new CountDownLatch(1);
try {
//创建zookeeper连接
ZooKeeper zooKeeper = new ZooKeeper(
CONNECT_STRING, SESSION_TIMEOUT, watchedEvent -> {
//获取事件的状态
Watcher.Event.KeeperState keeperState = watchedEvent.getState();
Watcher.Event.EventType eventType = watchedEvent.getType();
//如果是建立连接
if (Watcher.Event.KeeperState.SyncConnected == keeperState) {
if (Watcher.Event.EventType.None == eventType) {
logger.info("连接zookeeper成功...");
//收到服务端的响应,表示连接zookeeper成功,同时发送信号量,计数器减一,让await阻塞程序继续往下执行
countDownLatch.countDown();
}
}
});
//阻塞程序
countDownLatch.await();
logger.info("客户端连接状态: {}", zooKeeper.getState());
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
运行main方法,结果如下:
11:23:59.299 [main-EventThread] INFO com.wsh.zookeeper.zookeeperapidemo.zookeper.ZookeeperDemo - 连接zookeeper成功...
11:23:59.299 [main] INFO com.wsh.zookeeper.zookeeperapidemo.zookeper.ZookeeperDemo - 客户端连接状态: CONNECTED
可以看到,我们成功连接到zookeeper服务器端,下面就可以进行创建节点、修改节点、删除节点等操作了。
三、创建节点
创建节点主要分为同步方式和异步方式进行创建:
- create(String path, byte[] data, List<ACL> acl, CreateMode createMode) // 同步方式
- create(String path, byte[] data, List<ACL> acl, CreateMode createMode,AsyncCallback.StringCallback callBack,Object ctx) // 异步方式
参数说明如下:
- path - znode路径;
- data - 要存储在指定znode路径中的数据;
- acl - 要创建的节点的访问控制列表。zookeeper API提供了一个静态接口ZooDefs.Ids 来获取一些基本的acl列表;
- createMode - 节点的类型,这是一个枚举,提供四种节点类型:
- PERSISTENT (持久节点)
- PERSISTENT_SEQUENTIAL (持久顺序节点)
- EPHEMERAL (临时节点)
- EPHEMERAL_SEQUENTIAL (临时顺序节点)
- callBack - 异步回调接口;
- ctx - 传递上下文参数;
案例:
首先在命令行创建/create父节点:
[zk: localhost:2181(CONNECTED) 19] create /create "create"
Created /create
[zk: localhost:2181(CONNECTED) 20] ls /
[zookeeper, create]
package com.wsh.zookeeper.zookeeperapidemo.zookeper;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ZookeeperCreate {
/**
* zookeeper服务器地址
*/
private static final String CONNECT_STRING = "192.168.179.133:2181";
/**
* 会话超时时间
*/
private static final int SESSION_TIMEOUT = 3000;
/**
* 日志
*/
private static final Logger logger = LoggerFactory.getLogger(ZookeeperDemo.class);
/**
* zookeeper对象
*/
private static ZooKeeper zooKeeper = null;
static {
//创建zookeeper连接
try {
zooKeeper = new ZooKeeper(
CONNECT_STRING, SESSION_TIMEOUT, new CustomWatcher());
} catch (IOException e) {
e.printStackTrace();
}
}
void createNode01() {
try {
/**
* create(String path, byte[] data, List<ACL> acl, CreateMode createMode)
* path: 节点的路径
* data: 节点的数据
* acl: 权限列表 world:anyone:cdrwa
* createMode: 节点类型 持久化节点
*/
zooKeeper.create("/create/node1", "node1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
/**
* world授权模式
*/
void createNode02() {
// 权限列表
List<ACL> aclList = new ArrayList<>();
// 授权模式和授权对象
Id id = new Id("world", "anyone");
// 权限设置:crdwa
aclList.add(new ACL(ZooDefs.Perms.READ, id));
aclList.add(new ACL(ZooDefs.Perms.WRITE, id));
aclList.add(new ACL(ZooDefs.Perms.DELETE, id));
aclList.add(new ACL(ZooDefs.Perms.CREATE, id));
aclList.add(new ACL(ZooDefs.Perms.ADMIN, id));
try {
zooKeeper.create("/create/node2", "node2".getBytes(), aclList, CreateMode.PERSISTENT);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
/**
* auth授权模式
*/
void createNode03() {
zooKeeper.addAuthInfo("digest", "admin:123456".getBytes());
// 权限列表
List<ACL> aclList = new ArrayList<>();
//授权模式和授权对象
Id id = new Id("auth", "admin");
// 权限设置
aclList.add(new ACL(ZooDefs.Perms.READ, id));
try {
zooKeeper.create("/create/node3", "node3".getBytes(), aclList, CreateMode.PERSISTENT);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
/**
* digest授权模式
*/
void createNode04() {
// 权限列表
List<ACL> aclList = new ArrayList<>();
// 授权模式和授权对象
Id id = new Id("digest", "admin:0uek/hZ/V9fgiM35b0Z2226acMQ=");
// 权限设置
aclList.add(new ACL(ZooDefs.Perms.ALL, id));
try {
zooKeeper.create("/create/node4", "node4".getBytes(), aclList, CreateMode.PERSISTENT);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
/**
* 异步方式创建节点
*/
void createNode05() {
Map<String, Object> callbackParams = new HashMap<>(16);
callbackParams.put("name", "wsh");
zooKeeper.create("/create/node5", "node5".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, new AsyncCallback.StringCallback() {
@Override
public void processResult(int rc, String path, Object ctx, String name) {
//rc: 0 代表创建成功
//path: 节点路径
//ctx: 上下文参数
//name: 节点路径
logger.info("rc : {}", rc);
logger.info("path : {}", path);
logger.info("ctx : {}", ctx);
logger.info("name : {}", name);
}
}, callbackParams);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ZookeeperCreate zookeeperCreate = new ZookeeperCreate();
zookeeperCreate.createNode01();
zookeeperCreate.createNode02();
zookeeperCreate.createNode03();
zookeeperCreate.createNode04();
zookeeperCreate.createNode05();
}
static class CustomWatcher implements Watcher {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
System.out.println("连接创建成功!");
}
}
}
}
然后打开终端查看对应节点是否创建成功:
[zk: localhost:2181(CONNECTED) 21] ls /
[zookeeper, create]
[zk: localhost:2181(CONNECTED) 22] ls /create
[node4, node5, node2, node3, node1]
[zk: localhost:2181(CONNECTED) 23] get /create/node1
node1
cZxid = 0x8f
ctime = Mon Dec 21 15:32:38 CST 2020
mZxid = 0x8f
mtime = Mon Dec 21 15:32:38 CST 2020
pZxid = 0x8f
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 24] get /create/node2
node2
cZxid = 0x90
ctime = Mon Dec 21 15:32:38 CST 2020
mZxid = 0x90
mtime = Mon Dec 21 15:32:38 CST 2020
pZxid = 0x90
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 25] get /create/node3
node3
cZxid = 0x91
ctime = Mon Dec 21 15:32:38 CST 2020
mZxid = 0x91
mtime = Mon Dec 21 15:32:38 CST 2020
pZxid = 0x91
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 26] get /create/node4
node4
cZxid = 0x92
ctime = Mon Dec 21 15:32:38 CST 2020
mZxid = 0x92
mtime = Mon Dec 21 15:32:38 CST 2020
pZxid = 0x92
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 27] get /create/node5
node5
cZxid = 0x93
ctime = Mon Dec 21 15:32:38 CST 2020
mZxid = 0x93
mtime = Mon Dec 21 15:32:38 CST 2020
pZxid = 0x93
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
四、更新节点
zookeeper中,跟创建节点一样,同样支持同步和异步两种方式更新节点的值:
- setData(String path, byte[] data, int version) // 同步方式
- setData(String path, byte[] data, int version,AsyncCallback.StatCallback callBack, Object ctx) // 异步方式
参数说明如下:
- path - znode路径;
- data - 要存储在指定znode路径中的数据;
- version - znode的当前版本。每当数据更改时,ZooKeeper会更新znode的版本号;
- callBack - 异步回调接口;
- ctx - 传递上下文参数;
案例:
首先得在终端上创建两个节点:
[zk: localhost:2181(CONNECTED) 29] create /set "set"
Created /set
[zk: localhost:2181(CONNECTED) 30] create /set/node1 "node1"
Created /set/node1
[zk: localhost:2181(CONNECTED) 31] create /set/node2 "node2"
Created /set/node2
[zk: localhost:2181(CONNECTED) 32] ls /set
[node2, node1]
package com.wsh.zookeeper.zookeeperapidemo.zookeper;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class ZookeeperUpdate {
/**
* zookeeper服务器地址
*/
private static final String CONNECT_STRING = "192.168.179.133:2181";
/**
* 会话超时时间
*/
private static final int SESSION_TIMEOUT = 3000;
/**
* 日志
*/
private static final Logger logger = LoggerFactory.getLogger(ZookeeperDemo.class);
/**
* zookeeper对象
*/
private static ZooKeeper zooKeeper = null;
static {
//创建zookeeper连接
try {
zooKeeper = new ZooKeeper(
CONNECT_STRING, SESSION_TIMEOUT, new CustomWatcher());
} catch (IOException e) {
e.printStackTrace();
}
}
void set01() {
try {
/**
* setData(String path, byte[] data, int version)
* path: 节点的路径
* data: 节点更新值
* version: 节点版本号 -1代表版本号不作为修改条件
*/
zooKeeper.setData("/set/node1", "node11".getBytes(), -1);
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
void set02() {
zooKeeper.setData("/set/node2", "node22".getBytes(), -1, new AsyncCallback.StatCallback() {
@Override
public void processResult(int rc, String path, Object ctx, Stat stat) {
// 0 代表修改成功
logger.info("rc: {}", rc);
// 节点路径
logger.info("path: {}", path);
// 上下文参数
logger.info("ctx: {}", ctx);
// 节点属性信息
logger.info("version: {}", stat.getVersion());
logger.info("ctime: {}", stat.getCtime());
}
}, "this is callback params");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ZookeeperUpdate zookeeperUpdate = new ZookeeperUpdate();
zookeeperUpdate.set01();
zookeeperUpdate.set02();
}
static class CustomWatcher implements Watcher {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
System.out.println("连接创建成功!");
}
}
}
}
运行程序,我们通过终端查看节点的值是否更新成功:
[zk: localhost:2181(CONNECTED) 33] get /set/node1
node11
cZxid = 0x96
ctime = Mon Dec 21 16:25:57 CST 2020
mZxid = 0x99
mtime = Mon Dec 21 16:28:38 CST 2020
pZxid = 0x96
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
[zk: localhost:2181(CONNECTED) 34] get /set/node2
node22
cZxid = 0x97
ctime = Mon Dec 21 16:26:04 CST 2020
mZxid = 0x9a
mtime = Mon Dec 21 16:28:38 CST 2020
pZxid = 0x97
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
可见,节点的值成功更新。
五、删除节点
zookeeper删除节点同样直接两种方式:同步方式和异步方式。
- delete(String path, int version) // 同步方式
- delete(String path, int version, AsyncCallback.VoidCallback callBack, Object ctx) // 异步方式
参数说明如下:
- path - 节点路径;
- version - 节点数据的当前版本;
- callBack - 异步回调接口;
- ctx - 异步回调上下文参数;
案例:
首先终端上面创建两个节点:
[zk: localhost:2181(CONNECTED) 36] create /delete "delete"
Created /delete
[zk: localhost:2181(CONNECTED) 37] create /delete/node1 "node1"
Created /delete/node1
[zk: localhost:2181(CONNECTED) 38] create /delete/node2 "node2"
Created /delete/node2
package com.wsh.zookeeper.zookeeperapidemo.zookeper;
import org.apache.zookeeper.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class ZookeeperDelete {
/**
* zookeeper服务器地址
*/
private static final String CONNECT_STRING = "192.168.179.133:2181";
/**
* 会话超时时间
*/
private static final int SESSION_TIMEOUT = 3000;
/**
* 日志
*/
private static final Logger logger = LoggerFactory.getLogger(ZookeeperDemo.class);
/**
* zookeeper对象
*/
private static ZooKeeper zooKeeper = null;
static {
//创建zookeeper连接
try {
zooKeeper = new ZooKeeper(
CONNECT_STRING, SESSION_TIMEOUT, new CustomWatcher());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 同步方式
*/
void delete01() {
try {
/**
* delete(String path, int version)
* path: 节点路径
* version: 节点数据版本号
*/
zooKeeper.delete("/delete/node1", -1);
} catch (InterruptedException | KeeperException e) {
e.printStackTrace();
}
}
/**
* 异步方式
*/
void delete02() {
zooKeeper.delete("/delete/node2", -1, new AsyncCallback.VoidCallback() {
@Override
public void processResult(int rc, String path, Object ctx) {
// 0代表删除成功
logger.info("rc: {}", rc);
// 节点的路径
logger.info("path: {}", path);
// 上下文参数对象
logger.info("ctx: {}", ctx);
}
}, "this is callback params");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ZookeeperDelete zookeeperDelete = new ZookeeperDelete();
zookeeperDelete.delete01();
zookeeperDelete.delete02();
}
static class CustomWatcher implements Watcher {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
System.out.println("连接创建成功!");
}
}
}
}
运行程序,执行完后,使用终端查看节点是否已删除:
[zk: localhost:2181(CONNECTED) 39] ls /delete
[]
六、查看节点
在zookeeper中,查看节点同样支持同步和异步方式。
- getData(String path, boolean b, Stat stat) // 同步方式
- getData(String path, boolean b,AsyncCallback.DataCallback callBack,Object ctx) // 异步方式
参数说明:
- path - znode路径;
- b - 是否使用连接对象中注册的监视器;
- stat - 返回znode的元数据;
- callBack - 异步回调接口;
- ctx - 传递上下文参数;
案例:
首先使用终端创建两个节点:
[zk: localhost:2181(CONNECTED) 40] create /read "read"
Created /read
[zk: localhost:2181(CONNECTED) 41] create /read/node1 "node1"
Created /read/node1
[zk: localhost:2181(CONNECTED) 42] create /read/node2 "node2"
Created /read/node2
[zk: localhost:2181(CONNECTED) 43] ls /read
[node2, node1]
package com.wsh.zookeeper.zookeeperapidemo.zookeper;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class ZookeeperRead {
/**
* zookeeper服务器地址
*/
private static final String CONNECT_STRING = "192.168.179.133:2181";
/**
* 会话超时时间
*/
private static final int SESSION_TIMEOUT = 3000;
/**
* 日志
*/
private static final Logger logger = LoggerFactory.getLogger(ZookeeperDemo.class);
/**
* zookeeper对象
*/
private static ZooKeeper zooKeeper = null;
static {
//创建zookeeper连接
try {
zooKeeper = new ZooKeeper(
CONNECT_STRING, SESSION_TIMEOUT, new CustomWatcher());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 同步方式
*/
void read01() {
try {
Stat stat = new Stat();
byte[] data = zooKeeper.getData("/read/node1", false, stat);
logger.info("data :{}", new String(data));
logger.info("version :{}", stat.getVersion());
logger.info("ctime :{}", stat.getCtime());
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
/**
* 异步方式
*/
void read02() {
zooKeeper.getData("/read/node2", false, new AsyncCallback.DataCallback() {
@Override
public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) {
// 0代表读取成功
logger.info("rc :{}", rc);
// 节点的路径
logger.info("path :{}", path);
// 上下文参数对象
logger.info("ctx :{}", ctx);
// 数据
logger.info("data :{}", new String(data));
// 属性对象
logger.info("version :{}", stat.getVersion());
}
}, "this is callback params");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ZookeeperRead zookeeperRead = new ZookeeperRead();
zookeeperRead.read01();
zookeeperRead.read02();
}
static class CustomWatcher implements Watcher {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
System.out.println("连接创建成功!");
}
}
}
}
观察后端日志,可以看到,成功读取到节点的数据。
16:46:35.429 [main-EventThread] INFO com.wsh.zookeeper.zookeeperapidemo.zookeper.ZookeeperDemo - rc :0
16:46:35.429 [main-EventThread] INFO com.wsh.zookeeper.zookeeperapidemo.zookeper.ZookeeperDemo - path :/read/node2
16:46:35.429 [main-EventThread] INFO com.wsh.zookeeper.zookeeperapidemo.zookeper.ZookeeperDemo - ctx :this is callback params
16:46:35.429 [main-EventThread] INFO com.wsh.zookeeper.zookeeperapidemo.zookeper.ZookeeperDemo - data :node2
16:46:35.429 [main-EventThread] INFO com.wsh.zookeeper.zookeeperapidemo.zookeper.ZookeeperDemo - version :0
七、查看子节点
- getChildren(String path, boolean b) // 同步方式
- getChildren(String path, boolean b,AsyncCallback.ChildrenCallback callBack,Object ctx) // 异步方式
参数说明如下:
- path - Znode路径;
- b - 是否使用连接对象中注册的监视器;
- callBack - 异步回调接口;
- ctx - 传递上下文参数;
案例:
首先在终端创建两个节点:
[zk: localhost:2181(CONNECTED) 44] create /children "children"
Created /children
[zk: localhost:2181(CONNECTED) 45] create /children/node1 "node1"
Created /children/node1
[zk: localhost:2181(CONNECTED) 46] create /children/node2 "node2"
Created /children/node2
[zk: localhost:2181(CONNECTED) 47] ls /children
[node2, node1]
package com.wsh.zookeeper.zookeeperapidemo.zookeper;
import org.apache.zookeeper.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
public class ZookeeperChildren {
/**
* zookeeper服务器地址
*/
private static final String CONNECT_STRING = "192.168.179.133:2181";
/**
* 会话超时时间
*/
private static final int SESSION_TIMEOUT = 3000;
/**
* 日志
*/
private static final Logger logger = LoggerFactory.getLogger(ZookeeperDemo.class);
/**
* zookeeper对象
*/
private static ZooKeeper zooKeeper = null;
static {
//创建zookeeper连接
try {
zooKeeper = new ZooKeeper(
CONNECT_STRING, SESSION_TIMEOUT, new CustomWatcher());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 同步方式
*/
void getChildren01() {
List<String> children;
try {
children = zooKeeper.getChildren("/children", false);
for (String child : children) {
logger.info("节点:" + child);
}
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
/**
* 异步方式
*/
void getChildren02() {
zooKeeper.getChildren("/children", false, new AsyncCallback.ChildrenCallback() {
@Override
public void processResult(int rc, String path, Object ctx, List<String> children) {
// 0代表读取成功
logger.info("rc :{}", rc);
// 节点的路径
logger.info("path :{}", path);
// 上下文参数对象
logger.info("ctx :{}", ctx);
for (String child : children) {
logger.info("节点:" + child);
}
}
}, "this is callback params");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ZookeeperChildren zookeeperChildren = new ZookeeperChildren();
zookeeperChildren.getChildren01();
zookeeperChildren.getChildren02();
}
static class CustomWatcher implements Watcher {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
System.out.println("连接创建成功!");
}
}
}
}
观察后端日志,可见成功查询出节点对应的所有子节点信息:
16:54:08.926 [main] INFO com.wsh.zookeeper.zookeeperapidemo.zookeper.ZookeeperDemo - 节点:node2
16:54:08.926 [main] INFO com.wsh.zookeeper.zookeeperapidemo.zookeper.ZookeeperDemo - 节点:node1
16:54:08.928 [main-SendThread(192.168.179.133:2181)] DEBUG org.apache.zookeeper.ClientCnxn - Reading reply sessionid:0x1000003ec2f000a, packet:: clientPath:/children serverPath:/children finished:false header:: 2,8 replyHeader:: 2,171,0 request:: '/children,F response:: v{'node2,'node1}
16:54:08.929 [main-EventThread] INFO com.wsh.zookeeper.zookeeperapidemo.zookeper.ZookeeperDemo - rc :0
16:54:08.930 [main-EventThread] INFO com.wsh.zookeeper.zookeeperapidemo.zookeper.ZookeeperDemo - path :/children
16:54:08.930 [main-EventThread] INFO com.wsh.zookeeper.zookeeperapidemo.zookeper.ZookeeperDemo - ctx :this is callback params
16:54:08.930 [main-EventThread] INFO com.wsh.zookeeper.zookeeperapidemo.zookeper.ZookeeperDemo - 节点:node2
16:54:08.930 [main-EventThread] INFO com.wsh.zookeeper.zookeeperapidemo.zookeper.ZookeeperDemo - 节点:node1
八、总结
本篇文章主要总结了我们如何使用原生API去连接zookeeper服务器端,然后使用客户端API去进行一些操作,如新增节点、修改节点等。当然还有一些开源框架对zookeeper进行了封装,如Curator,它解决了很多 Zookeeper 客户端非常底层的细节开发工作,包括连接重连、反复注册 Watcher 和 NodeExistsException 异常等,感兴趣的小伙伴可以去学习一下。