客户端注册 Watcher 实现 ? 思维导图 代码示例(java 架构)

客户端注册 Watcher 是 ZooKeeper 中一个非常重要的操作,它允许客户端监听特定 ZNode 的变化。通过注册 Watcher,客户端可以在 ZNode 发生创建、删除、数据变更或子节点列表变更等事件时得到通知。下面我将提供一个关于如何在 ZooKeeper 客户端中注册 Watcher 的思维导图大纲,并给出详细的 Java 代码示例来展示这个过程。

思维导图大纲

1. 注册 Watcher 概述
  • 定义
    • 在 ZooKeeper 客户端中设置监听器以接收指定 ZNode 的状态变化通知
  • 目的
    • 实现对 ZooKeeper 状态变化的实时响应
  • 特点
    • 一次性:每个 Watcher 只能触发一次,之后需要重新注册
    • 异步:事件处理在独立线程中进行,不阻塞主流程
2. Watcher 注册方式
  • 获取数据时注册
    • getData(path, watcher, stat)
  • 列出子节点时注册
    • getChildren(path, watcher)
  • 检查节点存在时注册
    • exists(path, watcher)
3. Watcher 类型与触发条件
  • 节点数据变化(Node Data Changed)
    • 监听指定 ZNode 的数据更新
  • 子节点列表变化(Children List Changed)
    • 监听指定 ZNode 的子节点增删
  • 节点创建/删除(Node Created/Deleted)
    • 监听 ZNode 的创建或移除
4. 处理 Watcher 事件
  • 实现 Watcher 接口
    • 重写 process(WatchedEvent event) 方法
  • 事件类型
    • EventType.NodeCreated, EventType.NodeDeleted, EventType.NodeDataChanged, EventType.ChildChanged
  • 事件路径
    • 使用 event.getPath() 获取触发事件的 ZNode 路径
  • 事件状态
    • 使用 event.getState() 检查连接状态
5. Watcher 应用场景
  • 配置管理
    • 动态更新应用配置
  • 服务发现
    • 监控服务实例的变化
  • 分布式锁
    • 检测锁的释放情况
6. 注意事项
  • 重新注册 Watcher
    • 每次触发后需重新注册,以确保持续监听
  • 避免循环依赖
    • 防止 Watcher 触发导致无限递归调用
  • 处理并发
    • 在多线程环境中正确同步访问共享资源

Java 代码示例

创建自定义 Watcher 并注册到 ZooKeeper
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

public class CustomWatcher implements Watcher {

    private final ZooKeeper zk;

    public CustomWatcher(ZooKeeper zk) {
        this.zk = zk;
    }

    @Override
    public void process(WatchedEvent event) {
        System.out.println("Received event: " + event.getType() + " on path: " + event.getPath());

        // 根据事件类型采取行动
        if (event.getType() == Event.EventType.NodeDataChanged) {
            try {
                // 重新注册 Watcher 以便继续监听
                byte[] data = zk.getData(event.getPath(), this, null);
                System.out.println("Node data changed to: " + new String(data));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
连接到 ZooKeeper 并注册 Watcher
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;

public class ZKRegisterWatcherExample {

    private static final String ZOOKEEPER_ADDRESS = "localhost:2181";
    private static final int SESSION_TIMEOUT = 3000;

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        // 创建 ZooKeeper 实例
        CountDownLatch connectedSignal = new CountDownLatch(1);
        ZooKeeper zk = new ZooKeeper(ZOOKEEPER_ADDRESS, SESSION_TIMEOUT, event -> {
            if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
                connectedSignal.countDown();
            }
        });

        // 等待连接建立
        connectedSignal.await();

        try {
            // 创建持久节点
            String path = "/example/watched-node";
            zk.create(path, "Initial Data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            System.out.println("Created node: " + path);

            // 设置 Watcher 监听节点数据变化
            byte[] initialData = zk.getData(path, new CustomWatcher(zk), null);
            System.out.println("Initial node data: " + new String(initialData));

            // 更新节点数据,这会触发 Watcher
            zk.setData(path, "Updated Data".getBytes(), -1); // -1 表示忽略版本号

            // 模拟等待一段时间以观察变化
            Thread.sleep(60000);
        } finally {
            zk.close();
        }
    }
}
使用 Curator Framework 简化 Watcher 注册

Curator 是一个更高层次的 ZooKeeper 客户端库,它简化了许多常见的任务,包括 Watcher 的使用。以下是使用 Curator 来注册 Watcher 的例子。

首先添加 Maven 依赖:

<dependency>
    <groupId>org.apache.curatorgroupId>
    <artifactId>curator-frameworkartifactId>
    <version>5.2.0version> 
dependency>
<dependency>
    <groupId>org.apache.curatorgroupId>
    <artifactId>curator-recipesartifactId>
    <version>5.2.0version> 
dependency>

然后编写 Curator 示例代码:

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.retry.ExponentialBackoffRetry;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CuratorRegisterWatcherExample {

    private static final String ZOOKEEPER_ADDRESS = "localhost:2181";

    public static void main(String[] args) throws Exception {
        // 创建 Curator 客户端实例
        CuratorFramework client = CuratorFrameworkFactory.newClient(
            ZOOKEEPER_ADDRESS,
            new ExponentialBackoffRetry(1000, 3)
        );
        client.start();

        // 等待客户端启动完成
        Thread.sleep(1000);

        try {
            // 创建持久节点
            String path = "/example/curator-watched-node";
            client.create().creatingParentsIfNeeded().forPath(path, "Initial Data".getBytes());
            System.out.println("Created node using Curator: " + path);

            // 使用 PathChildrenCache 来监听节点及其子节点的变化
            ExecutorService executor = Executors.newSingleThreadExecutor();
            PathChildrenCache cache = new PathChildrenCache(client, path, true);
            cache.getListenable().addListener((client1, event) -> {
                switch (event.getType()) {
                    case CHILD_ADDED:
                        System.out.println("Child added: " + event.getData().getPath());
                        break;
                    case CHILD_UPDATED:
                        System.out.println("Child updated: " + event.getData().getPath());
                        break;
                    case CHILD_REMOVED:
                        System.out.println("Child removed: " + event.getData().getPath());
                        break;
                }
            }, executor);

            cache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);

            // 模拟等待一段时间以观察变化
            Thread.sleep(60000);
        } finally {
            client.close();
        }
    }
}

详细步骤说明

  1. 连接 ZooKeeper

    • 使用 ZooKeeper 构造函数创建客户端实例,并传入 ZooKeeper 地址和会话超时时间。
    • 提供一个匿名内部类实现 Watcher 接口,用于监听连接状态的变化,特别是连接成功 (SyncConnected) 的时刻。
  2. 创建 ZNode

    • 使用 create 方法创建一个新的持久节点(或其他类型的节点),并初始化一些数据。
  3. 注册 Watcher

    • 使用 getData, getChildren, 或 exists 方法时传递一个实现了 Watcher 接口的对象作为参数。
    • 这个 Watcher 对象会在对应的 ZNode 上设置监听,当发生特定事件时,process 方法会被调用。
  4. 处理 Watcher 事件

    • process 方法中根据 WatchedEvent 的类型执行相应的逻辑。
    • 如果需要持续监听同一个 ZNode,则应在事件处理完毕后再次为该 ZNode 注册 Watcher。
  5. 使用 Curator 简化操作

    • Curator 提供了更高级别的 API,如 PathChildrenCache,它可以自动处理 Watcher 的注册和重新注册,并且能够方便地监听 ZNode 及其子节点的变化。

注意事项

  • 依赖管理:确保项目中包含了正确的 ZooKeeper 和 Curator 库依赖。
  • 安全性:考虑启用身份验证和加密通信以保护 ZooKeeper 集群。
  • 容错处理:设计应用程序时考虑到网络分区、节点失败等情况下的行为。
  • 性能优化:根据实际负载调整会话超时时间和线程池大小等参数。

希望这个思维导图大纲和代码示例能帮助你更好地理解和实现 ZooKeeper 客户端中 Watcher 的注册。如果你有更具体的问题或需要进一步的帮助,请随时告诉我。

你可能感兴趣的:(java-zookeeper,zookeeper,java)