文章目录

  • 1 watcher概念和架构
  • 1.1 概念
  • 1.2 架构
  • 1.3 watcher特性
  • 1.4 watcher接口设计
  • 1.4.1 Watcher通知状态(KeeperState)
  • 1.4.2 Watcher事件类型(EventType)
  • 1.5 如何注册监听
  • 2 java API的使用
  • 2.1 构造ZooKeeper对象注册监听
  • 2.2 exist等节点API如何注册自定义监听

1 watcher概念和架构

1.1 概念

zookeeper提供了数据的发布/订阅功能,多个订阅者可同时监听某一特定主题对 象,当该主题对象的自身状态发生变化时(例如节点内容改变、节点下的子节点列表改变 等),会实时、主动通知所有订阅者。

zookeeper采用了Watcher机制实现数据的发布/订阅功能。该机制在被订阅对象发生变化时会异步通知客户端,因此客户端不必在Watcher注册后轮询阻塞,从而减轻了客户端压力。

watcher机制实际上与观察者模式类似,也可看作是一种观察者模式在分布式场 景下的实现方式。

1.2 架构

Watcher实现由三个部分组成:

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

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

zookeeper会监听8080端口_zookeeper会监听8080端口

1.3 watcher特性

特性

说明

一次性

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

客户端顺序回调

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

轻量级

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

时效性

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

1.4 watcher接口设计

Watcher是一个接口,任何实现了Watcher接口的类就是一个新的Watcher。 Watcher内部包含了两个枚举类:org.apache.zookeeper.Watcher.Event.KeeperStateorg.apache.zookeeper.Watcher.Event.EventType

1.4.1 Watcher通知状态(KeeperState)

KeeperState是客户端与服务端连接状态发生变化时对应的通知类型。是一个枚举类,其枚举属性 如下:

枚举属性

说明

SyncConnected

客户端与服务器正常连接时

Disconnected

客户端与服务器正常连接时

Expired

会话session失效时

AuthFailed

身份认证失败时

1.4.2 Watcher事件类型(EventType)

EventType是数据节点(znode)发生变化时对应的通知类型。

  • EventType变化时,KeeperState永远处于SyncConnected通知状态下;
  • 当KeeperState发生变化时, EventType永远为None。

枚举属性

说明

None


NodeCreated

Watcher监听的数据节点被创建时

NodeDeleted

Watcher监听的数据节点被删除时

NodeDataChanged

Watcher监听的数据节点内容发生变更时(无论内容数据 是否变化)

NodeChildrenChanged

Watcher监听的数据节点的子节点列表发生变更时

客户端接收到的相关事件通知中只包含状态及类型等信息,不包括节点变化前后的 具体内容,变化前的数据需业务自身存储,变化后的数据需调用get等方法重新获取;

1.5 如何注册监听

上面讲到zookeeper客户端连接的状态和zookeeper对znode节点监听的事件类型,下面我们来讲解如何建立zookeeper的watcher监听。在zookeeper中采用

  • 构造zookeeper
  • zk.getChildren(path, watch)
  • zk.exists(path, watch)
  • zk.getData(path, watcher, stat) 这样的方式为某个znode注册监听。

实现接口Watcher接口

2 java API的使用

2.1 构造ZooKeeper对象注册监听

比如下面的代码,就是监听java客户端的服务端的连接状态,也就是监听KeeperState的变化,当KeeperState发生变化时, EventType永远为None。

package study.wyy.zookeeper.watcher;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;


public class ZkConnectionWatcher implements Watcher {

    private final CountDownLatch countDownLatch;

    public ZkConnectionWatcher(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void process(WatchedEvent event) {
        // 1 获取事件类型
        Event.EventType type = event.getType();
        // 当KeeperState发生变化时, EventType永远为None。
        if (type.equals(Event.EventType.None)) {
            // 获取KeeperState
            Event.KeeperState state = event.getState();
            if (state.equals(Event.KeeperState.SyncConnected)) {
                // 客户端与服务器正常连接时
                System.out.println("连接创建成功");
                // 唤醒
                countDownLatch.countDown();
            } else if (state.equals(Event.KeeperState.Disconnected)) {
                System.out.println("连接断开");
            } else if (state.equals(Event.KeeperState.Expired)) {
                System.out.println("会话超时");
            } else if (state.equals(Event.KeeperState.AuthFailed)) {
                System.out.println("认证失败");
            }
        }
    }

    /**
     * 测试
     */
    public static void main(String[] args) throws IOException, InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        // 连接的时候,注册Watcher
        ZooKeeper zooKeeper = new ZooKeeper("localhost:2181", 10000, new ZkConnectionWatcher(countDownLatch));
        // 主线程阻塞等待连接对象创建成功
        countDownLatch.await();
    
    }
}

2.2 exist等节点API如何注册自定义监听

构造方法中注册的监听,是一个全局的监听,也可以为exist等节点API注册自己的监听

  • exists API
  • 可以监听到的时间类型:NodeCreated:节点创建 NodeDeleted:节点删除 NodeDataChanged:节点内容发生变化
public void exists() throws KeeperException, InterruptedException {
        zooKeeper.exists("/watch",new Watcher(){
            @Override
            public void process(WatchedEvent watchedEvent) {
                // 获取时间类型
                Event.EventType type = watchedEvent.getType();
                // 获取节点路径,发生事件的节点路径
                String path = watchedEvent.getPath();
            }
        });
    }
  • getData API
  • 可以监听到的时间类型: NodeDeleted:节点删除 NodeDataChanged:节点数据发送变化
@Test
 public void get() throws KeeperException, InterruptedException {
     Stat stat = new Stat();
     zooKeeper.getData("/watch", new Watcher() {
         @Override
         public void process(WatchedEvent watchedEvent) {
             // 获取时间类型
             Event.EventType type = watchedEvent.getType();
             // 获取节点路径,发生事件的节点路径
             String path = watchedEvent.getPath();
         }
     }, stat);
 }
  • getChildren API
  • 可以监听到的时间类型: NodeChildrenChanged:子节点发生变化 NodeDeleted:节点删除
@Test
    public void getChildren() throws KeeperException, InterruptedException {
        Stat stat = new Stat();
        zooKeeper.getChildren("/watch", new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                // 获取时间类型
                Event.EventType type = watchedEvent.getType();
                // 获取节点路径,发生事件的节点路径
                String path = watchedEvent.getPath();
            }
        }, stat);
    }