前言
原计划今天是安利好用的开源框架系列,但是最近被赶鸭子'上线',开源软件篇还没有写完,就继续源码解读。 ZooKeeper是使用得最频繁的分布式框架了,一直都感觉它功能强大,貌似无所不能,能当注册中心也能当分布式锁,那它的底层究竟是怎么设计和运行的,我也一直很好奇,所以今天来研究一下,zk的wather机制,篇幅会较长所以会分就几个章节进行解读。
ZooKeeper的Watcher机制,总的来说可以分为三个过程:
1. 客户端注册Watcher
2. 服务器处理Watcher
3. 客户端回调Watcher
整个过程涉及到的类图如下图所示: 包含以下主要的方法和类:
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的类图: watcher的源码解读
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的一次性,执行一次都删除了。
小结: 上面的类图画得有点丑,大家凑合看吧,还不太习惯画图,另外源码解读的也可能存在不到位或者理解错误的地方,还是得多看几遍代码。