Watchers机制是ZooKeeper的一大特色,其构建了整个ZooKeeper服务端和客户端的事件通知机制,可以通过阅读以下4个类的源码更好的理解
- org.apache.zookeeper.Watcher(接口)
- org.apache.zookeeper.WatchedEvent(类)
- org.apache.zookeeper.ClientWatchManager(接口)
- org.apache.zookeeper.ZooKeeper.ZKWatchManager(ZooKeeper类中的静态内部类)
ZooKeeper允许客户端向服务端注册一个 Watcher监听,当服务端的一些指定事件触发了这个 Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能。整个 Watcher注册与通知过程如图所示。
从图中,我们可以看到, ZooKeeper的 Watcher机制主要包括客户端线程、客户端WatchManager和 ZooKeeper服务器三部分。
在具体工作流程上,简单地讲,客户端在向 ZooKeeper服务器注册 Watcher的同时,会将 Watcher对象存储在客户端的WatchManager中。当 ZooKeeper服务器端触发 Watcher事件后,会向客户端发送通知,客户端线程从 WatchManager中取出对应的 Watcher对象来执行回调逻辑。
说明:贴出的源码不是全部代码,仅仅列出重点需要说明的部分代码!!!
Watcher接口
在ZooKeeper中,接口类Watcher用于表示一个标准的事件处理器,其定义了事件通知相关的逻辑,包含KeeperState和EventType两个枚举类,分别代表了通知状态和事件类型,同时定义了事件的回调方法:process(WatchedEvent event)
此接口指定事件处理程序类必须实现的公共接口。 ZooKeeper客户端将从与其连接的ZooKeeper服务器获取各种事件。 使用此类客户端的应用程序通过向客户端注册回调对象来处理这些事件。 回调对象应该是实现Watcher接口的类的实例。
public interface Watcher {
public interface Event {
//ZooKeeper可能发生的状态的枚举
public enum KeeperState {
//废弃
@Deprecated
Unknown (-1),
//客户端处于断开状态-未与整体中的任何服务器连接
Disconnected (0),
//废弃
@Deprecated
NoSyncConnected (1),
//客户端处于连接状态-它已连接到整体中的服务器(在ZooKeeper客户端创建期间主机连接参数中指定的服务器之一)
SyncConnected (3),
//验证失败状态
AuthFailed (4),
//客户端连接到只读服务器,即当前未连接到主服务器.
//收到此状态后,唯一允许的操作是读取操作。
ConnectedReadOnly (5),
/**
* SaslAuthenticated: used to notify clients that they are SASL-authenticated,
* so that they can perform Zookeeper actions with their SASL-authorized permissions.
*/
SaslAuthenticated(6),
//服务群集已在此会话中过期。 ZooKeeper客户端连接(会话)不再有效。
//如果要访问集成,则必须创建一个新的客户端连接(实例化一个新的ZooKeeper实例)。
Expired (-112);
private final int intValue;
KeeperState(int intValue) {
this.intValue = intValue;
}
........省略部分代码........
public static KeeperState fromInt(int intValue) {
switch(intValue) {
case -1: return KeeperState.Unknown;
case 0: return KeeperState.Disconnected;
case 1: return KeeperState.NoSyncConnected;
case 3: return KeeperState.SyncConnected;
case 4: return KeeperState.AuthFailed;
case 5: return KeeperState.ConnectedReadOnly;
case 6: return KeeperState.SaslAuthenticated;
case -112: return KeeperState.Expired;
default:
throw new RuntimeException("Invalid integer value for conversion to KeeperState");
}
}
}
//ZooKeeper上可能发生的事件类型的枚举
public enum EventType {
None (-1),
NodeCreated (1),
NodeDeleted (2),
NodeDataChanged (3),
NodeChildrenChanged (4);
private final int intValue;
EventType(int intValue) {
this.intValue = intValue;
}
........省略部分代码........
public static EventType fromInt(int intValue) {
switch(intValue) {
case -1: return EventType.None;
case 1: return EventType.NodeCreated;
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");
}
}
}
}
abstract public void process(WatchedEvent event);
}
WatchedEvent类
WatchedEvent表示Watcher能够响应的ZooKeeper上的更改。 WatchedEvent包括发生的确切事件,ZooKeeper的当前状态以及事件中涉及的znode的路径。
public class WatchedEvent {
//通知状态
final private KeeperState keeperState;
//事件类型
final private EventType eventType;
//节点路径
private String path;
public WatchedEvent(EventType eventType, KeeperState keeperState, String path) {
this.keeperState = keeperState;
this.eventType = eventType;
this.path = path;
}
//将通过网络发送的WatcherEvent转换为完整的WatchedEvent
public WatchedEvent(WatcherEvent eventMessage) {
//通过int值得到相应的枚举
keeperState = KeeperState.fromInt(eventMessage.getState());
eventType = EventType.fromInt(eventMessage.getType());
path = eventMessage.getPath();
}
//将WatchedEvent转换为可以通过网络发送的类型WatcherEvent
public WatcherEvent getWrapper() {
return new WatcherEvent(eventType.getIntValue(), keeperState.getIntValue(),path);
}
........省略部分代码........
}
WatcherEvent类
WatchedEvent和WatcherEvent表示的是同个事物,都是对一个服务端事件的封装。不同的是, WatchedEvent是一个逻辑事件,用于服务端和客户端程序执行过程中所需的逻辑对象,而 WatcherEvent因为实现了序列化接口,因此可以用于网络传输。
服务端在生成 WatchedEvent事件之后,会调用 getWrapper方法将自己包装成一个可序列化的 WatcherEvent事件,以便通过网络传输到客户端。客户端在接收到服务端的这个事件对象后,首先会将 WatcherEvent事件还原成一个 WatchedEvent事件,并传递给process(WatchedEvent event)方法处理,回调方法 process根据入参就能够解析出完整的服务端事件了。
ZooKeeper使用的序列化组件是jute,需要实现Record序列化接口,有序列化serialize方法和反序列化deserialize方法
import org.apache.jute.*;
public class WatcherEvent implements Record {
private int type;
private int state;
private String path;
........省略部分代码........
public void serialize(OutputArchive a_, String tag) throws java.io.IOException {
a_.startRecord(this,tag);
a_.writeInt(type,"type");
a_.writeInt(state,"state");
a_.writeString(path,"path");
a_.endRecord(this,tag);
}
public void deserialize(InputArchive a_, String tag) throws java.io.IOException {
a_.startRecord(tag);
type=a_.readInt("type");
state=a_.readInt("state");
path=a_.readString("path");
a_.endRecord(tag);
}
........省略部分代码........
public void write(java.io.DataOutput out) throws java.io.IOException {
BinaryOutputArchive archive = new BinaryOutputArchive(out);
serialize(archive, "");
}
public void readFields(java.io.DataInput in) throws java.io.IOException {
BinaryInputArchive archive = new BinaryInputArchive(in);
deserialize(archive, "");
}
........省略部分代码........
}
ClientWatchManager接口
返回一组应该通知该事件的观察者。 管理者不得通知watchers,但是它将更新其内部结构,就像watchers已触发一样。 目的是让被调用者现在负责将事件通知watchers,可能在以后的某个时间。
public interface ClientWatchManager {
public Set<Watcher> materialize(Watcher.Event.KeeperState state,
Watcher.Event.EventType type, String path);
}
ZKWatchManager(ClientWatchManager接口的实现类)
管理watchers并处理由ClientCnxn对象生成的事件。 我们将其实现为ZooKeeper的内部类,以使公共方法不会作为ZooKeeper客户端API的一部分公开。
说明:外面的ZooKeeper类没有贴出来,只贴了内部类ZKWatchManager
private static class ZKWatchManager implements ClientWatchManager {
//节点的数据内容和数据的版本号dataVersion变更的watchers,该map是数据节点路径和watcher对象的映射
private final Map<String, Set<Watcher>> dataWatches = new HashMap<String, Set<Watcher>>();
//
private final Map<String, Set<Watcher>> existWatches = new HashMap<String, Set<Watcher>>();
//数据节点的子节点列表发生变更的watchers(新增、删除子节点触发,子节点内容变更不触发)
private final Map<String, Set<Watcher>> childWatches = new HashMap<String, Set<Watcher>>();
//客户端的默认watcher,在实例化ZooKeeper客户端对象时,可以通过构造方法传入
private volatile Watcher defaultWatcher;
final private void addTo(Set<Watcher> from, Set<Watcher> to) {
if (from != null) {
to.addAll(from);
}
}
@Override
public Set<Watcher> materialize(Watcher.Event.KeeperState state,Watcher.Event.EventType type,String clientPath)
{
Set<Watcher> result = new HashSet<Watcher>();
switch (type) {
........省略部分代码........
case NodeDataChanged:
case NodeCreated:
synchronized (dataWatches) {
addTo(dataWatches.remove(clientPath), result);
}
synchronized (existWatches) {
addTo(existWatches.remove(clientPath), result);
}
break;
case NodeChildrenChanged:
synchronized (childWatches) {
addTo(childWatches.remove(clientPath), result);
}
break;
case NodeDeleted:
synchronized (dataWatches) {
addTo(dataWatches.remove(clientPath), result);
}
// XXX This shouldn't be needed, but just in case
synchronized (existWatches) {
Set<Watcher> list = existWatches.remove(clientPath);
if (list != null) {
addTo(existWatches.remove(clientPath), result);
LOG.warn("We are triggering an exists watch for delete! Shouldn't happen!");
}
}
synchronized (childWatches) {
addTo(childWatches.remove(clientPath), result);
}
break;
........省略部分代码........
}
return result;
}
}
WatchRegistration类
该类也是ZooKeeper类的内部类
//为一个节点路径注册一个watcher
abstract class WatchRegistration {
private Watcher watcher;
private String clientPath;
public WatchRegistration(Watcher watcher, String clientPath)
{
this.watcher = watcher;
this.clientPath = clientPath;
}
abstract protected Map<String, Set<Watcher>> getWatches(int rc);
/**
* Register the watcher with the set of watches on path.
* @param rc the result code of the operation that attempted to
* add the watch on the path.
*/
public void register(int rc) {
if (shouldAddWatch(rc)) {
Map<String, Set<Watcher>> watches = getWatches(rc);
synchronized(watches) {
Set<Watcher> watchers = watches.get(clientPath);
if (watchers == null) {
watchers = new HashSet<Watcher>();
watches.put(clientPath, watchers);
}
watchers.add(watcher);
}
}
}
/**
* Determine whether the watch should be added based on return code.
* @param rc the result code of the operation that attempted to add the
* watch on the node
* @return true if the watch should be added, otw false
*/
protected boolean shouldAddWatch(int rc) {
return rc == 0;
}
}
/** Handle the special case of exists watches - they add a watcher
* even in the case where NONODE result code is returned.
*/
class ExistsWatchRegistration extends WatchRegistration {
public ExistsWatchRegistration(Watcher watcher, String clientPath) {
super(watcher, clientPath);
}
@Override
protected Map<String, Set<Watcher>> getWatches(int rc) {
return rc == 0 ? watchManager.dataWatches : watchManager.existWatches;
}
@Override
protected boolean shouldAddWatch(int rc) {
return rc == 0 || rc == KeeperException.Code.NONODE.intValue();
}
}
class DataWatchRegistration extends WatchRegistration {
public DataWatchRegistration(Watcher watcher, String clientPath) {
super(watcher, clientPath);
}
@Override
protected Map<String, Set<Watcher>> getWatches(int rc) {
return watchManager.dataWatches;
}
}
class ChildWatchRegistration extends WatchRegistration {
public ChildWatchRegistration(Watcher watcher, String clientPath) {
super(watcher, clientPath);
}
@Override
protected Map<String, Set<Watcher>> getWatches(int rc) {
return watchManager.childWatches;
}
}