初窥Zookeeper之watch

       Zookeeper有watch事件,是一次性触发的,当watch监视的数据节点发生变化时,通知设置了该watch的客户端,即watcher

当然了,watcher监听数据发生了哪些变化,必须得有对应的事件类型了,在实现了Watcher接口重写的process方法里面,我们也是根据事件类型做我们的业务逻辑。

事件类型:(znode节点有关)

       EventType.NodeCreated:创建新节点

       EventType.NodeDataChanged:节点数据发生改变

       EventType.NodeChildrenChanged:节点的子节点发生改变(新增或删除)

       EventType.NodeDeleted:删除节点

状态类型:(和客户端连接zk服务器相关)

       KeeperState.Disconnected:与zk服务器断开连接

       KeeperState.SyncConnected:与zk服务器连接成功

       KeeperState.AythFailed:检查权限失败(ACL)

       KeeperState.Expired:会话失效

 

       watcher有以下三个特性:

1、一次性:watch事件是一次性触发的,如果想再次监控数据,必须重新设置监控

2、客户端串行执行:客户端Watcher回调过程是一个串行同步的过程,这为我们保证了顺序。

3、轻量的:WatchedEvent是zk整个Watcher通知机制的最小通知单元,只有三部分组成:通知状态、时间类型、节点路径。也就是说,具体发生了什么变化,是需要客户端自己去查询的。

 

我自己觉得需要注意的点:

1、watch事件是一次性触发的,只要触发了一次就没了。

2、如果Zookeeper新建实例时加上了Watcher,调用方法里面带上true/false参数来表示是否添加watch事件,不然只能用匿名类来添加watch事件。

 

3、可以用exists方法:exists(path,true)来添加watch事件,当下次操作path会出发watch事件

例如:

        zk.exists(path,true);//给path添加watch事件

        zk.writeData(path,"newValue");//触发watch事件 EventType->nodeDataChenage

 

        zk.exists(path,true);//给path添加watch事件

        zk.create(path,"newValue",Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);//触发watch事件 EventType->nodeCreated

 

        zk.exists(path,true);//给path添加watch事件

        zk.delete(path,-1);//触发watch事件 EventType->nodeDeleted

 

4、getChildren(path,neeWatch) //给path添加watch事件,监控子节点变化

        如果path新增或者删除节点会触发watch事件 EventType->nodechildrenChanged

 

Demo代码:

package com.demo.zookeeper.watcher;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
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.ACL;
import org.apache.zookeeper.data.Stat;

public class ZookeeperWatcher implements Watcher{
	/**定义原子变量,来看出发了多少次watch事件*/
	AtomicInteger seq = new AtomicInteger();
	/** 定义session失效时间 */
	private static final int SESSION_TIMEOUT = 10000;
	/** zookeeper服务器地址 */
	private static final String CONNECTION_ADDR = "192.168.1.111:2181,192.168.1.112:2181,192.168.1.113:2181";
	/** zk父路径设置 */
	private static final String PARENT_PATH = "/testWatch";
	/** zk子路径设置 */
	private static final String CHILDREN_PATH = "/testWatch/children";
	/**zk*/
	private ZooKeeper zk = null;
	/** 信号量设置,用于等待zookeeper连接建立之后 通知阻塞程序继续向下执行 */
	private CountDownLatch semaphore = new CountDownLatch(1);
	
	/**
	 * 创建zk连接
	 * @param connectAddr zk服务器连接地址列表
	 * @param sessionTimeout Session超时时间
	 */
	public void createConnection(String connectAddr,int sessionTimeout){
		//创建前先释放一下,防止没释放
		this.releaseConnection();
		try {
			//wathcer为自己
			this.zk = new ZooKeeper(connectAddr,sessionTimeout,this);
			System.out.println("【Main】:"+"开始连接zk服务器");
			semaphore.await();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 释放zk连接
	 */
	public void releaseConnection(){
		if(this.zk!=null){
			try {
				this.zk.close();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	/**
	 * 获取节点数据
	 * @param path 节点路径
	 * @param watch 是否添加watch事件
	 * @return
	 */
	public String readData(String path,boolean watch){
		try {
			return new String(this.zk.getData(path, watch, null));
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * 修改节点数据
	 * @param path
	 * @param data
	 */
	public void updateData(String path,String data){
		try {
			this.zk.setData(path, data.getBytes(), -1);
		} catch (KeeperException | InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 创建新节点
	 * @param path
	 * @param data
	 */
	public boolean createNode(String path,String data){
		try {
			//利用exists方法添加watch事件
			this.zk.exists(path, true);
			//因为上面添加了watch事件,所以接下来的创建节点一定会触发watch事件
			this.zk.create(path, 
					data.getBytes(), 
					Ids.OPEN_ACL_UNSAFE, //打开ACL,不进行权限控制
					CreateMode.PERSISTENT);//永久节点
			System.out.println("【Main】:节点创建成功,Path:"+path);
		} catch (KeeperException | InterruptedException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	/**
	 * 删除节点
	 * @param path
	 * @return
	 */
	public boolean deleteNode(String path){
		try {
			this.zk.delete(path,-1);
		} catch (InterruptedException | KeeperException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	/**
	 * 判断指定节点是否存在
	 * @param path 节点路径
	 */
	public Stat exists(String path, boolean needWatch) {
		try {
			return this.zk.exists(path, needWatch);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * 获取子节点
	 * @param path
	 * @param watch
	 * @return
	 */
	public List<String> getChildren(String path,boolean watch){
		List<String> pathList = new ArrayList<String>();
		try {
			pathList = this.zk.getChildren(path, watch);
		} catch (KeeperException | InterruptedException e) {
			e.printStackTrace();
			return null;
		}
		return pathList;
	}
	
	/**
	 * 删除所有节点,因为创建的节点是限制的,所以这么直接删除
	 */
	public void deleteAllTestPath() {
		if(this.exists(CHILDREN_PATH, false) != null){
			this.deleteNode(CHILDREN_PATH);
		}
		if(this.exists(PARENT_PATH, false) != null){
			this.deleteNode(PARENT_PATH);
		}		
	}
	
	@Override
	public void process(WatchedEvent watchedEvent) {
		//获取连接事件
		KeeperState keeperState = watchedEvent.getState();
		//获取节点事件
		EventType eventType = watchedEvent.getType();
		// 受影响的path
		String path = watchedEvent.getPath();
		
		String logPrefix = "【Watcher-" + this.seq.incrementAndGet() + "】:";

		System.out.println(logPrefix + "收到Watcher通知");
		System.out.println(logPrefix + "连接状态:\t" + keeperState.toString());
		System.out.println(logPrefix + "事件类型:\t" + eventType.toString());
				
		if(KeeperState.SyncConnected.equals(keeperState)){//成功连接
			if(EventType.None.equals(eventType)){
				System.out.println(logPrefix + "成功连接上ZK服务器");
				semaphore.countDown();
			}else if(EventType.NodeCreated.equals(eventType)){
				System.out.println(logPrefix + "创建节点:" + path);
			}else if(EventType.NodeDataChanged.equals(eventType)){
				System.out.println(logPrefix + "修改节点"+path+"数据,修改为:"+this.readData(path,false));
			}else if(EventType.NodeDeleted.equals(eventType)){
				System.out.println(logPrefix + "删除节点:"+path);
			}else if(EventType.NodeChildrenChanged.equals(eventType)){
				System.out.println(logPrefix + path +"的子节点变更");
			}
		}else if(KeeperState.Disconnected.equals(keeperState)){
			System.out.println(logPrefix + "与ZK服务器断开连接");
		}else if(KeeperState.AuthFailed.equals(keeperState)){
			System.out.println(logPrefix + "权限检查失败");
		}else if(KeeperState.Expired.equals(keeperState)){
			System.out.println(logPrefix + "会话失效");
		}
	}
	
	/**
	 * <B>方法名称:</B>测试zookeeper监控<BR>
	 * <B>概要说明:</B>主要测试watch功能<BR>
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {

		//建立watcher
		ZookeeperWatcher zkWatch = new ZookeeperWatcher();
		//创建连接
		zkWatch.createConnection(CONNECTION_ADDR, SESSION_TIMEOUT);
		//System.out.println(zkWatch.zk.toString());
		
		Thread.sleep(1000);
		
		// 清理节点
		zkWatch.deleteAllTestPath();
		
		if (zkWatch.createNode(PARENT_PATH, System.currentTimeMillis() + "")) {
			
			Thread.sleep(1000);
			
			
			// 读取数据
			System.out.println("---------------------- read parent ----------------------------");
			//zkWatch.readData(PARENT_PATH, true);
			
			// 读取子节点,因为添加了watch事件,所以下面创建子节点时会触发watch事件
			System.out.println("---------------------- read children path ----------------------------");
			zkWatch.getChildren(PARENT_PATH, true);

			// 更新数据
			zkWatch.updateData(PARENT_PATH, System.currentTimeMillis() + "");
			
			Thread.sleep(1000);
			
			// 创建子节点
			zkWatch.createNode(CHILDREN_PATH, System.currentTimeMillis() + "");
			
			Thread.sleep(1000);
			
			zkWatch.updateData(CHILDREN_PATH, System.currentTimeMillis() + "");
		}
		
		Thread.sleep(50000);
		// 清理节点
		zkWatch.deleteAllTestPath();
		Thread.sleep(1000);
		zkWatch.releaseConnection();
	}


}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值