Zookeeper使用-4.Watcher机制

85 阅读4分钟

通过前面的文章对Zookeeper在java中的使用有了初步的了解,但是不能仅仅止步于他的使用,去看看其中内部的原理,对以后的工作和使用都会有更好的了解。接下来主要来看一下在使用watcher通知时候,客户端和服务器都有哪些操作。


一、作用

使用zk可以作为发布订阅的服务器,使用watcher机制便可以实现发布订阅的功能,实现分布式通知的能力。

二、实现

1.示例

具体实现可以在之前的文章中进行查看,此处仅做简单的实例。在创建zk链接时候可以传入watcher,这个watcher将会作为全局默认的watcher,被存储在客户端。
来看下之前的示例代码:

/**
 * 建立链接
 */
public ZooKeeper connectionZk(){
    /**
     * 创建zookeeper链接
     */
    try {
        ZooKeeper zooKeeper = new ZooKeeper(host+":"+port,5000,new ZkWatch());
        System.out.println("链接成功:"+zooKeeper.getState());
        return zooKeeper;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}
/**
 * 创建节点后会通过此watch进行通知
 */
public class ZkWatch implements Watcher{
    @Override
    public void process(WatchedEvent watchedEvent) {
        System.out.println("state:"+watchedEvent.getState());
    }
}

2.相关状态介绍

可以看到上述示例中ZkWatch中实现了Watcher接口,并且重写了process()方法,那么在触发时候,会在系统内部进行调用自定义Wacher类中的process()。参数为WatchedEvent,这个参数类中包括KeeperStateEventType,来看下对应的状态和类型都有哪些

public class WatchedEvent {
    private final KeeperState keeperState;
    private final EventType eventType;
    private String path;
}

通过表格来看下枚举值

KeeperState EventType 触发条件
SyncConnected(3) None(-1) 客户端与服务端成功建立会话
NodeCreated(1) 监听的数据节点被创建
NodeDelete(2) 监听的数据节点被删除
NodeDataChanged(3) 监听的数据节点发生变化
NodeChildChanged(4) 监听的数据节点的子节点列表发生变更
Disconnected(0) None(-1) 客户端与服务端断开连接
Expired(-122) None(-1) 会话超时
AuthFailed None(-1) 权限错误

三、工作原理

来看下下面这张图,描述了从客户端注册watcher到watcher触发进行回调通知的流程。整体分为客户端(上半部分)和服务端(下半部分)。 zkwatcher机制.png

1.客户端注册watcher

  1. getData()方法调用时候传入watcher信息为例。在获取数据时候可以对当前节点进行使用watcher进行监听。
  2. getData()方法中将watcher信息被包装到WatchRegistration进行调用。
  3. 接下来使用ClientCnxn进行发送网络请求,在ClientCnxn中会将WatchRegistration打包成packet,然后放到发送队列outgoingQueue中,等待SendThread线程对其进行发送到服务端.
  4. 通过SendThread线程从发送队列中进行读取发送到服务端,当服务端返回成功,则将当前watcher信息存储到本地的ZKWatcherManager中进行保存.
  5. 在本地中实际存储到Map中

2.服务端接收注册watcher

  1. 服务端接收到请求后,会在FinalRequestProcessor.processRequest()方法中进行判断是否需要注册watcher,判断是否注册其实是判断客户端是否传入watcher信息。
  2. 如果需要存储则将当前的ServerCnxn链接进行保存起来。保存到服务端WatcherManager中,服务端WatcherManager会包含两个map结构,分别保存不同的映射关系。
  3. 然后返回客户端成功信息

3.watcher触发回调

此处将介绍watcher的触发回调,将服务端和客户端的流程一起写在这里。以setData为例改变数据,触发通知流程。

  1. 服务端收到客户端的setData()请求,进行同步设置数据。
  2. 当设置数据成功后,同步调用WatcherManager.triggerWatch()方法进行发起回调请求。
  3. 在方法中将当前节点封装为WatcheredEvent对象,然后将对应watcher信息从服务端watcher信中进行删除,此处说明每次发生调用都是一次性的。触发后便会失效。
  4. 通过之前存储的ServerCnxn链接信息,将当前变更发送给客户端
  5. 客户端由SendThread进行接收服务器的请求信息,进行反序列化还原WatcherEvent对象。然后放入pendingQueue阻塞队列中。
  6. 客户端存在EventThread线程会对阻塞队列中进行获取回调数据。然后调用watcher.process()的方法。

四、总结

通过上面的总结分析,整体结构脉络有了初步的认识。

客户端一共有ZKWatcherManager管理客户端watcher信息、ClientCnxn管理客户端链接、SendThread用于从队列中发送请求到服务端,并且接受服务单的请求、EventThread用于处理接受到的请求进行回调watcher数据,并且有两个队列outgoingQuenepengdingQueue分别用于发送数据存储和接受数据的存储。

服务端一共有WatcherManger管理注册和删除客户端的watcher信息。ServerCnxn管理服务端接收到请求链接。

Watcher整体逻辑可以看做是客户端的操作,服务端只是收到请求后再次从当前的链接进行通知到客户端,客户端进行回调。客户端对同一个path的回调采用轮训方式,也支持单独传入线程池进行处理。

继续加油