一、前言
前面已經分析了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、其對應的路徑以及觸發器,其方法都是針對兩個對映的操作,相對簡單,也謝謝各位園友的觀看~