Zookeeper_watch机制核心讲解
2019-05-23 09:03
706 查看
[code]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种类型,那你现在知道他的 类型变化了
[code]咱们画一个图看一下什么叫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,只能是这样的一个逻辑,可能说起来还有有点麻烦,今天讲完了大概也能理解了吧,就是这个逻辑, 既然是这样的话,咱们来看一下代码,代码来说明问题,代码写的比较乱,其实是因为什么啊
[code]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( 3ff7 ); } } /** * 关闭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对象去判断的 * 然后做相应的操作 */ @Override public 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秒钟 * 有一个很清晰的效果 * */ Thr 4000 ead.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核心工作机制(zookeeper特性、zookeeper数据结构、节点类型)
- 分布式协调服务zookeeper02-zookeeper核心工作机制以及api代码演示
- Zookeeper核心机制
- zookeeper(四)核心watch和watcher
- 《zookeeper》---原生原生API中的watch机制二
- Zookeeper核心机制
- Spring的核心机制依赖注入讲解
- Spring的核心机制依赖注入讲解
- zookeeper节点Watch机制实例展示
- zookeeper工作原理、核心机制
- zookeeper节点Watch机制实例展示
- 《zookeeper》---原生原生API中的watch机制一
- zookeeper节点Watch机制实例展示
- 【zookeeper】事件 watch 机制 原理
- Zookeeper Watcher核心机制·安全认证(ACL)·实际应用
- Zookeeper核心机制
- Zookeeper核心工作机制(zookeeper特性、zookeeper数据结构、节点类型)
- zookeeper 的Watcher机制实现
- 为什么watch机制不是银弹?
- [ZooKeeper]Two-phased Commit机制活动图