11.0 Zookeeper watcher 事件机制原理剖析

zookeeper 的 watcher 机制,可以分为四个过程:

  • 客户端注册 watcher。
  • 服务端处理 watcher。
  • 服务端触发 watcher 事件。
  • 客户端回调 watcher。

其中客户端注册 watcher 有三种方式,调用客户端 API 可以分别通过 getData、exists、getChildren 实现,利用前面章节创建的 maven 工程,新建 WatcherDemo 类,以 exists 方法举例说明其原理。

java 复制代码
// 导入 ZooKeeper 相关的类
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.data.Stat;

// 定义 WatcherDemo 类,实现 Watcher 接口
public class WatcherDemo implements Watcher {

  // 静态 ZooKeeper 客户端
  static ZooKeeper zooKeeper;

  // 静态代码块,初始化 ZooKeeper 客户端
  static {
    try {
      // 创建 ZooKeeper 客户端实例,连接到 ZooKeeper 服务器
      zooKeeper = new ZooKeeper("192.168.3.39:2181", 4000, new WatcherDemo());
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  // 重写 Watcher 接口的 process 方法,处理 ZooKeeper 事件
  @Override
  public void process(WatchedEvent event) {
    // 打印事件类型
    System.out.println("eventType:" + event.getType());

    // 判断事件类型
    if (event.getType() == Event.EventType.NodeDataChanged) {
      // 节点数据改变事件
      try {
        // 重新注册对节点的监视
        zooKeeper.exists(event.getPath(), true);
      } catch (KeeperException e) {
        e.printStackTrace();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  // main 方法,程序入口
  public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
    // 定义节点路径
    String path = "/watcher";

    // 判断节点是否存在
    if (zooKeeper.exists(path, false) == null) {
      // 创建节点,并设置初始数据
      zooKeeper.create(path, "0".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    // 睡眠 1 秒
    Thread.sleep(1000);

    // 打印分割线
    System.out.println("-----------");

    // 重新注册对节点的监视,并设置 Watcher
    Stat stat = zooKeeper.exists(path, true);

    // 等待用户输入
    System.in.read();
  }
}

运行完程序,控制台显示:

此时启动 zookeeper 命令行终端,查看并且删除 watcher 节点:

IDE 控制台输出,触发了节点删除事件:

客户端发送请求给服务端是通过 TCP 长连接建立网络通道,底层默认是通过 java 的 NIO 方式,也可以配置 netty 实现方式。

注册 watcher 监听事件流程图:

1、客户端发送事件通知请求

在 Zookeeper 类调用 exists 方法时候,把创建事件监听封装到 request 对象中,watch 属性设置为 true,待服务端返回 response 后把监听事件封装到客户端的 ZKWatchManager 类中。

2、服务端处理 watcher 事件的请求

服务端 NIOServerCnxn 类用来处理客户端发送过来的请求,最终调用到 FinalRequestProcessor,其中有一段源码添加客户端发送过来的 watcher 事件:

然后进入 statNode 方法,在 DataTree 类方法中添加 watcher 事件,并保存至 WatchManager 的 watchTable 与 watchTable 中。

3、服务端触发 watcher 事件流程:

若服务端某个被监听的节点发生事务请求,服务端处理请求过程中调用 FinalRequestProcessor 类 processRequest 方法中的代码如下所示:

删除调用链最终到 DataTree 类中删除节点分支的触发代码段:

进入 WatchManager 类的 triggerWatch 方法:

继续跟踪进入 NIOServerCnxn,构建了一个 xid 为 -1,zxid 为 -1 的 ReplyHeader 对象,然后再调用 sendResonpe 方法。

4、客户端回调 watcher 事件

客户端 SendThread 类 readResponse 方法接收服务端触发的事件通知,进入 xid 为 -1 流程,处理 Event 事件。

希望你也学会了,更多编程源码模板请来二当家的素材网:https://www.erdangjiade.com

相关推荐
小二·44 分钟前
MyBatis基础入门《十五》分布式事务实战:Seata + MyBatis 实现跨服务数据一致性
分布式·wpf·mybatis
feathered-feathered3 小时前
Redis基础知识+RDB+AOF(面试)
java·数据库·redis·分布式·后端·中间件·面试
lang201509283 小时前
深入解析Kafka Broker核心读写机制
分布式·kafka
平行云3 小时前
实时云渲染支持在网页上运行UE5开发的3A大作Lyra项目
unity·云原生·ue5·webgl·虚拟现实·实时云渲染·像素流送
lang201509283 小时前
Kafka高水位与日志末端偏移量解析
分布式·kafka
Tadas-Gao4 小时前
GraphQL:下一代API架构的设计哲学与实践创新
java·分布式·后端·微服务·架构·graphql
阿里云云原生4 小时前
AI 原生落地成果获认可,阿里云云原生多项案例入选信通院「AI 云」典型示范
云原生
lang201509285 小时前
Kafka副本管理核心:ReplicaManager揭秘
分布式·kafka·linq
阿里云云原生5 小时前
阿里云可观测 2025 年 11 月产品动态
阿里云·云原生·云计算·可观测
GGBondlctrl7 小时前
【Redis】从单机架构到分布式,回溯架构的成长设计美学
分布式·缓存·架构·微服务架构·单机架构