ZK Watcher 的原理和实现
什么是 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
放进 ClientCnxn
的 EventThread
中,在对应的操作完成时登记到客户端的 Watches 集合里。在服务端,对应的 GetDataRequest 等请求有一个是否设置了 Watcher 的 flag,服务端由此来判断是否要设置相应的 Watcher。这里,ZK 为 ServerCnxn
实现了 Watcher
接口。ServerCnxn
是每个服务端上对于客户端的连接对象,它的 process(WatchedEvent)
方法就是将对应的 WatchedEvent 打包为 WatcherEvent 然后发送给客户端
Watcher 成功设置后需要关心的就是 Watcher 的触发了,本质上 Watcher 是在 ZK 集合发生状态变化的时候在客户端回调对应处理逻辑的。但是 ZK 集合发生状态变化要以服务端的状态为准,服务器维护了 ZK 集合的状态,这主要是由 ZKDatabase
和 DataTree
来实现的。当服务器判断发生了需要出发 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 的原理和实现的更多相关文章
- zk的watcher机制的实现
转载:https://www.ibm.com/developerworks/cn/opensource/os-cn-apache-zookeeper-watcher/ http://blog.csdn ...
- Zookeeper——Watcher原理详解
文章目录 引言 正文 一.如何注册监听 二.如何触发监听事件 三.事件类型有哪些 四.Watcher可以被无限次触发么?为什么要这么设计? 五.Watcher实现原理 1. 客服端发送请求 a. 初始 ...
- Zookeeper原理与Curator使用
近期打算实现一个基于Zookeeper的分布式的集群状态一致性控制, 对Zookeeper的原理不太了解, 正好学习一下, 网上找到了几篇文章, 先贴在这边, 等我熟读官方文档后, 再来补充自己的见解 ...
- zookeeper系列之异步通知模式-Watcher
1 watcher种类和事件种类 Watcher种类 1. zookeeper实例化时注入的默认Watcher 2. dataWatchers 一个Map<string Set<Watch ...
- Java分布式锁看这篇就够了
### 什么是锁? 在单进程的系统中,当存在多个线程可以同时改变某个变量(可变共享变量)时,就需要对变量或代码块做同步,使其在修改这种变量时能够线性执行消除并发修改变量. 而同步的本质是通过锁来实现的 ...
- curator 分布式锁InterProcessMutex
写这篇文章的目的主要是为了记录下自己在zookeeper 锁上踩过的坑,以及踩坑之后自己的一点认识; 从zk分布式锁原理说起,原理很简单,大家也应该都知道,简单的说就是zookeeper实现分布式锁是 ...
- zookeeper项目使用几点小结
背景 前段时间学习了zookeeper后,在新的项目中刚好派上了用场,我在项目中主要负责分布式任务调度模块的开发,对我自己来说是个不小的挑战. 分布式的任务调度,技术上我们选择了zookeeper,具 ...
- Zookeeper 入门第一篇
转载原文地址: ZooKeeper学习总结 第一篇:ZooKeeper快速入门 ZooKeeper学习总结 第二篇:ZooKeeper深入探讨 ZooKeeper学习第一期---Zookeeper简单 ...
- 基于zookeeper实现分布式配置中心(一)
最近在学习zookeeper,发现zk真的是一个优秀的中间件.在分布式环境下,可以高效解决数据管理问题.在学习的过程中,要深入zk的工作原理,并根据其特性做一些简单的分布式环境下数据管理工具.本文首先 ...
随机推荐
- 《机器学习技法》---soft-margin SVM
1. soft-margin SVM的形式 其中ξn表示每个点允许的犯错程度(偏离margin有多远),但是犯错是有代价的,也就是目标函数里面要最小化的.c控制对犯错的容忍程度. 2. 推导soft ...
- 前端利器躬行记(1)——npm
npm(Node Package Manager)是Node.js的包管理工具,相当于一个在线仓库.它提供了一个公共的平台,将分散在世界各地的包集中起来,能轻松的安装.分享和管理相关的包,不用再为搜索 ...
- Zabbix数据库空间大小使用计算
一.Zabbix的数据存储主要分类 1.历史数据 2.趋势数据 3.事件数据 二.每秒处理的数据量 顾名思义,例如,有3000个监控项(item),每60秒取一次值,即平均每秒有50(3000/60) ...
- three.js基础前置知识
这一节是纯理论知识,用于介绍three.js的入门概念,也就是开发前需要准备的理论基础. 一,三剑客 当然就是scene,camera,renderer这三个基本要素. scene是一个用于容纳三维空 ...
- Flutter学习笔记(24)--SingleChildScrollView滚动组件
如需转载,请注明出处:Flutter学习笔记(23)--多 在我们实际的项目开发中,经常会遇到页面UI内容过多,导致手机一屏展示不完的情况出现,以Android为例,在Android中遇到这类情况的做 ...
- 13张PPT带你了解主动式消息队列处理集群
前言 偷偷和你们说,我搞了一份内部资料,该内部资料共有13张PPT,据作者透露,该PPT至少花了整整1周时间才编写完成,其内容简洁明了,内容深度足够,易于初学者理解,也给深度开发人员分享了不一样的消息 ...
- Leetcode之回溯法专题-22. 括号生成(Generate Parentheses)
Leetcode之回溯法专题-22. 括号生成(Generate Parentheses) 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合. 例如,给出 n ...
- [Python] Django框架入门
说明:Django框架入门 当前项目环境:python3.5.django-1.11 项目名:test1 应用名:booktest 命令可简写为:python manager.py xxx => ...
- GCN和GCN在文本分类中应用
1.GCN的概念 传统CNN卷积可以处理图片等欧式结构的数据,却很难处理社交网络.信息网络等非欧式结构的数据.一般图片是由c个通道h行w列的矩阵组成的,结构非常规整.而社交网络.信息网络 ...
- MSIL实用指南-生成while语句
本篇讲解怎样生成while语句.while语句是编程语言中很重要的循环语句,它的结构是while(<表达式>) <语句或语句块> 当表达式的结果为true时就一直执行语句或语句 ...