ZooKeeper监听机制

转自:http://www.cnblogs.com/viviman/archive/2013/03/11/2954118.html

前言

Zookeeper的监听机制很多人都踩过坑,感觉实现了watcher 接口,后面节点的变化都会一一推送过来,然而并非如此。

Watch机制官方声明:一个Watch事件是一个一次性的触发器,当被设置了Watch的数据发生了改变的时候,则服务器将这个改变发送给设置了Watch的客户端,以便通知它们。

Zookeeper机制的特点

   1.一次性触发  数据发生改变时,一个watcher event会被发送到client,但是client只会收到一次这样的信息。

   2.watcher event异步发送   watcher 的通知事件从server发送到client是异步的,这就存在一个问题,不同的客户端和服务器之间通过socket进行通信,由于网络延迟或其他因素导致客户端在不通的时刻监听到事件,由于Zookeeper本身提供了ordering guarantee,即客户端监听事件后,才会感知它所监视znode发生了变化。

   3.数据监视   Zookeeper有数据监视和子数据监视   getdata() and exists() 设置数据监视,getchildren()设置了子节点监视

   4.注册watcher   getData、exists、getChildren

    5. 触发watcher    create、delete、setData

回调基础知识  

znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的集中管理,集群管理,分布式锁等等。

//创建一个Zookeeper实例,第一个参数为目标服务器地址和端口,第二个参数为Session超时时间,第三个为节点变化时的回调方法
ZooKeeper zk = new ZooKeeper("127.0.0.1:2181", 500000,new Watcher() {
          // 监控所有被触发的事件
           public void process(WatchedEvent event) {
     //dosomething
          }

     });
将APP1的所有配置配置到/APP1 znode下,APP1所有机器一启动就对/APP1这个节点进行监控(zk.exist("/APP1",true)),并且实现回调方法Watcher,那么在zookeeper上/APP1 znode节点下数据发生变化的时候,每个机器都会收到通知,Watcher方法将会被执行,那么应用再取下数据即可(zk.getData("/APP1",false,null));

下面表格列出了写操作与ZK内部产生的事件的对应关系:

 

event For “/path”

event For “/path/child”

create(“/path”)

EventType.NodeCreated

NA

delete(“/path”)

EventType.NodeDeleted

NA

setData(“/path”)

EventType.NodeDataChanged

NA

create(“/path/child”)

EventType.NodeChildrenChanged

EventType.NodeCreated

delete(“/path/child”)

EventType.NodeChildrenChanged

EventType.NodeDeleted

setData(“/path/child”)

NA

EventType.NodeDataChanged

而ZK内部的写事件与所触发的watcher的对应关系如下:

event For “/path”

defaultWatcher

exists
(“/path”)

getData
(“/path”)

getChildren
(“/path”)

EventType.None

EventType.NodeCreated

 

 

EventType.NodeDeleted

 

√(不正常)

 

EventType.NodeDataChanged

 

 

EventType.NodeChildrenChanged

   

综合上面两个表,我们可以总结出各种写操作可以触发哪些watcher,如下表所示:

 

“/path”

“/path/child”

 

exists

getData

getChildren

exists

getData

getChildren

create(“/path”)

 

 

 

 

delete(“/path”)

 

 

 

setData(“/path”)

 

 

 

 

create(“/path/child”)

 

 

 

delete(“/path/child”)

 

 

setData(“/path/child”)

 

 

 

 

zkClient除了做了一些便捷包装之外,对watcher使用做了一点增强。比如subscribeChildChanges实际上是通过exists和getChildren关注了两个事件。这样当create(“/path”)时,对应path上通过getChildren注册的listener也会被调用。另外subscribeDataChanges实际上只是通过exists注册了事件。因为从上表可以看到,对于一个更新,通过exists和getData注册的watcher要么都会触发,要么都不会触发。如果发生session close、authFail和invalid,那么所有类型的wather都会被触发

getData,getChildren(),exists()这三个方法可以针对参数中的path设置watcher,当path对应的Node 有相应变化时,server端会给对应的设置了watcher的client 发送一个一次性的触发通知事件。客户端在收到这个触发通知事件后,可以根据自己的业务逻辑进行相应地处理。

注意这个watcher的功能是一次性的,如果还想继续得到watcher通知,在处理完事件后,要重新register。

测试watcher回调机制

// Watcher实例

Watcher wh = new Watcher() {
    public void process(WatchedEvent event) {
        System.out.println("回调watcher实例: 路径" + event.getPath() + " 类型:"
                + event.getType());
    }
};
ZooKeeper zk = new ZooKeeper("10.15.82.166:3351", 500000, wh);
System.out.println("---------------------");
// 创建一个节点root,数据是mydata,不进行ACL权限控制,节点为永久性的(即客户端shutdown了也不会消失)
zk.exists("/root", true);
zk.create("/root", "mydata".getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
System.out.println("---------------------");
// 在root下面创建一个childone znode,数据为childone,不进行ACL权限控制,节点为永久性的
zk.exists("/root/childone", true);
zk.create("/root/childone", "childone".getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
System.out.println("---------------------");
// 删除/root/childone这个节点,第二个参数为版本,-1的话直接删除,无视版本
zk.exists("/root/childone", true);
zk.delete("/root/childone", -1);
System.out.println("---------------------");
zk.exists("/root", true);
zk.delete("/root", -1);
System.out.println("---------------------");
// 关闭session
zk.close();

结果

---------------------
回调watcher实例: 路径null 类型:None
回调watcher实例: 路径/root 类型:NodeCreated
---------------------
回调watcher实例: 路径/root/childone 类型:NodeCreated
---------------------
回调watcher实例: 路径/root/childone 类型:NodeDeleted
---------------------
回调watcher实例: 路径/root 类型:NodeDeleted
---------------------

永久回调 

3类事件触发wather后就不再作用,也就是所谓的(一次作用),但是,如何永久监听呢?这需要我们再程序逻辑上进行控制,网上有更好的办法,但是,在简单的应用中,可以再wather方法里面再设置监听,这个方法很笨,但是,很有效,达到了预期的效果。

Watcher wh = new Watcher() {
        public void process(WatchedEvent event) {
            Log.AC_DEBUG("触发回调watcher实例: 路径" + event.getPath() + " 类型:"
                    + event.getType());

            if (event.getType() == EventType.None) {
                try { //
                        // 判断userauth权限是否能访问userpath
                    String auth_type = "digest";
                    zk.addAuthInfo(auth_type, userauth.getBytes());
                    zk.getData(userpath, null, null);
                } catch (Exception e) {
//                    e.printStackTrace();
                    Log.AC_ERROR("get node faild:userpath=" + userpath
                            + ",auth=" + userauth + " e:" + e.getMessage());
                    return;
                }
                Log.AC_INFO("userpath=" + userpath + " userauth=" + userauth);
            }

            try {

                switchinfo = getallswitch(); // 更新userpath和匿名用户路径下的配置信息,监听这些节点

                // 监听用户路径节点
                Log.AC_DEBUG("lesson user=" + userpath + " node...");
                zk.exists(userpath, true); // 监听匿名用户路径节点
                Log.AC_DEBUG("lesson user=" + AnonymousUSERpath + " node...");
                zk.exists(AnonymousUSERpath, true);

                // 监听用户路径下的开关节点
                if (zk.exists(userpath, false) != null) {
                    Log.AC_DEBUG("lesson user=" + userpath
                            + " 's swich node...");
                    List<String> swnodes = zk.getChildren(userpath, true); //
                    // 监听switch层节点的变化
                    Iterator<String> it_sw = swnodes.iterator();
                    while (it_sw.hasNext()) {
                        String swpath = userpath + "/" + it_sw.next();
                        Log.AC_DEBUG("lesson user=" + swpath + " node...");
                        zk.exists(swpath, true);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                Log.AC_ERROR("lesson znode error:" + e.getMessage());
            }
        }
    };

参考

Zookeeper提供了一种事件监听机制,用于实时监控Zookeeper节点的状态变化。当节点发生变化时,Zookeeper会通知注册了相应监听器的客户端,以便客户端可以及时处理相关事件。 Zookeeper的事件监听机制主要包括两个部分:Watcher和Listener。 1. Watcher:Watcher是Zookeeper提供的一种轻量级的事件通知机制。当客户端注册了Watcher,并且指定了需要监控的节点路径和事件类型后,Zookeeper会在节点的状态发生变化时,向客户端发送相应的事件通知。客户端可以通过Watcher回调方法来处理这些事件,例如节点的创建、删除、数据更新等。 2. Listener:Listener是Zookeeper客户端提供的一种高级抽象,用于对Zookeeper节点的变化进行监听。与Watcher不同,Listener可以一次性监听多个节点,并提供更灵活的事件处理方式。通过Listener,客户端可以设置节点的创建、删除、数据更新等多个事件类型,并通过回调方法来处理这些事件。 使用Zookeeper的事件监听机制,可以实现实时监控分布式系统中各个节点的状态变化,并及时做出相应的操作。例如,在分布式锁场景中,可以通过监听节点的删除事件来实现锁的释放;在配置管理场景中,可以监听节点的数据更新事件来实时获取最新的配置信息。这种机制可以帮助开发者更好地处理分布式系统中的数据一致性和协调问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值