Zookeeper的watch机制是如何工作的?

2024-08-23 18:52
文章标签 工作 机制 zookeeper watch

本文主要是介绍Zookeeper的watch机制是如何工作的?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

ZooKeeper Watch 概述

ZooKeeper Watch 机制类似于 Java 设计模式中的观察者模式或者监听模式,唯一的不同是不再基于线程间通信,而是基于进程间通信。

ZooKeeper Watch 机制是指,客户端在所有的读命令上告知服务端:这个节点或者子节点变化时通知我,具体来说,支持的写操作有:

  • getData

  • getChildren

  • exists

例如,我们在命令行可以输入 get -w /foo,其中 -w 参数就是用于告知 ZooKeeper 服务端,当前客户端想在 /foo 节点上设置一个监听器。

注意事项:写操作不支持任何形式的 watch 注册。

另一方面,ZooKeeper 支持的事件监听类型与对应的注册方法有:

  • NodeCreated 节点创建:exits()

  • NodeDataChanged 节点数据改变:exits()getData()

  • NodeDeleted 节点删除:exits()getData()getChildren()

  • NodeChildrenChanged 子节点改变:getChildren()

注意事项:自节点数据的改变并不会引发 NodeChildrenChanged 子节点改变事件。

ZooKeeper Watch 机制的两个细节:

  • wactch 是一次性触发的(除了永久递归 watch),如果客户端需要在一个 watch 通知后继续收到相同节点的 watch 通知,那么必须再次注册 watch 一次

  • 服务端发给客户端的 watch 通知并不包含具体的节点数据,其起到的作用非常存粹:告知客户端其关注的节点发生了 watch 事件

关于 ZooKeeper Watch ,我们需要解决如下模型的实现:

模型如下图所示:

  • 服务端

    • 如何为带有 watch 的读请求进行事件注册;

    • 在节点的写操作发送时,如何触发事件,将事件通知发送给客户端;

  • 客户端

    • 命令的发送、注册用与序列化发送;

    • 事件的监听与回调;

模型如下图所示:

这里的要点是:无论是客户端还是服务端,只有将 Watcher 进行注册,才能在事件发送时进行回调,否则不进行回调。

Watcher实现原理 

client端连接后会注册一个事件,然后客户端会保存这个事件,通过zkWatcherManager保存客户端的事件注册,通知服务端Watcher为true,然后服务端会通过WahcerManager会绑定path对应的事件。如下图:

一个Watcher工作主要包含三步:客户端注册watcher、服务端处理watcher和客户端回调watcher事件。

客户端注册watcher 

在通过客户端接口调用时,可以指定一个watcher接口,以接受服务器事件的回调。在注册watcher接口后,客户端首先会对当前客户端请求request进行标记,将其设置为“使用watcher监听”,同时会封装一个watcher的注册信息watchRegistration对象,用于暂时保存数据数据节点和watcher的对象关系。由于zookeeper中的最小通讯单元为packet,因此,在clientCnxn中watchregistration又会被封装到Packet中,然后放入发送队列中等待客户端发送。随后,客户端会想服务端发送请求,并等待请求返回。完成请求之后,客户端的SendThread线程的readresponse方法负责接收服务端的请求,并将接收到的packet中的watcher注册到ZKWatcherManager中,并最终保存到dataWatchers中,用于服务端事件的回调的时候获取对应节点的对应watcher事件。

用户调用exists/getData/getChildren注册监听以后,会做几个事情

1.将请求数据封装为packet,添加到outgoingQueue队列中

2.SendThread这个线程会执行数据发送操作,主要是将outgoingQueue队列中的数据发送到服务端

3.通过clientCnxnSocket.doTransport(to,pendingQueue,ClientCnxn.this);其中ClientCnxnSocket是zookeeper客户端和服务端的连接通信的封装,有两个具体的实现类ClientCnxnSocketNetty和ClientCnxnSocketNIO;具体使用哪一个类来实现发送,是在初始化过程是在实例化Zookeeper的时候设置的,默认是ClientCnxnSocketNIO这个类

4.基于第3步,最终会在ClientCnxnSocketNetty方法中执行sendPkt将请求的数据包发送到服务端

服务端处理watcher 

其实客户端注册的watcher并不会传递到服务端,只是在客户端进行了watcher的保存,所以服务端接受到watch注册事件之后,需要将该事件进行封装,在服务端也进行保存。

ServerCnxn存储

ServerCnxn是服务端与客户端进行网络交互的一个NIO接口,代表了客户端与服务端的连接。其底层采用netty实现。所以,在接受到注册请求之后,服务端会将ServerCnxn对象和数据阶段路径保存到WatchManager的watchTable和watch2Paths中。方便事件触发时的调用。

watcher触发

当指定的节点发生相关的事件时,通过调用WatchManager的triggerWatch方法触发相关的事件。其通过将节点信息和事件类型进行封装成为watchedevent,并查找到到对应节点的注册的watcher,然后分别调用watcher的回调函数process。而在process函数中其实就是通过封装的ServerCnxn向客户端发送watchedevent数据请求。具体的watcher业务处理则在客户端处理。

客户端回调watcher事件 

SendThread是客户端开启的与服务端建立TCP长连接的线程,它会一直保持连接状态,采用心跳监测的方式确保与服务端的连接存活。

在客户端的SendThread中,当接收到服务端请求之后,会将请求反序列化成watchedevent对象,并将watchedevent对象转换为watcherevent对象,最后将watcherevent对象添加到EventThread线程,完成对watcher的回调。

EventThread是客户端的一个专门处理watcher时间的线程,其保持了一个待处理事件的队列。它根据传递的事件的类型和节点信息,从客户端的ZKWatcherManager中取出相关的watcher,将其添加到EventThread事件队列中,并在去run方法中不断取出watcher事件进行处理。

这里需要注意是:(1)事件注册是一次性的,因为在每次处理事件之后,就会将相应的watcher注册删除;(2)客户端接收到的服务端watcher事件中并不包含事件更改的具体内容,只是告知发生了这样一个watcher事件,所以,客户端在接收到watcher事件之后,需要在回调函数process中对服务端的数据进行重新获取,才能获得更改的具体内容。

ZooKeeper Watch 机制的特性总结

ZooKeeper 的 Watch 有如下几个特性需要注意:

  • 一次性

    无论是服务端还是客户端,一旦一个 Watcher 被触发,ZooKeeper都会将其从相应的存储中移除。因此,开发人员在 Watcher 的使用上要记住的一点是需要反复注册。这样的设计有效地减轻了服务端的压力。试想,如果注册一个 Watcher 之后一直有效,那么,针对那些更新非常频繁的节点,服务端会不断地向客户端发送事件通知,这无论对于网络还是服务端性能的影响都非常大。

    需要注意的是:ZooKeeper 在 3.6.0 版本中引入了永久 Watcher 机制,利用这个机制可以避免反复注册 Watcher。

  • 客户端串行执行

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

  • 轻量

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

    另外,客户端向服务端注册 Watcher 的时候,并不会把客户端真实的 Watcher 对象传递到服务端,仅仅只是在客户端请求中使用 boolean 类型属性进行了标记,同时服务端也仅仅只是保存了当前连接的 ServerCnxn 对象。如此轻量的 Watcher 机制设计,在网络开销和服务端内存开销上都是非常廉价的。

这篇关于Zookeeper的watch机制是如何工作的?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1100221

相关文章

vue监听属性watch的用法及使用场景详解

《vue监听属性watch的用法及使用场景详解》watch是vue中常用的监听器,它主要用于侦听数据的变化,在数据发生变化的时候执行一些操作,:本文主要介绍vue监听属性watch的用法及使用场景... 目录1. 监听属性 watch2. 常规用法3. 监听对象和route变化4. 使用场景附Watch 的

JAVA实现Token自动续期机制的示例代码

《JAVA实现Token自动续期机制的示例代码》本文主要介绍了JAVA实现Token自动续期机制的示例代码,通过动态调整会话生命周期平衡安全性与用户体验,解决固定有效期Token带来的风险与不便,感兴... 目录1. 固定有效期Token的内在局限性2. 自动续期机制:兼顾安全与体验的解决方案3. 总结PS

C#利用Free Spire.XLS for .NET复制Excel工作表

《C#利用FreeSpire.XLSfor.NET复制Excel工作表》在日常的.NET开发中,我们经常需要操作Excel文件,本文将详细介绍C#如何使用FreeSpire.XLSfor.NET... 目录1. 环境准备2. 核心功能3. android示例代码3.1 在同一工作簿内复制工作表3.2 在不同

Java中的分布式系统开发基于 Zookeeper 与 Dubbo 的应用案例解析

《Java中的分布式系统开发基于Zookeeper与Dubbo的应用案例解析》本文将通过实际案例,带你走进基于Zookeeper与Dubbo的分布式系统开发,本文通过实例代码给大家介绍的非常详... 目录Java 中的分布式系统开发基于 Zookeeper 与 Dubbo 的应用案例一、分布式系统中的挑战二

详解Spring中REQUIRED事务的回滚机制详解

《详解Spring中REQUIRED事务的回滚机制详解》在Spring的事务管理中,REQUIRED是最常用也是默认的事务传播属性,本文就来详细的介绍一下Spring中REQUIRED事务的回滚机制,... 目录1. REQUIRED 的定义2. REQUIRED 下的回滚机制2.1 异常触发回滚2.2 回

深入浅出Spring中的@Autowired自动注入的工作原理及实践应用

《深入浅出Spring中的@Autowired自动注入的工作原理及实践应用》在Spring框架的学习旅程中,@Autowired无疑是一个高频出现却又让初学者头疼的注解,它看似简单,却蕴含着Sprin... 目录深入浅出Spring中的@Autowired:自动注入的奥秘什么是依赖注入?@Autowired

基于Redis自动过期的流处理暂停机制

《基于Redis自动过期的流处理暂停机制》基于Redis自动过期的流处理暂停机制是一种高效、可靠且易于实现的解决方案,防止延时过大的数据影响实时处理自动恢复处理,以避免积压的数据影响实时性,下面就来详... 目录核心思路代码实现1. 初始化Redis连接和键前缀2. 接收数据时检查暂停状态3. 检测到延时过

Redis中哨兵机制和集群的区别及说明

《Redis中哨兵机制和集群的区别及说明》Redis哨兵通过主从复制实现高可用,适用于中小规模数据;集群采用分布式分片,支持动态扩展,适合大规模数据,哨兵管理简单但扩展性弱,集群性能更强但架构复杂,根... 目录一、架构设计与节点角色1. 哨兵机制(Sentinel)2. 集群(Cluster)二、数据分片

Python中的filter() 函数的工作原理及应用技巧

《Python中的filter()函数的工作原理及应用技巧》Python的filter()函数用于筛选序列元素,返回迭代器,适合函数式编程,相比列表推导式,内存更优,尤其适用于大数据集,结合lamb... 目录前言一、基本概念基本语法二、使用方式1. 使用 lambda 函数2. 使用普通函数3. 使用 N

深入理解go中interface机制

《深入理解go中interface机制》本文主要介绍了深入理解go中interface机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前言interface使用类型判断总结前言go的interface是一组method的集合,不