上述代码是一个JUnit测试类中的@Before方法,用于在测试之前初始化ZooKeeper客户端对象,具体来说,它完成以下几个操作:
- 创建一个ZooKeeper客户端对象,连接到指定的ZooKeeper服务器
- 注册一个Watcher监听器,当指定路径的子节点发生变化时,会触发Watcher的process方法,在process方法中,会打印出节点变化前后的子节点列表
该段代码的主要作用是演示ZooKeeper中Watcher的使用方法,当指定节点的子节点发生变化时,会触发Watcher的process方法,从而实现对节点变化的监听和处理。
package com.guang.zk;
import org.apache.zookeeper.*;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
public class ZkClient {
private String connectString = "hadoop102,hadoop103,hadoop104"; // 连接的服务器端口号
private int sessionTimeout = 2000; // 会话连接延时
private ZooKeeper zkClient; // zookeeper客户端
@Before
public void init() throws IOException {
// 初始化一个zookeeper客户端对象
zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("----------");
List<String> children = null;
try {
children = zkClient.getChildren("/", true);
} catch (KeeperException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for (String child : children) {
System.out.println(child);
}
System.out.println("----------");
}
});
}
// 获取子节点
@Test
public void getChildren() throws InterruptedException, KeeperException {
// 保持主线程不结束
Thread.sleep(Long.MAX_VALUE);
}
}
下面是一些经典的问题,这里统一做出了回答
为什么主线程没有调用任何函数,也会进行监听呢?
上面代码创建 ZooKeeper
客户端对象时,注册了一个 Watcher
来监听指定路径下子节点的变化,当子节点发生变化时就会触发 Watcher
的 process
方法。由于 Watcher
是在 ZooKeeper
客户端的后台线程中运行的,因此即使主线程没有调用任何方法,Watcher
仍然在后台监听着指定路径下子节点的变化。当 ZooKeeper
客户端建立连接时,它会向 ZooKeeper
服务器发送一个心跳消息,以保持连接。在这个过程中,ZooKeeper
服务器会检查指定路径下子节点的变化情况,并将变化通知给客户端。因此,即使主线程没有调用任何方法,客户端也会接收到变化通知,并触发 Watcher
的 process
方法。
ZooKeeper
客户端会在后台启动一个线程来处理网络通信和事件通知,这个线程负责与 ZooKeeper
服务器保持连接,并接收、处理来自服务器的事件通知,包括节点变化、连接状态变化等。当客户端注册了 Watcher
后,后台线程就会在收到事件通知时触发 Watcher
的回调方法,从而实现对节点变化等事件的监听和处理。
在创建 ZooKeeper
客户端对象时,会启动一个线程来处理与服务器的通信和事件通知。这个线程会一直运行,直到客户端关闭或失去连接。因此,即使主线程没有调用任何方法,后台线程仍然在运行,监听着与服务器的通信和事件通知。
为什么process方法在程序刚开始会执行一次呢?
在 ZooKeeper
客户端连接到服务器时,会发送一个连接请求,如果连接成功,则会触发 Watcher
的 process
方法一次,此时会返回当前指定节点的子节点列表。这是因为在创建 ZooKeeper
客户端对象时,Watcher
的构造函数中调用了 getChildren
方法,这个方法会返回当前指定节点的子节点列表,并触发 Watcher
的 process
方法。
在上面的代码中,Watcher
是在 ZooKeeper
客户端对象创建时就被注册的,因此在客户端连接到服务器时,Watcher
就会被触发一次,返回当前指定节点的子节点列表。
Zookeeper的监听器不是只监听一次吗,为什么上述代码可以多次监听?
这是因为在初始化 ZooKeeper
客户端对象时会执行一次 process
方法,然后在该方法中调用了 zkClient.getChildren()
又重新注册了一次监听事件,事实上 ZooKeeper
只能监听一次,但是在监听方法中又调用了方法重新进行了注册,所以还可以继续监听。
当调用zkClient.getChildren()
方法时,如果之前已经注册了相同的Watcher
对象,则会覆盖之前的Watcher
,并重新注册新的Watcher
,以持续监听指定节点的变化。
在上述代码中,当调用zkClient.getChildren()
方法时,会重新注册之前已经注册过的Watcher
对象,以持续监听指定节点的变化。因此,即使在测试过程中,指定节点的子节点列表发生了多次变化,Watcher
也会持续监听并执行相应的回调方法。