一、前言

  前面已经分析了Watcher机制中的第一部分,即在org.apache.zookeeper下的相关类,接着来分析org.apache.zookeeper.server下的WatchManager类。

二、WatchManager源码分析

  2.1 类的属性 

public class WatchManager {
// Logger
private static final Logger LOG = LoggerFactory.getLogger(WatchManager.class); // watcher表
private final HashMap<String, HashSet<Watcher>> watchTable =
new HashMap<String, HashSet<Watcher>>(); // watcher到节点路径的映射
private final HashMap<Watcher, HashSet<String>> watch2Paths =
new HashMap<Watcher, HashSet<String>>();
}

  说明:WatcherManager类用于管理watchers和相应的触发器。watchTable表示从节点路径到watcher集合的映射,而watch2Paths则表示从watcher到所有节点路径集合的映射。

  2.2 核心方法分析

  1. size方法 

    public synchronized int size(){
int result = 0;
for(Set<Watcher> watches : watchTable.values()) { // 遍历watchTable所有的值集合(HashSet<Watcher>集合)
// 每个集合大小累加
result += watches.size();
}
// 返回结果
return result;
}

  说明:可以看到size方法是同步的,因此在多线程环境下是安全的,其主要作用是获取watchTable的大小,即遍历watchTable的值集合。

  2. addWatch方法 

    public synchronized void addWatch(String path, Watcher watcher) {
// 根据路径获取对应的所有watcher
HashSet<Watcher> list = watchTable.get(path);
if (list == null) { // 列表为空
// don't waste memory if there are few watches on a node
// rehash when the 4th entry is added, doubling size thereafter
// seems like a good compromise
// 新生成watcher集合
list = new HashSet<Watcher>(4);
// 存入watcher表
watchTable.put(path, list);
}
// 将watcher直接添加至watcher集合
list.add(watcher); // 通过watcher获取对应的所有路径
HashSet<String> paths = watch2Paths.get(watcher);
if (paths == null) { // 路径为空
// cnxns typically have many watches, so use default cap here
// 新生成hash集合
paths = new HashSet<String>();
// 将watcher和对应的paths添加至映射中
watch2Paths.put(watcher, paths);
}
// 将路径添加至paths集合
paths.add(path);
}

  说明:addWatch方法同样是同步的,其大致流程如下

  ① 通过传入的path(节点路径)从watchTable获取相应的watcher集合,进入②

  ② 判断①中的watcher是否为空,若为空,则进入③,否则,进入④

  ③ 新生成watcher集合,并将路径path和此集合添加至watchTable中,进入④

  ④ 将传入的watcher添加至watcher集合,即完成了path和watcher添加至watchTable的步骤,进入⑤

  ⑤ 通过传入的watcher从watch2Paths中获取相应的path集合,进入⑥

  ⑥ 判断path集合是否为空,若为空,则进入⑦,否则,进入⑧

  ⑦ 新生成path集合,并将watcher和paths添加至watch2Paths中,进入⑧

  ⑧ 将传入的path(节点路径)添加至path集合,即完成了path和watcher添加至watch2Paths的步骤。

  3. removeWatcher方法  

    public synchronized void removeWatcher(Watcher watcher) {
// 从wach2Paths中移除watcher,并返回watcher对应的path集合
HashSet<String> paths = watch2Paths.remove(watcher);
if (paths == null) { // 集合为空,直接返回
return;
}
for (String p : paths) { // 遍历路径集合
// 从watcher表中根据路径取出相应的watcher集合
HashSet<Watcher> list = watchTable.get(p);
if (list != null) { // 若集合不为空
// 从list中移除该watcher
list.remove(watcher);
if (list.size() == 0) { // 移除后list为空,则从watch表中移出
watchTable.remove(p);
}
}
}
}

  说明:removeWatcher用作从watch2Paths和watchTable中中移除该watcher,其大致步骤如下

  ① 从watch2Paths中移除传入的watcher,并且返回该watcher对应的路径集合,进入②

  ② 判断返回的路径集合是否为空,若为空,直接返回,否则,进入③

  ③ 遍历②中的路径集合,对每个路径,都从watchTable中取出与该路径对应的watcher集合,进入④

  ④ 若③中的watcher集合不为空,则从该集合中移除watcher,并判断移除元素后的集合大小是否为0,若为0,进入⑤

  ⑤ 从watchTable中移除路径。

  4. triggerWatch方法

    public Set<Watcher> triggerWatch(String path, EventType type, Set<Watcher> supress) {
// 根据事件类型、连接状态、节点路径创建WatchedEvent
WatchedEvent e = new WatchedEvent(type,
KeeperState.SyncConnected, path); // watcher集合
HashSet<Watcher> watchers;
synchronized (this) { // 同步块
// 从watcher表中移除path,并返回其对应的watcher集合
watchers = watchTable.remove(path);
if (watchers == null || watchers.isEmpty()) { // watcher集合为空
if (LOG.isTraceEnabled()) {
ZooTrace.logTraceMessage(LOG,
ZooTrace.EVENT_DELIVERY_TRACE_MASK,
"No watchers for " + path);
}
// 返回
return null;
}
for (Watcher w : watchers) { // 遍历watcher集合
// 根据watcher从watcher表中取出路径集合
HashSet<String> paths = watch2Paths.get(w);
if (paths != null) { // 路径集合不为空
// 则移除路径
paths.remove(path);
}
}
}
for (Watcher w : watchers) { // 遍历watcher集合
if (supress != null && supress.contains(w)) { // supress不为空并且包含watcher,则跳过
continue;
}
// 进行处理
w.process(e);
}
return watchers;
}

  说明:该方法主要用于触发watch事件,并对事件进行处理。其大致步骤如下

  ① 根据事件类型、连接状态、节点路径创建WatchedEvent,进入②

  ② 从watchTable中移除传入的path对应的键值对,并且返回path对应的watcher集合,进入③

  ③ 判断watcher集合是否为空,若为空,则之后会返回null,否则,进入④

  ④ 遍历②中的watcher集合,对每个watcher,从watch2Paths中取出path集合,进入⑤

  ⑤ 判断④中的path集合是否为空,若不为空,则从集合中移除传入的path。进入⑥

  ⑥ 再次遍历watcher集合,对每个watcher,若supress不为空并且包含了该watcher,则跳过,否则,进入⑦

  ⑦ 调用watcher的process方法进行相应处理,之后返回watcher集合。

  5. dumpWatches方法

    public synchronized void dumpWatches(PrintWriter pwriter, boolean byPath) {
if (byPath) { // 控制写入watchTable或watch2Paths
for (Entry<String, HashSet<Watcher>> e : watchTable.entrySet()) { // 遍历每个键值对
// 写入键
pwriter.println(e.getKey());
for (Watcher w : e.getValue()) { // 遍历值(HashSet<Watcher>)
pwriter.print("\t0x");
pwriter.print(Long.toHexString(((ServerCnxn)w).getSessionId()));
pwriter.print("\n");
}
}
} else {
for (Entry<Watcher, HashSet<String>> e : watch2Paths.entrySet()) { // 遍历每个键值对
// 写入"0x"
pwriter.print("0x");
pwriter.println(Long.toHexString(((ServerCnxn)e.getKey()).getSessionId()));
for (String path : e.getValue()) { // 遍历值(HashSet<String>)
//
pwriter.print("\t");
pwriter.println(path);
}
}
}
}

  说明:dumpWatches用作将watchTable或watch2Paths写入磁盘。

三、总结

  WatchManager类用作管理watcher、其对应的路径以及触发器,其方法都是针对两个映射的操作,相对简单,也谢谢各位园友的观看~ 

【Zookeeper】源码分析之Watcher机制(二)的更多相关文章

  1. 【Zookeeper】源码分析之Watcher机制(三)之Zookeeper

    一.前言 前面已经分析了Watcher机制中的大多数类,本篇对于ZKWatchManager的外部类Zookeeper进行分析. 二.Zookeeper源码分析 2.1 类的内部类 Zookeeper ...

  2. 【Zookeeper】源码分析之Watcher机制(二)之WatchManager

    一.前言 前面已经分析了Watcher机制中的第一部分,即在org.apache.zookeeper下的相关类,接着来分析org.apache.zookeeper.server下的WatchManag ...

  3. 【Zookeeper】源码分析之Watcher机制(一)

    一.前言 前面已经分析了Zookeeper持久话相关的类,下面接着分析Zookeeper中的Watcher机制所涉及到的类. 二.总体框图 对于Watcher机制而言,主要涉及的类主要如下. 说明: ...

  4. zookeeper源码分析之六session机制

    zookeeper中session意味着一个物理连接,客户端连接服务器成功之后,会发送一个连接型请求,此时就会有session 产生. session由sessionTracker产生的,sessio ...

  5. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  6. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  7. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  8. kernel 3.10内核源码分析--hung task机制

    kernel 3.10内核源码分析--hung task机制 一.相关知识: 长期以来,处于D状态(TASK_UNINTERRUPTIBLE状态)的进程 都是让人比较烦恼的问题,处于D状态的进程不能接 ...

  9. Zookeeper 源码分析-启动

    Zookeeper 源码分析-启动 博客分类: Zookeeper   本文主要介绍了zookeeper启动的过程 运行zkServer.sh start命令可以启动zookeeper.入口的main ...

随机推荐

  1. PHP中遍历stdclass object 及 json

    原文:PHP中遍历stdclass object 及 json (从网上找的模拟实例)需要操作的数据: $test=Array ( [0] => stdClass Object ( [tags] ...

  2. hdu5024(dp)

    意甲冠军: 薛期呵和王熙凤不想很接近生活(因为假定他们一起,柴可能取代王熙凤) 现在'.'事情是这样的.'#'一堵墙.薛期呵对宝让生活远: 因此,选择一个最长的公路,让他们住在两端: 路达一个转折点. ...

  3. windows安装MySQL

    1)双击MySQL安装文件mysql-essential-5.0.82-win32.msi.进入欢迎页面. 单击Nextbutton.进入到安装类型页面. 2)在安装类型页面中的第一项是典型安装,第二 ...

  4. WP 开发中.xaml 与.xaml.cs

    关于 WP 开发中.xaml 与.xaml.cs 的关系   今天我们先来看一下在WP8.1开发中最长见到的几个文件之间的关系.比较论证,在看这个问题之前我们简单看看.NET平台其他两个不同的框架: ...

  5. C#网络编程系列(两)它Socket同步TCPserver

    声明原文 笔者:竹zz  本文地址http://blog.csdn.net/zhujunxxxxx/article/details/44258719 转载请注明出处 文章系列文件夹 C#网络编程系列文 ...

  6. js获取当前指定的前几天的日期(如当前时间的前七天的日期)

    这里就不多说了,直接贴上代码: <html> <head> <meta http-equiv="Content-Type" content=" ...

  7. 更改MYSQL数据库不区分大小写表名

    今天郁闷死了,在LINUX下调一个程序老说找不到表,但是我明明是建了表的,在MYSQL的命令行下也可以查到,为什么程序就找不到表呢? 后来请教了一个老师才搞定,原来是LINUX下的MYSQL默认是要区 ...

  8. Oracle数据库面试题

    1.取出表中第31到40行的记录 mysql方案:select * from t order by id limit 30,10 oracle方案: select t2.* from (select ...

  9. linux服务创建及jq配置服务列表查看

    1.应用背景 随着业务需求,后台处理服务不断增多,对于这些服务或后台程序的查看.更新操作越来越凌乱,所以我们首先需要一个服务列表查看工具,方便查看各 服务的端口.运行状态.jar包路径等等. 2.创建 ...

  10. 基于Jquery 简单实用的弹出提示框

    基于Jquery 简单实用的弹出提示框 引言: 原生的 alert 样子看起来很粗暴,网上也有一大堆相关的插件,但是基本上都是大而全,仅仅几句话可以实现的东西,可能要引入好几十k的文件,所以话了点时间 ...