您的位置:首页 > 其它

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();

}

}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: