Zookeeper_watch机制核心讲解
生活随笔
收集整理的這篇文章主要介紹了
Zookeeper_watch机制核心讲解
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
zookeeper第一次接觸,API倒是很簡單,watcher的東西,確實是有點復雜,咱們的demo講的也有點亂,今天把這個重新講一下,我覺得第一遍不懂得沒關系,如果第二遍你們還是不懂的話,你的好好研究了,本來原生的API就是有點麻煩,沒辦法,關于API的調用就不說了,API怎么去使用,你自己去研究,我只能將一個大概的,今天講一個比較主要的,zookeeper中,API中,無論是什么set方法啊,還是一些get方法,還是其他一系列的方法,總有一個布爾類型watcher,這個東西很頭疼,我們簡單看一眼,zookeeper里面有一個watch事件,watch事件和watcher是兩碼事,watch事件是一次性觸發的,當watch監視的數據發生變化的時候,zookeeper會主動的去通知client端,client端就稱之為watcher,有兩大類,第一大類是咱們主要要關心的,數據一旦發生變化,zookeeper主動去觸發的一個事件,事件返回給watcher,client端,數據節點發生變化,它會有相應的事件產生,比如節點的創建,NodeCreate,還有NodeDataChanged,節點的數據發生變更,還有NodeChildChanged,我的節點的子節點發生變更,還有個叫NodeDeleted,就是把當前節點刪除了,他有這4個事件,其實這4個事件是針對于你監聽的,觀察那一個節點而言的,他并不代表任何的子節點,這是咱們對于數據發生變更產生的4種事件,還有一個是狀態類型,watcher和另外兩個角色,leader和follower,Disconnected是連接不上,連接上是SyncConnected,還有認證失敗和過期,這4種類型,那你現在知道他的類型變化了
咱們畫一個圖看一下什么叫watcher和watch,好像有點抽象繁瑣,首先這塊是一個zookeeper,然后這邊是我的一個client端,這也是一個client端,有好多個client端,zookeeper就是一個集群,里面有3個Node,有3個節點,第一個是Follower,第二個是leader,第三個是follower,這個就稱之為一個集群,但是他們的視圖都是單一的視圖,單一的view,他們三者之間的數據都是同步的,遵循原子消息廣播,利用了復制算法,下面我又一個C1,還有一個C2,兩個client端,我的WEB應用我就部署了兩份了,假如現在zookeeper上的結構,有一個根節點就是斜杠/,下面有一個/zk節點,還有一個/parent節點,假如我C1這個節點,想去監控parent節點的話,他們兩個就建立一個連接了,相當于建立一個watch連接,對應的C1可以稱之為一個watcher,你可以簡單這么去理解,當然其實說實話,你一個client可以擁有不同的watcher,可以擁有多個的,你代碼上new了幾個,他就產生了幾個watcher,一般來講,一個節點就應該有一個watcher,你可以簡單的理解什么啊,先不說太復雜的,C1監聽這個/parent,你可以把C1當做一個watcher,如果C1監聽這個/parent,我對應的/zk節點是不是也可以建立一個watcher,還可以建立一個watcher,這是可以的,我當前C1這個節點,咱們的client端就可以產生兩個watcher,怎么去建立呢,就是new一個watcher,咱們先不考慮藍色的那個,咱們去看紅色的,到底是怎么去進行監控呢,咱們寫一段代碼,單獨的process,單獨的一個線程去跟我的zk,watcher的一個關系,一旦我的C2對于/parent這個節點,發生了任何的數據修改,你只要update,或者delete,只要下面加了一個孩子節點了,那么對應的zk數據發生變化了,他就觸發事件,觸發完事件之后直接就返回去給了C1的client端,C1的client端就直接收到這個反饋了,收到反饋到底是什么變更,你自己通過process里面的代碼,自己if else判斷,判斷這個事件的狀態到底是什么,然后你做相應的代碼的處理,當然有一個非常重要的問題,那什么是watch呢,我對你進行監控的動作稱之為watch,watcher表示人你可以這么去理解,就是節點對應的watcher,動作就是watch,其實正常來講咱們的watch是一次性的,并不是一直在這里監聽,我這邊第一次對她進行修改,然后再次去修改一次,如果你watch了一次的話,就接收到一次,第二次再去修改的話,這邊就搜不到了這個zookeeper去設計的時候就是這么去做的,watch事件就是一次性的,如果你想一直去監聽這個節點的話,那你就需要重復的去進行watch,watch有兩種方案,觸發完了下次還要不要監聽,還要,那就寫成true,那這里的true指的是誰監聽啊,指的是之前的上次的監聽,還讓他繼續去監聽,第二種方案是你自己真正的再去new一個,再new一個watcher,再去new一個watcher,那可能就是一個新的watcher,只能是這樣的一個邏輯,可能說起來還有有點麻煩,今天講完了大概也能理解了吧,就是這個邏輯,既然是這樣的話,咱們來看一下代碼,代碼來說明問題,代碼寫的比較亂,其實是因為什么啊
package com.learn.zookeeper.watcher;import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;/*** ZooKeeperWatcher這個類實現Watcher接口必須要重寫一個方法* * 我連接上zookeeper了以后* 我會發生什么操作* * * @author Leon.Sun**/
public class ZooKeeperWatcher implements Watcher {/** 定義原子變量 *//*** 然后我上面定義了一個AtomicInteger變量* 實現這種原子性*/AtomicInteger seq = new AtomicInteger();/** 定義session失效時間 *//*** 然后我的超時時間是10秒*/private static final int SESSION_TIMEOUT = 10000;/** zookeeper服務器地址 */
// private static final String CONNECTION_ADDR = "192.168.1.121:2181,192.168.1.122:2181,192.168.1.123:2181";/*** 我的服務器的地址*/private static final String CONNECTION_ADDR = "59.110.138.145:2181";/** zk父路徑設置 *//*** 當前有一個/p的節點* */private static final String PARENT_PATH = "/p";/** zk子路徑設置 *//*** 然后/p下面有一個/c* 現在就兩個節點*/private static final String CHILDREN_PATH = "/p/c";/** 進入標識 *//*** 這里是一個靜態的字符串* 叫main一個靜態的字符串*/private static final String LOG_PREFIX_OF_MAIN = "【Main】";/** zk變量 *//*** 這里有一個zookeeper的實例* 我在創建zk實例的時候* 就new出來* */private ZooKeeper zk = null;/**用于等待zookeeper連接建立之后 通知阻塞程序繼續向下執行 *//*** 咱們之前也看過helloworde程序了* 咱們想異步的從client端連接服務器的時候* 都是通過CountDownLatch去做一個異步的回調* 程序一開始是阻塞的* 連接成功了就使用countdown* 然后讓他繼續往下走* 他只是一個這個邏輯* 這幾個成員變量簡單的說一下*/private CountDownLatch connectedSemaphore = new CountDownLatch(1);/*** 創建ZK連接* * 這里面有一個createConnection* 就是創建連接唄* * @param connectAddr ZK服務器地址列表* @param sessionTimeout Session超時時間*/public void createConnection(String connectAddr, int sessionTimeout) {/*** 就是有重復創建連接的時候先釋放一下連接* 就是后面的這個代碼*/this.releaseConnection();try {//this表示把當前對象進行傳遞到其中去(也就是在主函數里實例化的new ZooKeeperWatcher()實例對象)/*** 重新的去new一個zookeeper* new出一個zookeeper還是和之前的參數差不多* ZooKeeper(String connectString, int sessionTimeout, Watcher watcher)* 先連接字符串地址address* 然后是超時的時間timeout* 以及你現在要使用的watcher了* 如果發生變更的話我都是通過這個watcher對象去對某一個節點進行監控的* 然后做完這件事情之后* * this表示watcher當前這個類* 然后給zookeeper了* * */zk = new ZooKeeper(connectAddr, sessionTimeout, this);/*** 打印了一句話* 這句話放在后面會好一點* 其實無所謂了* 開始建立連接* 還是放在這里* * */System.out.println(LOG_PREFIX_OF_MAIN + "開始連接ZK服務器");/*** 然后等著建立連接* 然后調用countdown方法繼續往下執行* 然后剩余的代碼一會再說吧*/connectedSemaphore.await();} catch (Exception e) {e.printStackTrace();}}/*** 關閉ZK連接*/public void releaseConnection() {/*** 如果你的zookeeper之前不等于空的話* 那我就直接close掉*/if (this.zk != null) {try {this.zk.close();} catch (InterruptedException e) {e.printStackTrace();}}}/*** 創建節點* * 無非就是傳一個path* 傳一個數據data* * createPath做的事是這樣的* zookeeper開始是沒有/p這個節點的* 他可以提前的去監控* 這個節點還沒有就可以去監控* 當你這個節點真正的創建的時候* 創建好了以后* 在這里就收到了* 這個/p就已經create了* 然后收到的事件 狀態是什么* NodeCreated* 現在是這個機制* ls /* 就會發現有一個/p節點* 然后我這里就會對應有一個p節點* 然后我就直接delete掉* 我再ls /一下* 發現沒了* 咱們再試一種情況吧* 我所說的那個意思* 你不watch的話那就沒有* 如果你寫false的話* 我如果寫false的話* this.zk.exists這個方法就 不watch了* 說白了就變成false了* 就是你沒有監控我的zookeeper* 也不是我的zookeeper* 這個watch它是一個布爾類型的* 要么是true要么是false* 那這個true表示誰啊* 就是我當前上下文的環境* watch那個人* 到底是不是要監控這個節點* watch指的是誰呢* 說白了就是咱們create的時候* * 或者他在createPath的* needWatch是一個布爾類型* 但是你可以新建一個watcher* 你可以new一個watcher* 新建一個watcher表示你不使用上下文的一個watcher了* 你應該單獨再來一個watcher* 你new這個watcher之后* 你還是得實現process方法* 這個就是布爾類型的watch* * 就是走這個create方法* 首先我這個/p/c是不存在的* * * * @param path 節點路徑* @param data 數據內容* @return */public boolean createPath(String path, String data, boolean needWatch) {try {//設置監控(由于zookeeper的監控都是一次性的所以 每次必須設置監控)/*** 在這里我這么去監控了一下* 我之前是寫死的* 我要做一個exists是否存在* 其實這個節點如果不存在watch也是生效的* 我這塊必須得watch一下* 不watch有什么效果呢* * 我是在創建節點之前watch的* 我needWatch等于true的* 然后我去創建這個節點* 那就是說什么事啊* 我第一次去創建這個節點之前* 我用watcher去watch了這個path* path這個時候還沒有呢* 我創建的時候觸發了* 給你client推送了消息* 你接收到了* 咱兩的watch就結束了* 因為我已經觸發了一次了* 你再次去用的話* 他就不走watch了* * 首先我先讓他watch一下* 然后節點創建的時候就觸發了watch了* 大體上就是這個意思* 我現在要說一個問題是啥啊* 你看咱們正常走的時候1,2,3,4四個連接* 有4個watch* * */this.zk.exists(path, needWatch);/*** 假如創建成功了以后* 打印一句話* 這個節點創建成功* 調用原生API的create方法* * 其實打印的是這句話* LOG_PREFIX_OF_MAIN前綴是這個【Watcher-1】* path:/p* path為什么是/p嗎* 之前create返回的就是你創建的一個路徑* 內容是content* 內容是對應的value* 為什么要執行兩次* 什么時候執行* 什么時候不執行* 你應該理解watcher就是一次性的* 第二次不行了* 為什么第二次不行了* 我說了兩遍了* node是什么啊* 我一般把p理解為node* 你看API* createPath是我自己封裝的名字* 其實你可以叫createNode* 一般來說path指的是key* path表示路徑* node表示節點* 節點包含key也包含value* 還包含其他的一些值* 我創建一個節點叫node* 節點的一部分叫做path* 你可以理解為他們兩個是一個東西* 沒必要這么去咬文嚼字* * * */System.out.println(LOG_PREFIX_OF_MAIN + "節點創建成功, Path: " + this.zk.create( /**路徑*/ /*** 節點的路徑*/path, /*** 節點路徑對應的內容*//**數據*/data.getBytes(), /**所有可見*//*** 開放式的認證*/Ids.OPEN_ACL_UNSAFE, /**永久存儲*//*** 節點的模式* PERSISTENT的模式*/CreateMode.PERSISTENT ) + /*** 然后把當前節點的數據打印一下*/", content: " + data);} catch (Exception e) {e.printStackTrace();return false;}/*** 如果return true的話表示節點創建成功*/return true;}/*** 讀取指定節點數據內容* * 讀方法就是有一個needWatch* * * @param path 節點路徑* @return*/public String readData(String path, boolean needWatch) {try {System.out.println("讀取數據操作...");/*** getData的時候就設置為true了* 設置成true了* 然后才有的* */return new String(this.zk.getData(path, needWatch, null));} catch (Exception e) {e.printStackTrace();return "";}}/*** 更新指定節點數據內容* @param path 節點路徑* @param data 數據內容* @return*/public boolean writeData(String path, String data) {try {System.out.println(LOG_PREFIX_OF_MAIN + "更新數據成功,path:" + path + ", stat: " +this.zk.setData(path, data.getBytes(), -1));} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** 刪除指定節點* * @param path* 節點path*/public void deleteNode(String path) {try {this.zk.delete(path, -1);System.out.println(LOG_PREFIX_OF_MAIN + "刪除節點成功,path:" + path);} catch (Exception e) {e.printStackTrace();}}/*** 判斷指定節點是否存在* @param path 節點路徑*/public Stat exists(String path, boolean needWatch) {try {/*** 需要繼續watch的*/return this.zk.exists(path, needWatch);} catch (Exception e) {e.printStackTrace();return null;}}/*** 獲取子節點* * 這個方法里也寫的非常的easy* 其實一點也沒變* * * @param path 節點路徑*/private List<String> getChildren(String path, boolean needWatch) {try {System.out.println("讀取子節點操作...");/*** 就是和原生API保持一致* 原生API就是這么去寫的* getChildren里面傳一個path* 就是查這個節點下的子節點有多少個* getChildren(String path, boolean watch) * 是否需要監控子節點* 昨天其實我也講了* 你是否要監控他的子節點* 說白了你這塊watch* 設置成false和true的區別* 這4個方法都是對應自己的parent* 跟子節點沒關系* 這兒如果寫成true了* 子節點新增的話* 會觸發node change這個事件* 但是是相對于我這個parent* 只不過是我下面加了一個孩子* 刪除了一個節點* 會觸發child change* 所以你要理解這個事情* 現在咱們去做一下這個操作* 之前咱們有3次了* 上面講完的都保持不變* */return this.zk.getChildren(path, needWatch);} catch (Exception e) {e.printStackTrace();return null;}}/*** 刪除所有節點* * 之前是寫死的* 但是后來發現寫死了不太好* 我既然是false的話就不watch了* 先刪除/p/c這個子節點之后* 再刪除/p* */public void deleteAllTestPath(boolean needWatch) {/*** CHILDREN_PATH是/p/c*/if(this.exists(CHILDREN_PATH, needWatch) != null){this.deleteNode(CHILDREN_PATH);}/*** PARENT_PATH是/p*/if(this.exists(PARENT_PATH, needWatch) != null){this.deleteNode(PARENT_PATH);} }/*** 收到來自Server的Watcher通知后的處理。* * process方法我們可以簡單的讀一下* 他傳遞了一個事件類型event* 就是節點發生變更以后* zookeeper發生改變了之后* 到底是什么樣的事件* 是通過event對象去判斷的* 然后做相應的操作*/@Overridepublic void process(WatchedEvent event) {/*** 傳進來以后直接打印event對象*/System.out.println("進入 process 。。。。。event = " + event);try {/*** 然后休眠了200毫秒*/Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}/*** 如果event沒有獲取到* 干脆就直接return吧*/if (event == null) {return;}// 連接狀態/*** 首先兩個狀態* 第一個是連接的狀態* 無非就是連接成功了還是失敗了還是過期了* 還是認證失敗了* 就是這4個狀態* */KeeperState keeperState = event.getState();// 事件類型/*** 然后這個是最關鍵的* 就是事件的狀態* 你的zookeeper事件發生變化了* 我會給你返回不同的狀態的* */EventType eventType = event.getType();// 受影響的path/*** 通過event可以獲取受影響的路徑是什么* 就是getPath這個方法* 比如你監控的是/p這個節點* 然后我對/p節點進行一個set操作* 數據修改了* 原先的數據是1111現在改成2222了* * 受影響的節點是誰啊* 當然是p節點了* 大體上就是這么一個操作* * */String path = event.getPath();//原子對象seq 記錄進入process的次數/*** 接下來有一個logPrefix* 你現在是進入了Watcher線程* 其實咱們的程序一共有兩個線程* 第一個線程是咱們的主線程Main* main方法* 主線程是一直往下走* 第二個線程就是咱們的process線程* 就是我new出來的watcher* 其實就是ZooKeeperWatcher* process它是一直有的* 然后你發現前綴是【Watcher-這個名字了* 那就表明是process這個線程在做一些事* 就是這個事件被我接收到了* 肯定是會打印這個前綴* 然后this.seq就相當于一個序列了* 我到底觸發了幾次事件* watch到底是watch了幾次* 我可能通過AtomicInteger進行一個計數* 每次計數都會進行加1* 最開始的時候沒有進行初始化* 第一次seq就進行加1了* 第二次就變成2了* 大體上就是這個意思* * */String logPrefix = "【Watcher-" + this.seq.incrementAndGet() + "】";/*** 這里面也打印了三個* 把之前的三個東西直接print了一下* 首先打印一句白話收到了* 收到了watcher通知了*/System.out.println(logPrefix + "收到Watcher通知");/*** 然后把連接狀態state打印了一下*/System.out.println(logPrefix + "連接狀態:\t" + keeperState.toString());/*** eventType直接toString了一下* 有一個很長的字符串* 你當前觸發變更的事件到底是什么*/System.out.println(logPrefix + "事件類型:\t" + eventType.toString());/*** 通過你自己寫代碼去判斷了* 其實最外層是這樣* 最外層其實很簡單* 監控watcher和zookeeper的一個狀態* 無非就是這4個* 要么連接上* 連接上去這里面做一些其他的事情* 要么就是連接不上* 要么就是認證失敗* 要么就是過期* 這么一看就簡單了* 到了連接上的時候* 我這個數據是delete還是update* 數據是通過eventType去做判斷* * */if (KeeperState.SyncConnected == keeperState) {// 成功連接上ZK服務器/*** eventType一共有幾種* 我們可以看一下* 第一次連接上肯定會觸發這個事件* 然后打印一句話* 表示連接上zookeeper* 剩下的其他的狀態是咱們要了解的* * 連接成功的時候至少要走一個process* 要進入一次* */if (EventType.None == eventType) {/*** 當前我的client端監控了這個/p節點* 應該是觸發了什么事件呢* 很明顯觸發的是Node change的事件*/System.out.println(logPrefix + "成功連接上ZK服務器");/*** 讓阻塞的繼續去走*/connectedSemaphore.countDown();} //創建節點/*** 第一個是create* */else if (EventType.NodeCreated == eventType) {/*** 打印了一句話* 就是創建了一個節點*/System.out.println(logPrefix + "節點創建");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}} //更新節點else if (EventType.NodeDataChanged == eventType) {/*** 節點更新的時候啥也沒做* 只是打印了一句話* 節點數據發生變更了*/System.out.println(logPrefix + "節點數據更新");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}} //更新子節點/*** 更新子節點* 如果子節點發生變更的話* 我們也打印一句話* 子節點發生變更*/else if (EventType.NodeChildrenChanged == eventType) {System.out.println(logPrefix + "子節點變更");try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}} //刪除節點else if (EventType.NodeDeleted == eventType) {/*** 刪除子節點的時候我繼續打印一句話* 子節點被刪除* 今天的代碼寫的比較簡潔* 大體上整體的process要做的事情* */System.out.println(logPrefix + "節點 " + path + " 被刪除");}else ;} else if (KeeperState.Disconnected == keeperState) {System.out.println(logPrefix + "與ZK服務器斷開連接");} else if (KeeperState.AuthFailed == keeperState) {System.out.println(logPrefix + "權限檢查失敗");} else if (KeeperState.Expired == keeperState) {System.out.println(logPrefix + "會話失效");}else ;System.out.println("--------------------------------------------");}/*** <B>方法名稱:</B>測試zookeeper監控<BR>* <B>概要說明:</B>主要測試watch功能<BR>* * 咱們一點一點看不要著急* * 它會收到兩次watch* 建立連接會打印一下* 節點創建成功主函數會打印一下* content就是咱們建立/p的value* 時間戳我們在這里是這么去寫的* System.currentTimeMillis()* 你會發現現在我們觸發了兩次process* 第一次肯定會走連接成功* 進來之后肯定會第一次觸發* 然后第二次createPath的時候* 第一個節點的時候* 里面設置的是watch等于true* 你既然要watch這個節點* 因為咱們的zookeeper去監控節點* 都是一次性的* process他只執行一次* 你如果不設置watch等于true的話* 可能目前沒有這個節點* 你想一想我當前做的是什么事* * * * @param args* @throws Exception*/public static void main(String[] args) throws Exception {//建立watcher //當前客戶端可以稱為一個watcher 觀察者角色/*** 創建zookeeper* */ZooKeeperWatcher zkWatch = new ZooKeeperWatcher();//創建連接 zkWatch.createConnection(CONNECTION_ADDR, SESSION_TIMEOUT);//System.out.println(zkWatch.zk.toString());/*** 我在這里休眠了1秒鐘* 有一個很清晰的效果* */Thread.sleep(1000);// 清理節點/*** 如果之前有節點的話* 把它都清掉* 先把這個代碼注釋掉* 先不用這個代碼了* */zkWatch.deleteAllTestPath(false);//-----------------第一步: 創建父節點 /p ------------------------///*** 就是一句話* 首先看一下這句胡是什么意思* zookeeper就是默認一個節點* 是非常干凈的* createPath(String path, String data, boolean needWatch)* 這個是我自己封裝的* needWatch是否需要watch* 我這里給的true* * 創建一個parent節點* 我這里設置的是true* * 我現在寫false了* 我rmr遞歸的去刪除* rmr /p* 然后ls /* 又清空了一次* 然后咱們暫且寫true吧* 創建節點之前* 這是我在創建節點之前watch這個parent* 所以說我創建節點的時候* 才會有第二次的watch事件* 你要理解這個事情* * 現在我watch結束了以后* * */if (zkWatch.createPath(PARENT_PATH, System.currentTimeMillis() + "", true)) {Thread.sleep(1000);//-----------------第二步: 讀取節點 /p 和 讀取/p節點下的子節點(getChildren)的區別 --------------//// 讀取數據/*** 為什么在這段代碼中寫了這兩句話呢* 其實這句話我要不寫的話* 會是一個什么情況呢* 為什么這里要寫個true* 如果我把這段也注釋掉* * 我在這里讀一下* 讀一下它會有一個什么效果呢* * 主動地watch了一下這個節點* 我這回再更新* */
// zkWatch.readData(PARENT_PATH, true);/*** 它會再次的去更新*/zkWatch.exists(PARENT_PATH, true);// 讀取子節點(監控childNodeChange事件)/*** getChildren的時候我又進行了一次watch* 這個getChildren又是什么概念呢* * 我要監控子節點的變化* * 假設我現在寫成false了* * 現在又加上這句話* 這哥們又回來了* 改成true的話* 咱們在走一下* 都是true* 四個true* 這回就變成5個了* 為什么是5個呢* 第一個節點創建的時候不說了* 就是建立連接的時候* 第二個是createParent的時候* 第三次是父節點發生變更dataChange* 就是parent發生變更* 第四次是/p/c創建的時候* 設置為true就是可以監控了* 第五次就是getChildren設置為true了* 他就會默認的去監控* 就是NodeChildrenChange事件觸發的時候* 就是parent下面如果有孩子加進來* 那我也要監聽一下* 那你怎么去做這個操作呢* 無非就是要加這個true* 如果我要是改成false* 就是和沒寫是一樣的* 現在在咱們把這個測一下* datachange nodechange* 就是getChildren那一塊* 就是parent節點發生變化* 就是狀態變了* 添加了一個孩子* 這回能理解這個意思了吧* 就是你剛才看到我的操作你能理解就行了* 它是始終監控父節點* 但是它是兩個方法* 父節點發生變更* 它會額外有一個方法* 它會永遠監控parent這個人* 我有一個額外的事件* 子節點產生變化了* 他觸發的還是parent的這個事件* 這個事件叫個名* 什么名呢* Child Node Change* 叫做子節點變更* 其實事件變更的還是parent* 跟節點沒關系* 因為事件的觸發者還是parent* 只不過他額外加了一個* 你這兩個節點沒有任何關系的* 但是我昨天說難在哪啊* 你這個子節點無論是create還是update還是delete* 父節點永遠都是child node change* 就是你沒法判斷子節點是新增,刪除還是修改了* 這個是很惡心的事情* 如果你在工作中你真的想去實現這個的話* 你這會你得下會功夫* 這個東西越講越復雜* 你自己去看* * * */
// zkWatch.getChildren(PARENT_PATH, true);/*** 這里我們變成child* */
// zkWatch.getChildren(CHILDREN_PATH, true);// 更新數據/*** 咱們看這個* 先不考慮子節點這塊* 直接更新這個節點* 看行不行* 看會不會觸發update操作* nodeChange這個操作* 當前我的zookeeper下還是清空的* 一共就觸發了兩次watch* 10秒鐘以后就結束了* 第一次是咱們連接的時候* 第二次是咱們create的時候* 我去創建節點的時候over了* 但是沒第三次了* 節點更新了你也沒有觸發watch事件* 這是因為咱們沒有去進行watch這個操作* 并沒有去進行watch這個操作* 數據已經更新了* 而且更新成功了* 返回一個stat* 因為你兩已經發生一次操作了* 如果非得做這個事* * */zkWatch.writeData(PARENT_PATH, System.currentTimeMillis() + "");Thread.sleep(1000);// 創建子節點/*** 創建一個子節點* CHILDREN_PATH* 就是在/p下創建一個c* 寫成true或者false是有影響的* 寫false會有一個什么效果呢* * 當我create的時候事件一點也沒有觸發* 原因是你沒有去監控子節點* 所以還是正常的* 第一次建立連接的時候* 第二次父節點parent create的時候* 第三次就是父節點發生變更的時候* NodeDataChange了一下* 我最后只是單純的去創建了一個子節點* 沒有觸發任何的watch* 原因是沒人去監控這個節點的產生* 所以會變成這個事* 然后咱們的zookeeper下還有一個這個* rmr /p* 刪掉* 這回多了一個nodecreated* 這里成true了* * * */zkWatch.createPath(CHILDREN_PATH, System.currentTimeMillis() + "", true);// zkWatch.getChildren(CHILDREN_PATH, true);//-----------------第三步: 建立子節點的觸發 --------------///*** 相當于遞歸的去創建* * 現在有加兩個事件* 現在有6次了* 就是有2次create* 因為我這里創建c1成功c2成功的時候* 多加了兩次* NodeCreate是誰啊* 是這個/p/c/c1* 是這個/p/c/c1/c2* 如果你想在/p/c這個節點下監控* 監控他的子節點發生變化的時候* 你是不是還得getChildren* 怎么去寫啊* 寫起來就有點麻煩了* 這塊我之前設置成false* * */zkWatch.createPath(CHILDREN_PATH + "/c1", System.currentTimeMillis() + "", true);zkWatch.createPath(CHILDREN_PATH + "/c1/c2", System.currentTimeMillis() + "", true);//-----------------第四步: 更新子節點數據的觸發 --------------////在進行修改之前,我們需要watch一下這個節點:
// Thread.sleep(1000);/*** 這里是對children path的一些修改*/
// zkWatch.readData(CHILDREN_PATH, true);
// zkWatch.writeData(CHILDREN_PATH, System.currentTimeMillis() + "");}// Thread.sleep(10000);// 清理節點/*** 清空節點咱們先注釋掉* 咱們要手動的去清空*/
// zkWatch.deleteAllTestPath(false);/*** 等了10秒鐘以后我就釋放連接了*/Thread.sleep(5000);/*** 釋放連接咱們先保留* */zkWatch.releaseConnection();}}
?
總結
以上是生活随笔為你收集整理的Zookeeper_watch机制核心讲解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Zookeeper_原生API操作(二)
- 下一篇: Zookeeper_安全认证讲解