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机制核心讲解相关推荐

  1. ZooKeeper watch机制核心讲解

    Watch ZooKeeper有watch事件,是一次性触发的[每次数据要发生变化之前都要手动创建watch],当watch监视的数据发生时,通知设置了该watch的client,客户端即watche ...

  2. Python垃圾回收机制--完美讲解!!!!!

    欢迎关注WX公众号:[程序员管小亮] [Python - 100天从新手到大师] 虽然是自己转载的但是是真的好的一篇图文并茂的对垃圾回收机制的讲解!!! 先来个概述,第二部分的画述才是厉害的. Gar ...

  3. [转载]Python垃圾回收机制--完美讲解!

    虽然是自己转载的但是是真的好的一篇图文并茂的对垃圾回收机制的讲解!!! 先来个概述,第二部分的画述才是厉害的. Garbage collection(GC) 现在的高级语言如java,c#等,都采用了 ...

  4. JVM双亲委派机制全讲解

    JVM双亲委派机制全讲解 回顾类加载器 JVM加载类时,先寻找类对应的.class文件,将其加载到内存中,加载就需要类加载器执行,一个非数组类的加载是通过类加载器加载,数组类型不通过类加载器创建,它由 ...

  5. Coordinate Attention注意力机制注释讲解

    Coordinate Attention注意力机制注释讲解 原文链接:https://arxiv.org/pdf/2103.02907.pdf 源码链接:https://github.com/Andr ...

  6. 【Spring Boot 源码研究 】- 自动化装配机制核心注解剖析

    1. 自动化装配介绍 Spring Boot针对mvc做了大量封装,简化开发者的使用,内部是如何管理资源配置,Bean配置,环境变量配置以及启动配置等? 实质是SpringBoot做了大量的注解封装, ...

  7. XGBoost核心讲解笔记(贪心学院)

    Bagging和Boosting对比 老师的PPT中对比了 Bagging 和 Boosting 两种常用的集成学习方法. Bagging:利用多个过拟合的弱学习器来获得更好的效果.典型的算法有随机森 ...

  8. ajax异步执行调用什么机制,大白话讲解JavaScript 执行机制,一看就懂

    JavaScript的运行机制 1.JavaScript为什么是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程 ...

  9. java反射进行字段类型判断_Java反射机制的讲解

    Java中的反射提供了一种运行期获取对象元信息的手段.即正常方法是通过一个类创建对象,反射方法就是通过一个对象找到一个类的信息. Java的反射机制的实现要借助于4个类:class,Construct ...

最新文章

  1. php写网页6,基于ThinkPHP6+AdminLTE框架开发的响应式企业网站CMS系统PHP源码,ThinkPHP6开发的后台权限管理系统...
  2. 级联下拉框效果,动态加载图片
  3. Shiro集成Web时的Shiro JSP标签
  4. 在Android中使用FlatBuffers - 简介
  5. [转载]C#时间函数
  6. jQuery编写widget的一些窍门
  7. 强烈推荐:SiteServer CMS开源免费的企业级CMS系统!
  8. vue openlayer单击地图事件循环多次执行_12道vue高频原理面试题,你能答出几道?
  9. spring 的jdbc和事务支持
  10. Java技术预备作业02杨欣蕊
  11. IC卡插入与触点激活时序
  12. java od_OD使用教程
  13. Python软件封装打包
  14. AD13 plugins 安装
  15. AutoRunner学习——下载安装
  16. PDF文件转换成图片的格式
  17. HTML5 重复渐变
  18. [label][WorldPress] 一个很方便查找定位WorldPress源代码位置的网址
  19. 主流大数据存储解决方案评析
  20. codeforces 711C Coloring Trees(DP)

热门文章

  1. wait 和 sleep 的区别
  2. 为什么on用的时候会失效?
  3. BZOJ1996:[HNOI2010]CHORUS 合唱队(区间DP)
  4. 2016光伏创新如何突围融资困境?
  5. SparkSql官方文档中文翻译(java版本)
  6. Linux 随机启动 Mysql​
  7. SharePoint designer 文件--新建中没有工作流
  8. Effective C# 原则34:创建大容量的Web API(译)
  9. ROS 机器人操作系统进阶实战
  10. java swftools linux_swftools linux下安装