Zookeeper系列(二)——Zookeeper的Watch机制

2,677 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第18天,点击查看活动详情

概述

zookeeper 中提供类似unix文件结构的数据模型,每个节点分为永久节点和临时节点。现在讲另外一个关键技术—Zookeeper的Watch机制, 可以实现分布式的数据的发布/订阅功能。

一个典型的发布/订阅模型系统定义了一种一对多的订阅关系,能让多个订阅者同时监听某一个主题对象,当主题对象发生变化时,会通知所有的订阅者。在日常生活中有很多发布订阅的场景,比如我们在购物网站购物时,发现一个喜欢的商品,但是商品的价格太高了,通常网站会提供降价通知的功能,我们开启该功能后,商品降价就会通知到我们。

利用Zookeeper的监听机制和数据模型的特点,可以实现分布式锁的功能。

系列文章见Zookeeper系列笔记,带你入门到精通

Watch机制如何实现

整个watch机制类似我们的观察者模式,Zookeeper允许客户端像服务端注册一个Watcher监听,当服务端的一些事件触发了这个Watcher,那么就会向指定的客户端发送一个事件通知来实现分布式通知功能。

  1. 创建ZooKeeper客户端的时候,会创建两个线程,一个负责网络通信(send线程),另外一个负责监听(event线程)。
  1. 通过send线程将Watcher注册到Zookeeper服务端,同时Watcher对象保存到客户端的watch管理器中。
  2. Zookeper上的数据或者路径发生变化, 通过event线程异步通知给客户端。
  3. Zookeeper客户端的 Watch管理器会触发相关 Watcher来回调相应处理逻辑。

Watch机制的特点

一次性触发

数据或者路径发生变化,zookeeper会产生一个watcher事件,发送给注册监听的客户端,客户端只会接收到一次通知,如果数据还发生变化,客户端不会再收到通知。

先注册再触发

Zookeeper 中的 watch 机制,必须客户端先去服务端注册监听,这样事件发送才会触发监听,通知给客户端。

异步发送

watcher事件发送到客户端时采用异步发送的,有专门的一个linster线程。

客户端串行执行

客户端 Watcher 回调的过程是一个串行同步的过程,这为我们保证了顺序,同时,需要开发人员注意的一点是,千万不要因为一个 Watcher 的处理逻辑影响了整个客户端的 Watcher 回调。

时效性

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

轻量

Zookeper使用WatchedEvent对象来封装事件对象,这个数据结构中只包含三部分内容:通知状态、事件类型和节点路径。也就是说,Watcher 通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。例如针对 NodeDataChanged 事件,ZooKeeper 的 Watcher 只会通知客户端指定数据节点的数据内容发生了变更,而对于原始数据以及变更后的新数据都无法从这个事件中直接获取到,而是需要客户端主动重新去获取数据。这也是ZooKeeper 的 Watcher机制的一个非常重要的特性。

通知状态和事件类型

前面讲了WatchedEvent事件包含3部分内容,通知状态(KeeperState)、事件类型(EventType)和节点路径。同一个事件类型在不同的通知状态中代表的含义有所不同,下表列举了常见的通知状态和事件类型。

KeeperStateEventType触发条件说明
SyncConnected(0)None(-1)客户端与服务端成功建立连接
NodeCreated(1)Watcher 监听的对应数据节点被创建
NodeDeleted(2)Watcher 监听的对应数据节点被删除
NodeDataChanged(3)Watcher 监听的对应数据节点的数据内容发生变更
NodeChildChanged(4)Wather 监听的对应数据节点的子节点列表发生变更
Disconnected(0)None(-1)客户端与ZooKeeper 服务器断开连接此时客户端和服务器处于断开连接状态
Expired(-112)None(-1)会话超时此时客户端会话失效,通常同时也会收到SessionExpiredException 异常
AuthFailed(4)None(-1)通常有两种情况,1:使用错误的schema 进行权限检查2:SASL 权限检查失败通常同时也会收到AuthFailedException 异常

Shell验证Watcher

下面的两钟情况是常见的监听方式:

1). 监听节点数据的变化, get path [watch]

2). 监听子节点增减的变化, ls path [watch]

  1. 设置节点数据变动监听

  1. 启动另外一个session,修改alvin节点的数据。

  1. 收到监听

  1. 再次修改数据

  1. 未收到监听,验证了一次触发