ZooKeeper源码阅读系列-zk的Watcher机制一

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 原计划今天是安利好用的开源框架系列,但是最近被赶鸭子'上线',开源软件篇还没有写完,就继续源码解读。 ZooKeeper是使用得最频繁的分布式框架了,一直都感觉它功能强大,貌似无所不能,能当注册中心也能当分布式锁,那它的底层究竟是怎么设计和运行的,我也一直很好奇,所以今天来研究一下,zk的wather机制,篇幅会较长所以会分就几个章节进行解读。

微信截图_20220531131804.png

前言

原计划今天是安利好用的开源框架系列,但是最近被赶鸭子'上线',开源软件篇还没有写完,就继续源码解读。 ZooKeeper是使用得最频繁的分布式框架了,一直都感觉它功能强大,貌似无所不能,能当注册中心也能当分布式锁,那它的底层究竟是怎么设计和运行的,我也一直很好奇,所以今天来研究一下,zk的wather机制,篇幅会较长所以会分就几个章节进行解读。

ZooKeeper的Watcher机制,总的来说可以分为三个过程:
1. 客户端注册Watcher
2. 服务器处理Watcher
3. 客户端回调Watcher

整个过程涉及到的类图如下图所示: 微信截图_20220531132040.png包含以下主要的方法和类:

1.Zookeeper:客户端的Main.class
2.ClientWatchManager:接口,表示客户端的Watcher管理者,其定义了materialized方法,需子类实现。
3.ZKWatchManager:Zookeeper的内部类,ClientWatchManager的实现类。
4.WatchRegistration: watcher的注册包装类。
5.ClientCnxn:客户端核心线程管理类。
6.Packet:client和server的通信协议。
首先我们看下Watch在Client的存储以及体现以及主要涉及到的类
Watcher:接口类型,其定义了process方法,需子类实现。
Event:接口类型,Watcher的内部类,无任何方法。
KeeperState:枚举类型,Event的内部类,表示Zookeeper所处的状态。
EventType:枚举类型,Event的内部类,表示Zookeeper中发生的事件类型。
WatchedEvent:表示对ZooKeeper上发生变化后的反馈,包含了KeeperState和EventType。
ClientWatchManager:接口类型,表示客户端的Watcher管理者,其定义了materialized方法,需子类实现。
复制代码

在看之前要明白一个事情,Watcher是什么? ZK中引入Watcher机制来实现分布式的通知功能。 ZK允许客户端向服务端注册一个Watcher监听,当服务点的的指定事件触发监听时,那么服务端就会向客户端发送事件通知,以便客户端完成逻辑操作(即客户端向服务端注册监听,并将Watcher对象存在客户端的Watchermanager中服务端触发事件后,向客户端发送通知,客户端收到通知后从wacherManager中取出对象来执行回调逻辑) watcher的特点: 1.一次性:一旦一个Watcher被触发,ZK都会将其从相应的的存储中移除,所以Watcher是需要每注册一次,才可触发一次。 2. 客户端串行执行:客户端Watcher回调过程是一个串行同步的过程。 3. 轻量:Watcher数据结构中只包含:通知状态、事件类型和节点路径 接下来看看Watcher的类图: 微信截图_20220531132113.pngwatcher的源码解读

public interface Watcher {
    public interface Event {
     //用于记录Event发生时的zk状态(通知状态)
        public enum KeeperState { 
            @Deprecated
            //未知状态
            Unknown (-1),
            //客户端和服务端断开状态
            Disconnected (0), 
            @Deprecated
            //未同步链接
            NoSyncConnected (1),
             //客户端和服务端处于连接状态
            SyncConnected (3),
            //权限验证异常
            AuthFailed (4), 
             //只读
            ConnectedReadOnly (5),
            //SASL认证通过状态
            SaslAuthenticated(6),
            //会话过期
            Expired (-112);
         //省略......
         //用于记录event的类型
        public enum EventType {
            None (-1),
            NodeCreated (1),
            NodeDeleted (2),
            NodeDataChanged (3),
            NodeChildrenChanged (4);
            private final int intValue;
            public static EventType fromInt(int intValue) {
                switch(intValue) {
                 //客户端和服务器成功建立连接
                    case -1: return EventType.None;     
                    //watch监听对应节点被创建
                    case  1: return EventType.NodeCreated;
                    //watch 对应节点删除
                    case  2: return EventType.NodeDeleted;
                     // 监听对应节点数据内容发生改变
                    case  3: return EventType.NodeDataChanged;
                    /监听对应节点的子节点列表发生变化
                    case  4: return EventType.NodeChildrenChanged;/
                    default:
                        throw new RuntimeException("Invalid integer value for conversion to EventType");
                }
            }
        }
    }
    ////回调函数实现该函数,表示根据event执行的行为
    abstract public void process(WatchedEvent event);
}
复制代码
Watcher的类总结:
1. process函数,用于处理回调
2. 内部类Event又包含内部类KeeperState以及EventType
3. KeeperState用于记录Event发生时的zk状态(通知状态)
4. EventType用于记录Event的类型

接着看下WatchedEvent类,WatchedEvent三个属性很好的体现了这个类的意思记录了哪个节点,zk的状态以及影响的事件类型,代码如下:

public class WatchedEvent {
    //zk服务的状态
    final private KeeperState keeperState; 
    //事件类型
    final private EventType eventType; 
    //节点的路径
    private String path; 
    //转换为WatcherEvent
    public WatcherEvent getWrapper() {
        return new WatcherEvent(eventType.getIntValue(),
                keeperState.getIntValue(),
                path);
    }
}
复制代码

接着就是Watcher在Client存储的核心类ClientWatchManager接口的实现类ZKWatchManager,该方法在事件发生后,返回需要被通知的Watcher集合。在该方法中,首先会根据EventType类型确定相应的事件类型,然后根据事件类型的不同做出相应的操作,如针对None类型,即无任何事件,则首先会从三个键值对中删除clientPath对应的Watcher,然后将剩余的Watcher集合添加至结果集合;针对NodeDataChanged和NodeCreated事件而言,其会从dataWatches和existWatches中删除clientPath对应的Watcher,然后将剩余的Watcher集合添加至结果集合,从这个方法中也可看到Watcher的一次性,执行一次都删除了。

小结: 上面的类图画得有点丑,大家凑合看吧,还不太习惯画图,另外源码解读的也可能存在不到位或者理解错误的地方,还是得多看几遍代码。

相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
目录
相关文章
|
5月前
|
存储 负载均衡 算法
深入浅出Zookeeper源码(七):Leader选举
对于一个分布式集群来说,保证数据写入一致性最简单的方式就是依靠一个节点来调度和管理其他节点。在分布式系统中我们一般称其为Leader。
193 6
|
3月前
|
监控 NoSQL Java
分布式锁实现原理问题之ZooKeeper的观察器(Watcher)特点问题如何解决
分布式锁实现原理问题之ZooKeeper的观察器(Watcher)特点问题如何解决
|
2月前
|
监控 API
【zookeeper 第四篇章】监控 Watcher
ZooKeeper通过Watcher机制实现了数据的发布/订阅功能。多个订阅者可以监听同一主题对象,一旦该对象状态变化,如节点内容或子节点列表变动,ZooKeeper会实时通知所有订阅者。Watcher架构包括ZooKeeper服务端、客户端及其Watcher管理器。客户端向服务端注册Watcher并保存至本地管理器中;当状态变化时,服务端通知客户端,触发相关Watcher回调处理逻辑。
40 2
|
3月前
|
存储 数据库
zookeeper 集群环境搭建及集群选举及数据同步机制
zookeeper 集群环境搭建及集群选举及数据同步机制
54 2
|
3月前
|
API
【想进大厂还不会阅读源码】ShenYu源码-替换ZooKeeper客户端
ShenYu源码阅读。相信大家碰到源码时经常无从下手,不知道从哪开始阅读😭。我认为有一种办法可以解决大家的困扰!至此,我们发现自己开始从大量堆砌的源码中脱离开来😀。ShenYu是一个异步的,高性能的,跨语言的,响应式的 API 网关。
|
5月前
|
网络协议 中间件 数据库
Zookeeper学习系列【三】Zookeeper 集群架构、读写机制以及一致性原理(ZAB协议)
Zookeeper学习系列【三】Zookeeper 集群架构、读写机制以及一致性原理(ZAB协议)
240 0
|
5月前
|
存储 设计模式 算法
深入浅出Zookeeper源码(六):客户端的请求在服务器中经历了什么
当我们向zk发出一个数据更新请求时,这个请求的处理流程是什么样的?zk又是使用了什么共识算法来保证一致性呢?带着这些问题,我们进入今天的正文。
159 1
深入浅出Zookeeper源码(六):客户端的请求在服务器中经历了什么
|
5月前
|
存储 API
深入理解Zookeeper系列-4.Watcher原理
深入理解Zookeeper系列-4.Watcher原理
50 1
|
5月前
|
Apache
Apache ZooKeeper - 构建ZooKeeper源码环境及StandAlone模式下的服务端和客户端启动
Apache ZooKeeper - 构建ZooKeeper源码环境及StandAlone模式下的服务端和客户端启动
116 2
|
5月前
|
网络协议 数据库
深入浅出Zookeeper源码(五):BadVersionException到底是怎么一回事
最近在开发时偶尔会观测到zk报出`BadVersionException`,后在搜索引起上得知了是乐观锁相关的问题,很快就解决了问题。不过学而不思则罔:无论是单体应用还是分布式系统,在运行过程中总要有一种**机制**来保证数据排他性。接下来,我们就来看看zk是如何实现这种**机制**的。
123 1
下一篇
无影云桌面