天天看點

zookeeper - watcher(9)

watcher的特征

    zookeeper的一大特點之一就是watch機制,所有基于zookeeper的消息通知機制基本上都是建議在watcher機制之上的,比如業界比較有名的由360公司開源的Qconf的底層就是基于這個實作的,本章其實就是想将zookeeper的watcher的資料存儲、watcher的添加、watcher的觸發整個過程講解清楚,友善大家有個清晰的認識。

zookeeper - watcher(9)

watch特性

watcher的存儲

zookeeper - watcher(9)

說明:

     watcher主要分為dataWatches和childWatches,其中dataWatches是儲存節點層面的watcher對象的,childWatches是儲存子節點層面的watcher對象的。核心的點在于

WatchManager

資料結構。

zookeeper - watcher(9)

watcher的儲存結構

    WatchManager内部主要維持了

watchTable

(path->watcher)以及

watch2Paths

(watcher->path)兩個映射關系,其實這也就是代表了一個path有多個watcher,或一個watcher同時watch了多個path的場景。

    整個watcher的添加過程其實同時需要修改watchTable和watch2Paths兩個map對象,具體的修改操作可以參考上面的代碼貼圖。

watcher的添加

    zookeeper的watcher的添加一般通過三個途徑,分别是通過

exists

getData getChildren

三種方式添加,其中exists用于判斷節點是否存在,getData擷取節點資料,getChildren擷取子節點。其實從這個地方我們可以看出來其實watcher的添加其實是附加動作,基本上都是在主動作的同時添加一下watcher。

    從下面的API我們其實就看出來了,對應上面提到的3個動作提供3個接口用于添加watcher動作。

zookeeper - watcher(9)

watcher的添加方式

exists添加watcher

zookeeper - watcher(9)

exists的watcher

    直覺的感受就是我們在執行exists的api的時候,我們最終通過statNode的接口儲存了watcher,

注意這裡儲存在dataWatches當中

getData添加watcher

zookeeper - watcher(9)

getData的watcher

    直覺的感受就是我們再執行getData的時候,我們最終通過addWatch的接口儲存了watcher,

getChildren添加watcher

zookeeper - watcher(9)

getChildren的watcher

注意這裡的儲存在childWatches當中

watcher添加過程

zookeeper - watcher(9)

資料流入口

    其實資料是在FinalRequestProcessor當中進行寫入的,根據OpCode執行的寫入操作,是以基本上我認為通過這個圖基本上能夠了解到watcher寫入的入口了。

watcher的觸發

zookeeper - watcher(9)

watcher觸發後動作

    從這裡我們可以看出來并且印證一個觀點,那就是watcher是一次性觸發的,原因就在于每次我們triggerWatch的時候我們都會把watcher從watchTable當中移除,根據具體觸發的path路徑找到對應的watcher,然後移除watcher并觸發watcher的後續動作。注意這裡觸發了watcher。

zookeeper - watcher(9)

觸發watcher的動作

    從這裡我們可以看出來,觸發watcher的幾個動作主要包括建立節點(createNode)、删除節點(deleteNode)、資料變更(setData)。

zookeeper - watcher(9)

同時觸發dataWatches和childWatches

    從上圖可以看出來我們在建立節點的時候會同時判斷是否觸發dataWatches和childWatches,這也就解釋了childWatches是如何被觸發的。