Zookeeper中的watcher机制;watcher事件和zookeeper状态;watcher的一次性

watcher架构

Watcher实现由三个部分组成:

  • Zookeeper服务端;
  • Zookeeper客户端;
  • 客户端的ZKWatchManager对象;

  客户端首先将Watcher注册到服务端,同时将Watcher对象保存到客户端的Watch管理器中。当ZooKeeper服务端监听的数据状态发生变化时,服务端会主动通知客户端,接着客户端的Watch管理器会触发相关Watcher来回调相应处理逻辑,从而完成整体的数据发布/订阅流程。

watcher事件和zookeeper状态

Watcher是一个接口,任何实现了Watcher接口的类就是一个新的Watcher。Watcher内部包含了两个枚举类:KeeperState、EventType.

         

EventType事件类型:(znode节点相关的)
         EventType:NodeCreated            //节点创建
         EventType:NodeDataChanged        //节点的数据变更
         EventType:NodeChildrentChanged   //子节点下的数据变更
         EventType:NodeDeleted      //节点删除
KeeperState状态类型:(是跟客户端实例相关的)
         KeeperState:Disconneced        //连接失败
          KeeperState:SyncConnected    //连接成功    
         KeeperState:AuthFailed         //认证失败
         KeeperState:Expired            //会话过期

注意:当KeeperState发生变化时,触发的事件EventType为None

客户端如何注册Watch?

(1)创建zk客户端实例的时候注册watcher(构造方法中注册watcher);

(2)调用 getData()/getChildren()/exist()三个 API,传入 Watcher 对象;

Watch特性

一次性:Watcher是一次性的,事件一旦被触发就会移除,再次使用时需要重新注册

客户端顺序回调:Watcher回调是顺序串行化执行的,只有回调后客户端才能看到最新的数据状态。一个Watcher回调逻辑不应该太多,以免影响别的watcher执行

轻量级:WatchEvent是最小的通信单元,结构上只包含通知状态、事件类型和节点路径,并不会告诉数据节点变化前后的具体内容;

时效性:Watcher只有在当前session彻底失效时才会无效,若在session有效期内快速重连成功,则watcher依然存在,仍可接收到通知;

注意:KeeperState状态改变不属于事件(EventType=None),引起的触发不会移除Watcher。
 
特例:在创建Zookeeper实例时,允许接收一个watcher参数,此参数将会赋值给watchMnanger.defaultWatcher,成为当前客户端的默认Watcher。需要注意此watcher和其他watcher不同,此wather主要是响应"与链接状态转换"有关的事件(比如,"建立链接","链接关闭"等,参见KeeperState)。此默认watcher有zk client本地持有且生命周期伴随整个zookeeper实例,而不是“一次触发即消亡”,当Client收到EventType,NONE类型的消息时,则会触发这个"默认wather"被执行。(默认Watcher节点事件触发后对于EventType会失效,对KeeperState不会失效)

测试

测试代码1
package com.weng.zk;

import org.apache.zookeeper.*;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.List;

public class zkClient {


    private String connectString="hadoop102:2181,hadoop103:2181,hadoop104:2181";
    private int sessionTimeout=100000;
    private ZooKeeper zkClient = null;



    @Before
    public void init() throws IOException {
        //使用zk客户端实例注册一个默认Watcher
        zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            public void process(WatchedEvent watchedEvent) {

                if ( watchedEvent.getType() == null || "".equals( watchedEvent.getType())) {
                    return;
                }
                System.out.println("默认watcher");
                System.out.println("连接状态:"+watchedEvent.getState());
                System.out.println("已经触发了" +  watchedEvent.getType() + "事件!");


            }
        });

    }

    @Test
    public void getChildren() throws KeeperException, InterruptedException {
            //使用getChildren()注册一个普通Watcher
            List<String> children = zkClient.getChildren("/", new Watcher() {
            public void process(WatchedEvent watchedEvent) {
                System.out.println("普通watcher");
                System.out.println("连接状态:"+watchedEvent.getState());
                System.out.println("已经触发了" +  watchedEvent.getType() + "事件!");
            }
        });
        Thread.sleep(Long.MAX_VALUE);
    }
}        

测试结果1

 

测试代码2

package com.weng.zk;

import org.apache.zookeeper.*;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.List;

public class zkClient {


    private String connectString="hadoop102:2181,hadoop103:2181,hadoop104:2181";
    private int sessionTimeout=100000;
    private ZooKeeper zkClient = null;



    @Before
    public void init() throws IOException {
        //使用zk客户端实例注册一个默认Watcher
        zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            public void process(WatchedEvent watchedEvent) {

                if ( watchedEvent.getType() == null || "".equals( watchedEvent.getType())) {
                    return;
                }
                System.out.println("默认watcher");
                System.out.println("连接状态:"+watchedEvent.getState());
                System.out.println("已经触发了" +  watchedEvent.getType() + "事件!");


            }
        });

    }

    @Test
    public void getChildren() throws KeeperException, InterruptedException {
        //使用getChildren()时传入默认Watcher
        List<String> children = zkClient.getChildren("/",true);
        Thread.sleep(Long.MAX_VALUE);

    }
}
        

测试结果2

 

 (以上测试均保证了Watcher的时效性)

总结

普通Watcher触发节点事件后立即消亡,节点事件和服务器状态改变都不再触发。

默认Watcher伴随整个会话,触发节点事件后不会消亡,但对节点事件不再触发,对服务器状态改变仍会触发。

posted @ 2021-07-18 13:40  1243741754  阅读(368)  评论(0编辑  收藏  举报