什么是 ZK Watcher

基于 ZK 的应用程序的一个常见需求是需要知道 ZK 集合的状态。为了达到这个目的,一种方法是 ZK 客户端定时轮询 ZK 集合,检查系统状态是否发生了变化。然而,轮询并不是一种高效的方式,尤其是在状态变化的发生频率很低的时候

因此,ZK 提供了一种通过通知客户端感兴趣的具体时间来避免轮询造成的性能问题的方式,即设置 Watcher 的方式。通过设置 Watcher,ZK 客户端可以对指定的 znode 注册一个通知请求,在 znode 发生变化时收到一个单次的通知。例如,在 znode 被删除时向 Watcher 发送节点被删除的通知

应用 ZK Watcher 的代码通常遵循如下的框架

zk.exists("myZnode", myWatcher, existsCallback, null);

Watcher myWatcher new Watcher() {
public void process(WatchedEvent event) {
// process the watch event
}
} StatCallback existsCallback = new StatCallback() {
public void processResult(int rc, String path, Object ctx, Stat stat) {
// process the result of the exists call
}
}

上面的代码框架中以 exists 操作为例,展示了异步调用 ZK 操作并注册 Watcher 的一般用法

WatchedEvent 的分类

Watcher 的使用一个重要的内容就是了解 Watcher 如何设置以及何时触发,并不是所有的 ZK 操作都可以设置 Watcher,Watcher 也不是会被所有事件触发

抛开被重载的连接状态的 WatchedEvent,业务过程中会遇到的 WatchedEvent 分为以下几种

  • NodeCreated - 可以通过 exists 调用设置 Watcher,在 znode 从无到有创建的时候被触发
  • NodeDeleted - 可以通过 exists 或者 getData 调用设置 Watcher,在 znode 被删除时触发
  • NodeDataChanged - 可以通过 exists 或者 getData 调用设置 Watcher,在 znode 数据发生变化时触发
  • NodeChildrenChanged - 可以通过 getChildren 调用设置 Watcher,在 znode 的直接子节点创建或删除时触发
  • DataWatchRemoved - 在 exists 或者 getData 设置的 Watcher 被删除时触发对应的 Watcher
  • ChildWatchRemoved - 在 getChildren 设置的 Watcher 被删除时触发对应的 Watcher

可以看到,只有 exists 和 getData 和 getChildren 三种操作能够设置 Watcher

注意,getData 创建的 Watcher 不会接收到 NodeCreated 事件,这是因为 getData 在节点不存在的时候会抛出 KeeperException.NoNodeException 异常,而不会设置 Watcher

Watcher 机制的实现与生命周期

从应用程序的角度来讲,注册完 Watcher 之后只要等待事件被触发即可,无需关心 ZK 是怎么实现这个过程的。不过了解 ZK 的具体实现机制有助于我们在面对错误或者异常的时候更好的理解问题的出处以及针对性的排查问题

Watcher 机制的实现最重要的问题就是 Watcher 究竟是注册在哪里的,以及 Watcher 究竟是如何触发的。这两个问题很难分开来解释,因此下文会一并分析

原本讲解原理的部分最好是结合对应的源代码摘要来讲解,但是 ZK 的源码实在是难以阅读,贴在这里不但不能帮助理解,恐怕会让读者更加一头雾水。我会从伪代码的粒度介绍代码逻辑并附上对应的源文件位置,有兴趣的同学可以自行阅读,祝身体健康

Watcher 机制的实现要从注册讲起,ZK 客户端在执行 exists 或 getData 或 getChildren 操作的时候,可以设置一个自定义的 Watcher 或者通过 flag 复用创建客户端时设置的 Watcher。后者实践中比较少用,不做过多介绍。这个 Watcher 会被打包成 Packet 放进 ClientCnxnEventThread 中,在对应的操作完成时登记到客户端的 Watches 集合里。在服务端,对应的 GetDataRequest 等请求有一个是否设置了 Watcher 的 flag,服务端由此来判断是否要设置相应的 Watcher。这里,ZK 为 ServerCnxn 实现了 Watcher 接口。ServerCnxn 是每个服务端上对于客户端的连接对象,它的 process(WatchedEvent) 方法就是将对应的 WatchedEvent 打包为 WatcherEvent 然后发送给客户端

Watcher 成功设置后需要关心的就是 Watcher 的触发了,本质上 Watcher 是在 ZK 集合发生状态变化的时候在客户端回调对应处理逻辑的。但是 ZK 集合发生状态变化要以服务端的状态为准,服务器维护了 ZK 集合的状态,这主要是由 ZKDatabaseDataTree 来实现的。当服务器判断发生了需要出发 Watcher 的状态变化时,服务器会遍历异动节点上对应的 Watcher,在这里就是对应的客户端连接,回调它们的 process(WatchedEvent) 方法。如上所述,这就向客户端发送了一个对应的 WatchedEvent

上面介绍的 Watcher 注册和触发的过程实际上就囊括了 Watcher 的整个生命周期,即 Watcher 的生命周期由对应操作在客服端成功时开始,到触发后结束。也就是说,Watcher 是单次触发的,触发之后还想再次监听对应节点的状态需要重新设置 Watcher。Watcher 的生命周期结束还有另一个触发条件,即 session 被关闭或过期。此外,在 3.5.0 之后的版本中,ZK 能够主动执行 removeWatches 操作来移除不再感兴趣的节点。

Watcher 的错误处理

如前所述,Watcher 是一种轻量级的相应变化的通知机制。由于其功能简单,在实际应用当中为了构建更加复杂的语义,我们需要对 Watcher 在一些故障条件下的响应做对应的讨论。

其中第一个是单次触发和 WatchedEvent 携带的信息带来的问题。由于 Watcher 是单次触发的,所以我们可能会丢掉在前一个 Watcher 触发后到后一个 Watcher 重新设置之前的事件。通常来说这不是问题,因为 ZK 的目标是实现一个分布式环境下对状态达成共识的存储,而不保证每个事件都被客户端记录和处理。重新设置 Watcher 时附带的动作足以保证我们同步了当时的最新状态。因此,我们虽然漏掉了事件,但是那充其量只是一个中间状态,ZK 提供的保证是关于一段时间内的最终状态的。但是换个角度讲,由于 WatchedEvent 只包含了【事件发生了】这个信息,所以任何新的状态都需要重新从 ZK 集合上获取,这是 ZK 为了实现的简单在当初做的一个 trade-off

其中第二个是关于 CONNECTIONLOSS 异常的。严格来说这并不是 Watcher 应该关心的事情,因为操作由于 CONNECTIONLOSS 失败时 Watcher 是无法被成功设置上去的。CONNECTIONLOSS 异常意味着客户端和正在连接的服务器断开连接,由于 ZK 服务端有若干个服务器,在这种情况下客户端会尝试连接其他的服务器。但是在这种情况下,由于 Watcher 没有被成功设置,因此在重新连接成功后,应当重试刚才的操作,以正确的设置 Watcher。此外,此前已经成功设置的 Watcher 不会受到这种连接移动的影响,这是因为客户端重连服务端时会将所有 Watcher 重新发送一遍,服务端比对 znode 状态和 zxid 的相对值,推断出需要触发的 Watcher 进行触发,其他 Watcher 正常设置

小结

ZK 的 Watcher 机制正常流程还是比较顺畅的,但是 Watcher 触发后需要主动再次拉去状态这一点还是比较麻烦的,而且 ZK 的操作会出现各种各样诡异的异常。关于 ZK 在网络延迟或分区的情况下各种异常的处理,会有单独的一篇文章来介绍。此外,ZK 的源代码对身体有害,建议除了催吐最好不要闲着没事去看

ZK Watcher 的原理和实现的更多相关文章

  1. zk的watcher机制的实现

    转载:https://www.ibm.com/developerworks/cn/opensource/os-cn-apache-zookeeper-watcher/ http://blog.csdn ...

  2. Zookeeper——Watcher原理详解

    文章目录 引言 正文 一.如何注册监听 二.如何触发监听事件 三.事件类型有哪些 四.Watcher可以被无限次触发么?为什么要这么设计? 五.Watcher实现原理 1. 客服端发送请求 a. 初始 ...

  3. Zookeeper原理与Curator使用

    近期打算实现一个基于Zookeeper的分布式的集群状态一致性控制, 对Zookeeper的原理不太了解, 正好学习一下, 网上找到了几篇文章, 先贴在这边, 等我熟读官方文档后, 再来补充自己的见解 ...

  4. zookeeper系列之异步通知模式-Watcher

    1 watcher种类和事件种类 Watcher种类 1. zookeeper实例化时注入的默认Watcher 2. dataWatchers 一个Map<string Set<Watch ...

  5. Java分布式锁看这篇就够了

    ### 什么是锁? 在单进程的系统中,当存在多个线程可以同时改变某个变量(可变共享变量)时,就需要对变量或代码块做同步,使其在修改这种变量时能够线性执行消除并发修改变量. 而同步的本质是通过锁来实现的 ...

  6. curator 分布式锁InterProcessMutex

    写这篇文章的目的主要是为了记录下自己在zookeeper 锁上踩过的坑,以及踩坑之后自己的一点认识; 从zk分布式锁原理说起,原理很简单,大家也应该都知道,简单的说就是zookeeper实现分布式锁是 ...

  7. zookeeper项目使用几点小结

    背景 前段时间学习了zookeeper后,在新的项目中刚好派上了用场,我在项目中主要负责分布式任务调度模块的开发,对我自己来说是个不小的挑战. 分布式的任务调度,技术上我们选择了zookeeper,具 ...

  8. Zookeeper 入门第一篇

    转载原文地址: ZooKeeper学习总结 第一篇:ZooKeeper快速入门 ZooKeeper学习总结 第二篇:ZooKeeper深入探讨 ZooKeeper学习第一期---Zookeeper简单 ...

  9. 基于zookeeper实现分布式配置中心(一)

    最近在学习zookeeper,发现zk真的是一个优秀的中间件.在分布式环境下,可以高效解决数据管理问题.在学习的过程中,要深入zk的工作原理,并根据其特性做一些简单的分布式环境下数据管理工具.本文首先 ...

随机推荐

  1. 性能测试学习第七天-----JMeter之linux环境部署篇

    一.linux获取动态ip或静态ip: 1. virtualbox 加载linux虚拟机镜像文件,加载时重置全部网卡,加载后网络选择“桥接网络”--本机当前使用网卡: 2. ifconfig      ...

  2. ansible模块介绍之ios_command

    一.模块简介 ios_command此模块将任意命令发送到ios节点并返回设备读取的结果 此模块不支持在配置模式下使用,即只支持在用户模式>和特权模式#下使用 官方文档地址:https://do ...

  3. 合并多个jar包,并通过私服依赖

    背景:许多jar包在maven仓库中没有,项目如果添加了许多的本地jar包,别人拿到代码也无法编译 需求:将本地jar包上传至私服并设置依赖,如果jar包较多,但都从属于同一功能,需要合并为一个jar ...

  4. Kali Linux-装机后通用配置

    目录 前言 一. 网络优化 更换host 更换dns 添加源 二. 更新系统 三 .安装N卡驱动 四.修复 add-apt-repository 五.安装常用软件 安装apt自带的包 安装第三方的de ...

  5. RSA加密的java实现

    首先科普一波: RSA的1024位是指公钥及私钥分别是1024bit,也就是1024/8=128 Bytes RSA算法密钥长度的选择是安全性和程序性能平衡的结果,密钥长度越长,安全性越好,加密解密所 ...

  6. 基于python的mysql复制工具

    一简介 python-mysql-replication 是由python实现的 MySQL复制协议工具,我们可以用它来解析binlog 获取日志的insert,update,delete等事件 ,并 ...

  7. 学会了这些技术,你离BAT大厂不远了

    每一个程序员都有一个梦想,梦想着能够进入阿里.腾讯.字节跳动.百度等一线互联网公司,由于身边的环境等原因,不知道 BAT 等一线互联网公司使用哪些技术?或者该如何去学习这些技术?或者我该去哪些获取这些 ...

  8. 如何运用PHP+REDIS解决负载均衡后的session共享问题

    一.为什么要使用Session共享? 稍大一些的网站,通常都会有好几个服务器,每个服务器运行着不同功能的模块,使用不同的二级域名,而一个整体性强的网站,用户系统是统一的,即一套用户名.密码在整个网站的 ...

  9. 安装tomcat出现的问题

    今天在安装tomcat时出现了配置环境不对的问题. 在正确配置Tomcat环境变量后,遇到很多次运行startup.bat后,一个窗口一闪而过的. 解决方法: 1.在tomcat的目录下选中start ...

  10. 安装hadoop集群--hdfs

    安装hadoop集群--hdfs 大数据软件 链接:https://pan.baidu.com/s/1-3PYLHMgvvONawJq55hstQ 提取码:izqf 准备一台干净的虚拟机-centos ...