Apache zookeeper 为什么';Apache策展人是否启动所有更新?

Apache zookeeper 为什么';Apache策展人是否启动所有更新?,apache-zookeeper,apache-curator,Apache Zookeeper,Apache Curator,请在创建空白的/test/a路径后对Zookeeper服务器运行以下操作 import static java.lang.String.valueOf; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.cache.PathChi

请在创建空白的
/test/a
路径后对Zookeeper服务器运行以下操作

import static java.lang.String.valueOf;

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.PathChildrenCache.StartMode;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.retry.RetryForever;

public class CacheUpdateTest {
    static final String connectString = "127.0.0.1:2181,127.0.0.1:2191,127.0.0.1:2201";
    static volatile boolean stop = false;

    public static void main(String[] args) throws Exception {
        new Listener().start();
        Thread.sleep(1000);
        new Updater().start();
    }

    private static class Listener extends Thread {
        @SuppressWarnings("resource")
        @Override
        public void run() {
            CuratorFramework client = CuratorFrameworkFactory.builder().connectString(connectString).retryPolicy(new RetryForever(100)).build();
            client.start();

            PathChildrenCache cache = new PathChildrenCache(client, "/test", true);
            cache.getListenable().addListener(new PathChildrenCacheListener() {

                @Override
                public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                    if (event.getData() == null || event.getData().getData() == null) return;
                    int newI = Integer.parseInt(new String(event.getData().getData()));
                    System.err.println("Sensed update: " + newI);
                }
            });
            try {
                cache.start(StartMode.BUILD_INITIAL_CACHE);
                Thread.sleep(2000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static class Updater extends Thread {
        @Override
        public void run() {
            try {
                CuratorFramework client = CuratorFrameworkFactory.builder().connectString(connectString).retryPolicy(new RetryForever(100)).build();
                client.start();

                for (int i = 0; i < 10; i++) {
                    // Thread.sleep(100);
                    System.out.println("Updated child: " + i);
                    client.setData().forPath("/test/a", valueOf(i).getBytes());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
当我对它进行注释时,我得到以下输出

Updated child: 0
Updated child: 1
Sensed update: 1 --> Missed 0
Updated child: 2
Updated child: 3
Updated child: 4
Sensed update: 3 --> Missed 2
Updated child: 5
Updated child: 6
Sensed update: 5 --> Missed 4
Updated child: 7
Updated child: 8
Sensed update: 7 --> Missed 6
Updated child: 9
Sensed update: 9 --> Missed 8

为什么我不总是收到所有通知?为什么我没有错过第一个呢?

Curator是一个库,用于简化与Apache Zookeeper的工作。
PathChildrenCache
的工作方式是使用ZK观察器

观察者创建一次性手表。如果观察者收到有关更改(或其订阅的任何其他操作)的通知,则该观察者将使用该观察,并且它必须再次创建一个新的观察,以便在将来继续收到通知

在您的情况下,
PathChildrenCache
正在查找节点中的更改。它的工作方式是等待收到ZK的通知,然后重新创建手表,继续寻找进一步的更改

因为一切都是异步的,所以在收到更改通知之前,数据可能已经更改了很多次。这就是为什么在更新程序中设置延迟时,可以看到所有更改,因为缓存有足够的时间检测更改并在调用新的
setData
之前重新创建手表。当您忽略睡眠时,事情发生得如此之快,以至于缓存错过了一些事件

如需进一步阅读,请查阅有关观察者的官方信息,主要是本节:

由于手表是一次性触发器,并且在获取事件和发送新请求获取手表之间存在延迟,因此您无法可靠地看到ZooKeeper中节点发生的每一次更改。准备好处理znode在获取事件和再次设置手表之间多次更改的情况。(你可能不在乎,但至少意识到它可能发生。)


Curator是一个库,用于简化与ApacheZooKeeper的工作。
PathChildrenCache
的工作方式是使用ZK观察器

观察者创建一次性手表。如果观察者收到有关更改(或其订阅的任何其他操作)的通知,则该观察者将使用该观察,并且它必须再次创建一个新的观察,以便在将来继续收到通知

在您的情况下,
PathChildrenCache
正在查找节点中的更改。它的工作方式是等待收到ZK的通知,然后重新创建手表,继续寻找进一步的更改

因为一切都是异步的,所以在收到更改通知之前,数据可能已经更改了很多次。这就是为什么在更新程序中设置延迟时,可以看到所有更改,因为缓存有足够的时间检测更改并在调用新的
setData
之前重新创建手表。当您忽略睡眠时,事情发生得如此之快,以至于缓存错过了一些事件

如需进一步阅读,请查阅有关观察者的官方信息,主要是本节:

由于手表是一次性触发器,并且在获取事件和发送新请求获取手表之间存在延迟,因此您无法可靠地看到ZooKeeper中节点发生的每一次更改。准备好处理znode在获取事件和再次设置手表之间多次更改的情况。(你可能不在乎,但至少意识到它可能发生。)


但是为什么缓存会错过第一个通知呢?更新程序线程将节点数据设置为0。老实说,我不知道。您是否尝试使用“/test/a”的一些种子数据而不是空数据?肯定是某种竞赛条件。尝试删除
Thread.sleep(2000)缓存初始化后,这可能会有所帮助。很抱歉没有这么有用。通过Zookeeper监视数据更改的API,我看不到从激发事件读取更新节点数据的方法。唯一的办法是通过另一个操作从动物园管理员那里读到它。我相信这解释了无法读取第一个值的原因。坦率地说,我对这种情况感到惊讶。我使用的是Zookeeper v3.4.14。正如您所发现的,ZK不提供更改的数据,只提供其路径。守望者的责任是控制国家。在您的例子中,
PathChildrenCache
有一个映射字段来存储子数据。但是为什么缓存会错过第一个通知呢?更新程序线程将节点数据设置为0。老实说,我不知道。您是否尝试使用“/test/a”的一些种子数据而不是空数据?肯定是某种竞赛条件。尝试删除
Thread.sleep(2000)缓存初始化后,这可能会有所帮助。很抱歉没有这么有用。通过Zookeeper监视数据更改的API,我看不到从激发事件读取更新节点数据的方法。唯一的办法是通过另一个操作从动物园管理员那里读到它。我相信这解释了无法读取第一个值的原因。坦率地说,我对这种情况感到惊讶。我使用的是Zookeeper v3.4.14。正如您所发现的,ZK不提供更改的数据,只提供其路径。守望者的责任是控制国家。在您的例子中,
PathChildrenCache
有一个映射字段来存储子数据。
Updated child: 0
Updated child: 1
Sensed update: 1 --> Missed 0
Updated child: 2
Updated child: 3
Updated child: 4
Sensed update: 3 --> Missed 2
Updated child: 5
Updated child: 6
Sensed update: 5 --> Missed 4
Updated child: 7
Updated child: 8
Sensed update: 7 --> Missed 6
Updated child: 9
Sensed update: 9 --> Missed 8