上述代码是一个JUnit测试类中的@Before方法,用于在测试之前初始化ZooKeeper客户端对象,具体来说,它完成以下几个操作:

  1. 创建一个ZooKeeper客户端对象,连接到指定的ZooKeeper服务器
  2. 注册一个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 来监听指定路径下子节点的变化,当子节点发生变化时就会触发 Watcherprocess 方法。由于 Watcher 是在 ZooKeeper 客户端的后台线程中运行的,因此即使主线程没有调用任何方法,Watcher 仍然在后台监听着指定路径下子节点的变化。当 ZooKeeper 客户端建立连接时,它会向 ZooKeeper 服务器发送一个心跳消息,以保持连接。在这个过程中,ZooKeeper 服务器会检查指定路径下子节点的变化情况,并将变化通知给客户端。因此,即使主线程没有调用任何方法,客户端也会接收到变化通知,并触发 Watcherprocess 方法。

ZooKeeper 客户端会在后台启动一个线程来处理网络通信和事件通知,这个线程负责与 ZooKeeper 服务器保持连接,并接收、处理来自服务器的事件通知,包括节点变化、连接状态变化等。当客户端注册了 Watcher 后,后台线程就会在收到事件通知时触发 Watcher 的回调方法,从而实现对节点变化等事件的监听和处理。

在创建 ZooKeeper 客户端对象时,会启动一个线程来处理与服务器的通信和事件通知。这个线程会一直运行,直到客户端关闭或失去连接。因此,即使主线程没有调用任何方法,后台线程仍然在运行,监听着与服务器的通信和事件通知。

为什么process方法在程序刚开始会执行一次呢?

ZooKeeper 客户端连接到服务器时,会发送一个连接请求,如果连接成功,则会触发 Watcherprocess 方法一次,此时会返回当前指定节点的子节点列表。这是因为在创建 ZooKeeper 客户端对象时,Watcher 的构造函数中调用了 getChildren 方法,这个方法会返回当前指定节点的子节点列表,并触发 Watcherprocess 方法。

在上面的代码中,Watcher 是在 ZooKeeper 客户端对象创建时就被注册的,因此在客户端连接到服务器时,Watcher 就会被触发一次,返回当前指定节点的子节点列表。

Zookeeper的监听器不是只监听一次吗,为什么上述代码可以多次监听?

这是因为在初始化 ZooKeeper 客户端对象时会执行一次 process 方法,然后在该方法中调用了 zkClient.getChildren() 又重新注册了一次监听事件,事实上 ZooKeeper 只能监听一次,但是在监听方法中又调用了方法重新进行了注册,所以还可以继续监听。

当调用zkClient.getChildren()方法时,如果之前已经注册了相同的Watcher对象,则会覆盖之前的Watcher,并重新注册新的Watcher,以持续监听指定节点的变化。

在上述代码中,当调用zkClient.getChildren()方法时,会重新注册之前已经注册过的Watcher对象,以持续监听指定节点的变化。因此,即使在测试过程中,指定节点的子节点列表发生了多次变化,Watcher也会持续监听并执行相应的回调方法。

04-03 22:36