Zookeeper开源客户端Curator之事件监听详解

Curator对Zookeeper典型场景之事件监听进行封装,提供了使用参考。这篇博文笔者带领大家了解一下Curator的实现方式。

引入依赖

对于Curator封装Zookeeper的典型场景使用都放在了recipes中。因此,使用之前需先引入此依赖。

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

以下实例默认节点“/p1”已经被创建切存在于Zookeeper服务器上的。

监听方式一

利用Watcher来对节点进行监听操作,但此监听操作只能监听一次,与原生API并无太大差异。如有典型业务场景需要使用可考虑,但一般情况不推荐使用。下面是具体的使用案例。

package com.secbro.learn.curator;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;

/**
 * Created by zhuzs on 2017/4/14.
 */
public class CuratorListenerTest1{
    public static void main(String[] args) {
        CuratorFramework client = getClient();
        String path = "/p1";

        try {
            byte[] content = client.getData().usingWatcher(new Watcher() {
                @Override
                public void process(WatchedEvent watchedEvent) {
                    System.out.println("监听器watchedEvent:" + watchedEvent);
                }
            }).forPath(path);

            System.out.println("监听节点内容:" + new String(content));

            // 第一次变更节点数据
            client.setData().forPath(path,"new content".getBytes());

            // 第二次变更节点数据
            client.setData().forPath(path,"second content".getBytes());

            Thread.sleep(Integer.MAX_VALUE);
        } catch (Exception e) {
            e.printStackTrace();
            client.close();
        } finally {
            client.close();
        }

    }

    private static CuratorFramework getClient(){
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("127.0.0.0:2181")
                .retryPolicy(retryPolicy)
                .sessionTimeoutMs(6000)
                .connectionTimeoutMs(3000)
                .namespace("demo")
                .build();
        client.start();
        return client;
    }
}

执行结果:

监听节点内容:new content
监听器watchedEvent:WatchedEvent state:SyncConnected type:NodeDataChanged path:/p1

执行此程序之后,首先会对节点/p1注册一个Watcher监听事件,同时返回当前节点的内容信息。随后改变节点内容为“new content”,此时触发监听事件,并打印出监听事件信息。但当第二次改变节点内容时,监听已经失效,无法再次获得节点变动事件。

方法二

CuratorListener监听,此监听主要针对background通知和错误通知。使用此监听器之后,调用inBackground方法会异步获得监听,而对于节点的创建或修改则不会触发监听事件。具体实例代码如下:

package com.secbro.learn.curator;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.api.CuratorListener;
import org.apache.curator.retry.ExponentialBackoffRetry;

/**
 * Created by zhuzs on 2017/4/14.
 */
public class CuratorListenerTest1 {
    public static void main(String[] args) {
        CuratorFramework client = getClient();
        String path = "/p1";

        try {
            CuratorListener listener = new CuratorListener() {
                @Override
                public void eventReceived(CuratorFramework client, CuratorEvent event) throws Exception {
                    System.out.println("监听事件触发,event内容为:" + event);
                }
            };
            client.getCuratorListenable().addListener(listener);
            // 异步获取节点数据
            client.getData().inBackground().forPath(path);
            // 变更节点内容
            client.setData().forPath(path,"123".getBytes());

            Thread.sleep(Integer.MAX_VALUE);
        } catch (Exception e) {
            e.printStackTrace();
            client.close();
        } finally {
            client.close();
        }

    }

    private static CuratorFramework getClient(){
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("127.0.0.1:2181")
                .retryPolicy(retryPolicy)
                .sessionTimeoutMs(6000)
                .connectionTimeoutMs(3000)
                .namespace("demo")
                .build();
        client.start();
        return client;
    }
}

执行结果为:

监听事件触发,event内容为:CuratorEventImpl{type=WATCHED, resultCode=3, path='null', name='null', children=null, context=null, stat=null, data=null, watchedEvent=WatchedEvent state:SyncConnected type:None path:null, aclList=null}
监听事件触发,event内容为:CuratorEventImpl{type=GET_DATA, resultCode=0, path='/p1', name='null', children=null, context=null, stat=17814,18054,1491458317592,1492218568138,12,0,0,0,3,0,17814
, data=[49, 50, 51], watchedEvent=null, aclList=null}

其中两次触发监听事件,第一次触发为注册监听事件时触发,第二次为getData异步处理返回结果时触发。而setData的方法并未触发监听事件。

方法三

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

NodeCache

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

public NodeCache(CuratorFramework client, String path)

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

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

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

package com.secbro.learn.curator;

import org.apache.curator.RetryPolicy;
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;

/**
 * Created by zhuzs on 2017/4/15.
 */
public class CuratorNodeCacheTest {

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

        CuratorFramework client = getClient();
        String path = "/p1";
        final NodeCache nodeCache = new NodeCache(client,path);
        nodeCache.start();
        nodeCache.getListenable().addListener(new NodeCacheListener() {
            @Override
            public void nodeChanged() throws Exception {
                System.out.println("监听事件触发");
                System.out.println("重新获得节点内容为:" + new String(nodeCache.getCurrentData().getData()));
            }
        });
        client.setData().forPath(path,"456".getBytes());
        client.setData().forPath(path,"789".getBytes());
        client.setData().forPath(path,"123".getBytes());
        client.setData().forPath(path,"222".getBytes());
        client.setData().forPath(path,"333".getBytes());
        client.setData().forPath(path,"444".getBytes());
        Thread.sleep(15000);

    }

    private static CuratorFramework getClient(){
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("127.0.0.1:2181")
                .retryPolicy(retryPolicy)
                .sessionTimeoutMs(6000)
                .connectionTimeoutMs(3000)
                .namespace("demo")
                .build();
        client.start();
        return client;
    }
}

执行结果:

监听事件触发
重新获得节点内容为:123
监听事件触发
重新获得节点内容为:333
监听事件触发
重新获得节点内容为:444

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

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

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

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来处理监听事件。具体使用方法见代码实例:

package com.secbro.learn.curator;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;

/**
 * Created by zhuzs on 2017/4/15.
 */
public class CuratorPathChildrenCacheTest {

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

        CuratorFramework client = getClient();
        String parentPath = "/p1";

        PathChildrenCache pathChildrenCache = new PathChildrenCache(client,parentPath,true);
        pathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
        pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                System.out.println("事件类型:"  + event.getType() + ";操作节点:" + event.getData().getPath());
            }
        });

        String path = "/p1/c1";
        client.create().withMode(CreateMode.PERSISTENT).forPath(path);
        Thread.sleep(1000); // 此处需留意,如果没有现成睡眠则无法触发监听事件
        client.delete().forPath(path);

        Thread.sleep(15000);

    }

    private static CuratorFramework getClient(){
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("127.0.0.1:2181")
                .retryPolicy(retryPolicy)
                .sessionTimeoutMs(6000)
                .connectionTimeoutMs(3000)
                .namespace("demo")
                .build();
        client.start();
        return client;
    }
}

打印结果为:

事件类型:CHILD_ADDED;操作节点:/p1/c1
事件类型:CHILD_REMOVED;操作节点:/p1/c1

PathChildrenCache不会对二级子节点进行监听,只会对子节点进行监听。看上面的实例会发现在创建子节点和删除子节点两个操作中间使用了线程睡眠,否则无法接收到监听事件,这也是在使用过程中需要留意的一点。

总结

本篇博客讲述了Curator针对事件监听使用的方法和实例,并点出了其中需要留意的地方。由于篇幅有限部分使用场景未逐一举例说明,可自行尝试。

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
### 回答1: Curator 是一个 Zookeeper客户端库,主要用于实现 Zookeeper 的各种常见用法。它并不直接与 MySQL 有关系,但可以配合 MySQL 使用,例如监控 MySQL 的变化并更新到 Zookeeper 上。 实现监听 MySQL 变化的方式有很多种,其中一种比较常见的方式是使用 MySQL 的 binlog,在 binlog 中记录了 MySQL 的所有变化,包括增删改等操作。我们可以使用开源的 canal 工具来解析 binlog,并将变化发送给 Zookeeper。然后使用 Curator 来监听 Zookeeper 的变化,当 MySQL 中的数据发生变化时,Zookeeper 中相应节点的值也会发生变化,Curator 可以自动感知到这种变化并进行相应的处理。 ### 回答2: Curator是一个用于Apache ZooKeeper客户端库,用于对分布式系统中的节点进行管理和协调操作。与MySQL数据库相比,Curator并不直接用于监听MySQL。不过,在某些情况下可以结合使用。 在MySQL中,可以使用触发器和事件来监听数据库的变化。触发器是一种特殊类型的存储过程,当满足指定的条件时,可以在表中自动执行一系列的操作。通过在触发器中添加逻辑,可以实现对数据库变化的监听。 CuratorZooKeeper中可以创建临时节点,并能够对这些节点进行监听。这意味着,我们可以在MySQL的数据库中创建一个触发器,在触发器中通过Curator将变化通知到ZooKeeper的临时节点,然后使用Curator监听该节点的变化。 具体步骤如下: 1. 在MySQL数据库中创建一个触发器,监听指定数据表的变化。 2. 当触发器被触发时,触发器内部调用Curator将相关信息发送到ZooKeeper。 3. 在应用中使用Curator监听ZooKeeper上的临时节点,当节点变化时,执行需要的操作来处理MySQL数据库变化的通知。 通过以上步骤,我们可以实现Curator监听MySQL的功能。当MySQL数据库发生变化时,触发器会将相关信息发送到ZooKeeper上的临时节点,然后Curator监听该节点的变化,触发相应的操作。 需要注意的是,这个方法仅适用于特定场景下,比如需要将MySQL的变化通知到其他分布式系统中。在一般情况下,直接使用MySQL的触发器和事件机制就可以实现对数据库变化的监听,而无需使用Curator。 ### 回答3: Curator是一个开源ZooKeeper客户端框架,用于管理和协调分布式系统。它提供了一组丰富的API,可以帮助开发人员更轻松地与ZooKeeper进行交互。那么,如果我们要监听MySQL数据库的变化,可以借助Curator提供的功能。 首先,我们需要在应用程序中引入Curator的依赖,并在代码中初始化Curator客户端。然后,我们可以使用Curator提供的监听器接口来监听MySQL数据库的变化。 其中,监听MySQL数据库的变化可以通过以下步骤实现: 1. 创建一个MySQL监听器的类,实现Curator提供的CuratorListener接口。该接口包含了处理ZooKeeper事件的方法。 2. 在监听器的实现中,我们可以编写代码来连接MySQL数据库,并设置监听器,在数据发生变化时触发相应的事件。 3. 在监听器中,我们可以监听数据库表的变化,例如数据插入、更新、删除等。 4. 当数据库发生变化时,我们可以在CuratorListener的相应方法中编写业务逻辑,例如发送通知、更新缓存等。 使用Curator监听MySQL数据库的好处是可以实时获取数据库的变化,而不需要轮询查询数据库。同时,Curator还可以处理分布式情况下的一致性问题,保证各个节点之间的数据一致性。 总结起来,利用Curator可以实现对MySQL数据库的监听,实时获取数据库的变化,方便开发人员进行业务逻辑的处理。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序新视界

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值