Curator应用场景之实现事件监听机制与zookeeper原生watcher的监听原理的区别


原生的zookeeper的监听API所实现的方法存在一些缺点,ZooKeeper原生支持通过注册Watcher来进行事件监听,但是其使用并不是特别方便,需要开发人员自己反复注册Watcher,比较繁琐。对于开发者来说后续的开发会考虑的细节比较多。

Curator所实现的方法希望摒弃原声API 的不足,是开发看起来更加的简单,一些重连等操作对开发者变成了透明操作,而不需要去考虑。

Curator引入了Cache来实现对ZooKeeper服务端事件的监听。
Cache是Curator中对事件监听的包装,其对事件的监听其实可以近似看作是一个本地缓存视图和远程ZooKeeper视图的对比过程。同时Curator能够自动为开发人员处理反复注册监听,从而大大简化了原生API开发的繁琐过程。Cache分为两类监听类型:节点监听和子节点监听。

一、Curator的监听API

Curator的监听实现是对zookeeper原生监听方法的高级封装,
主要体现在两点:监听重复注册,事件发生信息。
而且监听事件返回详细的信息,如变动的节点信息,节点的value等。

Curator 提供了3个接口:
在这里插入图片描述
如图所示:

  1. NodeCache: 对一个节点进行监听,监听事件包括指定的路径节点的增、删、改的操作。

  2. PathChildrenCache: 对指定的路径节点的一级子目录进行监听,不对该节点的操作进行监听,对其子目录的节点进行增、删、改的操作监听

  3. TreeCache: 可以将指定的路径节点作为根节点(祖先节点),对其所有的子节点操作进行监听,呈现树形目录的监听,可以设置监听深度,最大监听深度为2147483647(int类型的最大值)。

Curator引入了Cache来实现对Zookeeper服务端事件监听,Cache事件监听可以理解为一个本地缓存视图与远程Zookeeper视图的对比过程。Cache提供了反复注册的功能。Cache分为两类注册类型:节点监听和子节点监听。

1、NodeCache(适用于监听数据节点本身)

用于监听数据节点本身的变化。提供了两个构造方法:

public NodeCache(CuratorFramework client, String path)

public NodeCache(CuratorFramework client, String path, boolean dataIsCompressed)

其中参数dataIsCompressed表示是否对数据进行压缩,而第一个方法内部实现为调用第二个方法,且dataIsCompressed默认设为false。

对节点的监听需要配合回调函数来进行处理接收到监听事件之后的业务处理。NodeCache通过NodeCacheListener来完成后续处理。具体代码示例如下:

/**
 * Curator的NodeCacheListener实现zk的节点监听功能:
 * todo: 触发事件为创建节点和更新节点,在删除节点的时候并不触发此watcher
 */
public class CuratorWatcher1 {

    /** zookeeper地址 */
    static final String CONNECT_ADDR = "172.16.158.11:2181,"
            + "172.16.158.12:2181,"
            + "172.16.158.13:2181";
    /** session超时时间 */
    static final int SESSION_OUTTIME = 5000;//ms

    public static void main(String[] args) throws Exception {

        //1 重试策略:初试时间为1s 重试10次
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 10);
        //2 通过工厂创建连接
        CuratorFramework cf = CuratorFrameworkFactory.builder()
                .connectString(CONNECT_ADDR)
                .sessionTimeoutMs(SESSION_OUTTIME)
                .retryPolicy(retryPolicy)
                .build();

        //3 建立连接
        cf.start();

        //4 建立一个cache缓存
        final NodeCache cache = new NodeCache(cf, "/super", false);
        cache.start(true);
        cache.getListenable().addListener(new NodeCacheListener() {
            /**
             * <B>方法名称:</B>nodeChanged<BR>
             * <B>概要说明:</B>触发事件为创建节点和更新节点,在删除节点的时候并不触发此操作。<BR>
             * @see org.apache.curator.framework.recipes.cache.NodeCacheListener#nodeChanged()
             */
            @Override
            public void nodeChanged() throws Exception {
                System.out.println("路径为:" + cache.getCurrentData().getPath());
                System.out.println("数据为:" + new String(cache.getCurrentData().getData()));
                System.out.println("状态为:" + cache.getCurrentData().getStat());
                System.out.println("---------------------------------------");
            }
        });

        Thread.sleep(1000);
        cf.create().forPath("/super", "123".getBytes());

        Thread.sleep(1000);
        cf.setData().forPath("/super", "456".getBytes());

        Thread.sleep(1000);
        cf.delete().forPath("/super");

//        Thread.sleep(Integer.MAX_VALUE);
    }
}

执行结果:

路径为:/super
数据为:123
状态为:8589937827,8589937827,1567847615844,1567847615844,0,0,0,0,3,0,8589937827

---------------------------------------
路径为:/super
数据为:456
状态为:8589937827,8589937828,1567847615844,1567847616855,1,0,0,0,3,0,8589937827

---------------------------------------

NodeCache的start方法有一个带Boolean参数的方法,如果设置为true则在首次启动时就会缓存节点内容到Cache中。

经过试验,发现注册监听之后,如果先后多次修改监听节点的内容,部分监听事件会发生丢失现象。其他版本未验证,此版本此处需特别留意。

NodeCache不仅可以监听节点内容变化,还可以监听指定节点是否存在。如果原本节点不存在,那么Cache就会在节点被创建时触发监听事件,如果该节点被删除,就无法再触发监听事件。

2、PathChildrenCache(用于监听数据节点的子节点)

PathChildrenCache用于监听数据节点子节点的变化情况。当前版本总共提供了7个构造方法,其中2个已经不建议使用了。

public PathChildrenCache(CuratorFramework client, String path, boolean cacheData)

public PathChildrenCache(CuratorFramework client, String path, boolean cacheData, boolean dataIsCompressed, final CloseableExecutorService executorService)

public PathChildrenCache(CuratorFramework client, String path, boolean cacheData, boolean dataIsCompressed, final ExecutorService executorService)

public PathChildrenCache(CuratorFramework client, String path, boolean cacheData, boolean dataIsCompressed, ThreadFactory threadFactory)

public PathChildrenCache(CuratorFramework client, String path, boolean cacheData, ThreadFactory threadFactory)

常见的参数就不再具体说明了。其中cacheData表示是否把节点内容缓存起来,如果为true,那么接收到节点列表变更的同时会将获得节点内容。

ExecutorService 和threadFactory提供了通过线程池的方式来处理监听事件。

PathChildrenCache使用PathChildrenCacheListener来处理监听事件。具体使用方法见代码实例:

/**
 * todo:PathChildrenCacheListener监听子节点变更--->子节点的新建、修改、删除
 * 注意:创建本身节点不发生变化
 */
public class CuratorWatcher2 {

    /** zookeeper地址 */
    static final String CONNECT_ADDR = "172.16.158.11:2181,"
            + "172.16.158.12:2181,"
            + "172.16.158.13:2181";
    /** session超时时间 */
    static final int SESSION_OUTTIME = 5000;//ms

    public static void main(String[] args) throws Exception {

        //1 重试策略:初试时间为1s 重试10次
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 10);
        //2 通过工厂创建连接
        CuratorFramework cf = CuratorFrameworkFactory.builder()
                .connectString(CONNECT_ADDR)
                .sessionTimeoutMs(SESSION_OUTTIME)
                .retryPolicy(retryPolicy)
                .build();

        //3 建立连接
        cf.start();

        //4 建立一个PathChildrenCache缓存,第三个参数为是否接受节点数据内容 如果为false则不接受
        PathChildrenCache cache = new PathChildrenCache(cf, "/super", true);
        //5 在初始化的时候就进行缓存监听
        cache.start(StartMode.POST_INITIALIZED_EVENT);
        cache.getListenable().addListener(new PathChildrenCacheListener() {
            /**
             * <B>方法名称:</B>监听子节点变更<BR>
             * <B>概要说明:</B>新建、修改、删除<BR>
             * @see org.apache.curator.framework.recipes.cache.PathChildrenCacheListener#childEvent(org.apache.curator.framework.CuratorFramework, org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent)
             */
            @Override
            public void childEvent(CuratorFramework cf, PathChildrenCacheEvent event) throws Exception {
                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;
                    default:
                        break;
                }
            }
        });

        //创建本身节点不发生变化
        cf.create().forPath("/super", "init".getBytes());

        //添加子节点
        Thread.sleep(1000);
        cf.create().forPath("/super/c1", "c1内容".getBytes());
        Thread.sleep(1000);
        cf.create().forPath("/super/c2", "c2内容".getBytes());

        //修改子节点
        Thread.sleep(1000);
        cf.setData().forPath("/super/c1", "c1更新内容".getBytes());

        //删除子节点
        Thread.sleep(1000);
        cf.delete().forPath("/super/c2");

        //删除本身节点
        Thread.sleep(1000);
        cf.delete().deletingChildrenIfNeeded().forPath("/super");

        Thread.sleep(Integer.MAX_VALUE);
    }
}


执行结果:

CHILD_ADDED :/super/c1
CHILD_ADDED :/super/c2
CHILD_UPDATED :/super/c1
CHILD_REMOVED :/super/c2
CHILD_REMOVED :/super/c1

**PathChildrenCache不会对二级子节点进行监听,只会对子节点进行监听。**看上面的实例会发现在创建子节点和删除子节点两个操作中间使用了线程睡眠,否则无法接收到监听事件,这也是在使用过程中需要留意的一点。
同时,我们也看到,对于节点zk=super本身的变更,并没有通知到客户端。
另外,和其他ZooKeeper客户端产品一样,Curator也无法对二级子节点进行事件监听。也就是说,如果使用PathChildrenCache对/super进行监听,那么当/super/c1/c2节点被创建或删除的时候,是无法触发子节点变更事件的。

3、TreeCache(监听到指定节点下所有节点的变化)

特点:

(1)永久监听指定节点下的节点的变化
(2)可以监听到指定节点下所有节点的变化,比如说指定节点”/example”, 在下面添加”node1”可以监听到,但是添加”node1/n1”也能被监听到
(3)可以监听到的事件:节点创建、节点数据的变化、节点删除等

使用方式:

(1)创建curatorframework的client
(2)添加TreeCache
(3)启动client 和 TreeCache
(4)注册监听器

部分样例代码:

TreeCache treeCache = null;

treeCache.start();
        // 没有开启模式作为入参的方法
        
        treeCache.getListenable().addListener(new TreeCacheListener(){

            @Override
            public void childEvent(CuratorFramework client, TreeCacheEvent event)
                    throws Exception {
                // 
                switch(event.getType()) {
                case NODE_ADDED: 
                    System.out.println("tree:发生节点添加" + event.getData().toString() ); break;
                case NODE_UPDATED:
                    System.out.println("tree:发生节点更新"); break;
                case NODE_REMOVED:
                    System.out.println("tree:发生节点删除"); break;
                case CONNECTION_SUSPENDED: 
                    break;
                case CONNECTION_RECONNECTED:
                    break;
                case CONNECTION_LOST:
                    break;
                case INITIALIZED:
                    System.out.println("初始化的操作"); break;
                default:
                    break;
                }
            }
            
        });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值