ZooKeeper Watch 回调事件丢失

ZooKeeper 在官网着重提示在使用 Watch 的时候要注意:
  • Watch 是一次性的,如果 watch 事件发生了,还想 watch 需要再设置新的watch
  • 因为 watch 的一次性,再次注册 watch 的网络延迟,所以 znode 每次变更不可能都 watch 到
  • 一个 watch 对象或者函数/上下文对(pair),只会触发一次。比如,如果相同的 watch 对象注册了 exist 和 getData 调用在相同文件,并且文件已经被删除,watch 对象只会在文件被删除触发一次
  • 当你与一个服务断开(比如zk服务宕机),你将不会获得任何 watch,直到连接重连。因此,session 事件将会发送给所有 watch 处理器。使用 session 事件进入一个安全模式:当断开连接的时候将不会收到任何事件,因此您的进程应该以该模式保守运行

用代码表现一下 Watch 回调事件丢失:

package com.wenniuwuren.zookeeper.curator.watch;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.NodeCache;
import org.apache.curator.framework.recipes.cache.NodeCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;


import com.wenniuwuren.Constants;

/**
 * Zookeeper之Watcher监听事件丢失
 *
 * @author wenniuwuren
 */
public class WatchLost {

    static String path = "/test/watchlost1";
    static CuratorFramework client = CuratorFrameworkFactory.builder().connectString(Constants.ZK_HOST)
            .retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();

    public static void main(String[] args) {
        try {
            client.start();

            final NodeCache nodeCache = new NodeCache(client, path);
            nodeCache.start();

            if (client.checkExists().forPath(path) == null)
                client.create().forPath(path, "0".getBytes());

            nodeCache.getListenable().addListener(new NodeCacheListener() {
                @Override
                public void nodeChanged() throws Exception {
                    if (nodeCache.getCurrentData() == null) {
                        System.out.println("节点被删除");
                    } else {
                        System.out.println("节点当前内容为:" + new String(nodeCache.getCurrentData().getData()));
                    }

                }
            });

            client.setData().forPath(path, "1".getBytes());
            client.setData().forPath(path, "2".getBytes());
            client.setData().forPath(path, "3".getBytes());
            client.setData().forPath(path, "4".getBytes());
            client.setData().forPath(path, "5".getBytes());
            client.setData().forPath(path, "6".getBytes());
            client.setData().forPath(path, "7".getBytes());
            client.setData().forPath(path, "8".getBytes());
            client.setData().forPath(path, "9".getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}

多次的运行结果:

运行结果一:
节点当前内容为:9
节点当前内容为:3
节点当前内容为:4
节点当前内容为:5
节点当前内容为:6
节点当前内容为:7
节点当前内容为:8
节点当前内容为:9

运行结果二:
节点当前内容为:9
节点当前内容为:2
节点当前内容为:3
节点当前内容为:4
节点当前内容为:5
节点当前内容为:6
节点当前内容为:7
节点当前内容为:8
节点当前内容为:9

运行结果三:
节点当前内容为:9
节点当前内容为:3
节点当前内容为:4
节点当前内容为:6
节点当前内容为:7
节点当前内容为:8
节点当前内容为:9

运行几次,可以看到,并不是每次 client 都是收到 watch 回调,会漏掉几次。所以在使用 ZooKeeper Watch 的时候,不能觉得监听回调一定会成功,所以在写代码的时候要注意这一点。

你可能感兴趣的:(ZooKeeper,ZooKeeper,从入门到精通)