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对象去判断的
	 * 然后做相应的操作
	 */
	@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秒钟
		 * 有一个很清晰的效果
		 * 
		 */
		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();
		
	}

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值