1、Zookeeper API

1.1、描述

在 ZooKeeper 中,Watcher 是一次性的,不会自动重新注册因此,如果你希望在特定事件(如节点数据变化)发生后继续监听其他事件(如节点删除),你需要在每次事件触发时重新注册 Watcher

1.2、示例

首先,确保你在项目中添加了 Zookeeper 的依赖:

<dependency>
  <groupId>org.apache.zookeeper</groupId>
  <artifactId>zookeeper</artifactId>
  <version>3.6.3</version>
</dependency>

完整示例代码

import org.apache.zookeeper.*;
import org.apache.zookeeper.AsyncCallback.StatCallback;
import org.apache.zookeeper.AsyncCallback.StringCallback;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;

public class ZooKeeperExample {

    private static ZooKeeper zk;
    private static final String ZK_SERVER = "localhost:2181";
    private static final int SESSION_TIMEOUT = 3000;

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        // 创建 ZooKeeper 客户端
        zk = new ZooKeeper(ZK_SERVER, SESSION_TIMEOUT, new MyWatcher());

        // 创建节点
        zk.create("/example", "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, new MyStringCallback(), null);

        // 检查节点状态并设置 Watcher
        zk.exists("/example", true, new MyStatCallback(), null);

        // 触发节点变化事件
        zk.setData("/example", "newData".getBytes(), -1);

        // 删除节点,验证 Watcher 是否会监听删除事件
        Thread.sleep(2000);
        zk.delete("/example", -1);

        // 保持程序运行以观察回调
        Thread.sleep(10000);

        // 关闭 ZooKeeper 客户端
        zk.close();
    }

    // Watcher 实现类
    static class MyWatcher implements Watcher {
        @Override
        public void process(WatchedEvent event) {
            System.out.println("Watcher received event: " + event);
            try {
                if (event.getType() == Event.EventType.NodeDataChanged || event.getType() == Event.EventType.NodeDeleted) {
                    // 重新设置 Watcher
                    zk.exists(event.getPath(), true);
                }
            } catch (KeeperException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // StatCallback 实现类
    static class MyStatCallback implements StatCallback {
        @Override
        public void processResult(int rc, String path, Object ctx, Stat stat) {
            if (rc == 0) {
                System.out.println("Node exists: " + path + ", stat: " + stat);
            } else {
                System.out.println("Node does not exist: " + path);
            }
        }
    }

    // StringCallback 实现类
    static class MyStringCallback implements StringCallback {
        @Override
        public void processResult(int rc, String path, Object ctx, String name) {
            if (rc == 0) {
                System.out.println("Node created: " + name);
            } else {
                System.out.println("Node creation failed: " + KeeperException.Code.get(rc));
            }
        }
    }
}

1.3、结果验证

  • 首次触发 Watcher:当第一次修改节点数据时,Watcher 会触发并打印事件信息。
  • 删除节点:删除节点时,Watcher 会再次触发并打印事件信息,因为在 process 方法中重新注册了 Watcher。

通过这个完整的示例代码,你可以验证 Watcher 是一次性的,并且需要在每次触发后重新注册以继续监听其他事件,包括节点删除事件

2、Apache Curator

2.1、描述

Apache Curator 是一个用于简化 ZooKeeper 客户端开发的 Java 库。它提供了更高层次的 API 来处理 ZooKeeper 的常见任务,例如连接管理、会话管理、重试机制等。以下是一个完整的实例,演示了如何使用 Curator 库来进行回调和监听节点事件

2.2、示例

确保在你的项目中添加了 Curator 的依赖:

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>5.2.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>5.2.0</version>
</dependency>

完整示例代码

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
import org.apache.curator.framework.recipes.cache.TreeCacheListener;
import org.apache.curator.framework.state.ConnectionState;
import org.apache.curator.framework.state.ConnectionStateListener;
import org.apache.curator.retry.ExponentialBackoffRetry;

public class TreeCacheExample {

    private static final String ZK_SERVER = "localhost:2181";
    private static final String ZNODE_PATH = "/example";

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

        client.getConnectionStateListenable().addListener(new ConnectionStateListener() {
            @Override
            public void stateChanged(CuratorFramework curatorFramework, ConnectionState newState) {
                switch (newState) {
                    case CONNECTED:
                        System.out.println("Registry connected");
                        break;
                    case LOST:
                        System.out.println("Registry disconnected");
                        break;
                    case RECONNECTED:
                        System.out.println("Registry reconnected");
                        break;
                    case SUSPENDED:
                        System.out.println("Registry suspended");
                        break;
                    default:
                        break;
                }
            }
        });

        // 创建 TreeCache
        TreeCache treeCache = new TreeCache(client, ZNODE_PATH);

        // 添加 TreeCache 监听器
        TreeCacheListener listener = new TreeCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
                switch (event.getType()) {
                    case NODE_ADDED:
                        System.out.println("Node added: " + event.getData().getPath());
                        break;
                    case NODE_UPDATED:
                        System.out.println("Node updated: " + event.getData().getPath());
                        break;
                    case NODE_REMOVED:
                        System.out.println("Node removed: " + event.getData().getPath());
                        break;
                    default:
                        break;
                }
            }
        };
        treeCache.getListenable().addListener(listener);

        // 启动 TreeCache
        treeCache.start();

        // 进行一些节点操作以触发事件
        client.create().forPath(ZNODE_PATH + "/child", "data".getBytes());
        client.setData().forPath(ZNODE_PATH + "/child", "newData".getBytes());
        client.delete().forPath(ZNODE_PATH + "/child");

        // 保持程序运行以观察回调
        Thread.sleep(10000);

        // 关闭 TreeCache 和 Curator 客户端
        treeCache.close();
        client.close();
    }
}

2.3、结果验证

  • ConnectionStateListener 监听Zookeeper的链接状态
  • TreeCacheListener 监听Zookeeper 路径的事件

3、对比

TreeCache 和 Watcher 是 Apache Curator 框架和 Apache ZooKeeper 中的两种不同的监听机制,用于监视 ZooKeeper 集群中的节点变化。它们的注册和使用方式有所不同,原因主要在于它们的设计目的和工作机制

Watcher

  • 作用: Watcher 是 ZooKeeper 提供的一种机制,用于监视节点的变化。每次客户端对 ZooKeeper 进行读取操作时,可以附带一个 Watcher,当该节点发生变化时,ZooKeeper 会通知客户端。
  • 多次注册: Watcher 是一次性的,即每次触发后就失效了。如果客户端需要继续监听该节点的变化,则需要重新注册 Watcher。因此,Watcher 的设计是为了轻量级、精细粒度的监听。

TreeCache

  • 作用: TreeCache 是 Apache Curator 提供的一个高级缓存机制,用于监视一个节点及其子节点的变化。它不仅仅是监视单个节点,而是监视整个子树的变化,并将数据缓存在本地。
  • 一次注册: TreeCache 通过一次注册,可以持续监视整个子树的变化,并保持缓存的更新。这是因为 TreeCache 维护了一个本地缓存,能够自动处理 ZooKeeper 的事件通知,保持缓存数据的一致性。它通过内部的机制,自动重新注册 Watcher,以保证对子树的变化持续监控。因此,用户不需要手动多次注册

为什么 TreeCache 不像 Watcher 一样多次注册?

1.    设计目的: TreeCache 的设计目的是为了方便地监视和缓存整个子树的变化,提供一个高效、易用的接口来处理复杂的节点监控需求。而 Watcher 是为了提供一个更底层的、精细粒度的节点监控机制,适合一些简单、轻量级的使用场景。
2.    内部实现: TreeCache 内部会自动管理 Watcher 的注册和事件处理,因此用户不需要手动多次注册 Watcher。而 Watcher 需要用户手动管理和重新注册,以确保对节点的持续监控。
3.    使用场景: 对于需要监控多个节点或整个子树变化的场景,TreeCache 提供了更高效和便捷的解决方案,而不需要用户频繁注册 Watcher。

总结来说,TreeCache 和 Watcher 是为了满足不同需求而设计的。TreeCache 通过一次注册,自动管理对节点及子节点的持续监控和缓存更新,而 Watcher 需要用户手动多次注册以实现持续监控


journey
32 声望20 粉丝