前言
- 当Znode发生增加,删除,修改,以及其子Znode发生变化时,服务端可以通过Watch机制通知到客户端。那么要实现Watch,就必须实现org.apache.zookeeper.Watcher接口,重写process方法,并且将实现类的对象传入到可以Watch的方法中,这样就能实现客户端监控服务端的Znode上的一些动态变化了。那么,在原生zookeeper的API中watch机制是怎么实现的哪?
实现方式
Zookeeper中所有读操作
- exists()
- getData()
- getChildren()
都可以设置Watch选项。Watch事件具有one-time trigger(一次性触发)的特性,如果Watch监视的Znode有变化,那么就会通知设置该Watch的客户端。
实现Watcher接口,重写process方法
public class ZooKeeperWatcher implements Watcher {
/** 定义原子变量 */
AtomicInteger seq = new AtomicInteger();
/** 定义session失效时间 */
private static final int SESSION_TIMEOUT = 90000;
/** zookeeper服务器地址 */
private static final String CONNECTION_ADDR = "192.168.141.140:2181,192.168.141.141:2181,192.168.141.143:2181";
/** zk父路径设置 */
private static final String PARENT_PATH = "/p";
/** zk子路径设置 */
private static final String CHILDREN_PATH = "/p/c";
/** 进入标识 */
private static final String LOG_PREFIX_OF_MAIN = "【Main】";
/** zk变量 */
private ZooKeeper zk = null;
/** 信号量设置,用于等待zookeeper连接建立之后 通知阻塞程序继续向下执行 */
private CountDownLatch connectedSemaphore = new CountDownLatch(1);
/**
* 收到来自Server的Watcher通知后的处理。
*/
@Override
public void process(WatchedEvent event) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (event == null) {
return;
}
// 连接状态
KeeperState keeperState = event.getState();
// 事件类型
EventType eventType = event.getType();
// 受影响的path
String path = event.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 == keeperState) {
// 成功连接上ZK服务器
if (EventType.None == eventType) {
System.out.println(logPrefix + "成功连接上ZK服务器");
connectedSemaphore.countDown();
}
// 创建节点
else if (EventType.NodeCreated == eventType) {
System.out.println(logPrefix + "节点创建");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//this.exists(path, true);
}
// 更新节点
else if (EventType.NodeDataChanged == eventType) {
System.out.println(logPrefix + "节点数据更新");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(logPrefix + "数据内容: "
+ this.readData(PARENT_PATH, true));
}
// 更新子节点
else if (EventType.NodeChildrenChanged == eventType) {
System.out.println(logPrefix + "子节点变更");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(logPrefix + "子节点列表:" + this.getChildren(PARENT_PATH, true));
}
// 删除节点
else if (EventType.NodeDeleted == eventType) {
System.out.println(logPrefix + "节点 " + path + " 被删除");
}
}
System.out.println("--------------------------------------------");
}
}
增删改查方法的封装
创建连接
public void createConnection(String connectAddr, int sessionTimeout) {
this.releaseConnection();
try {
zk = new ZooKeeper(connectAddr, sessionTimeout, this);
System.out.println(LOG_PREFIX_OF_MAIN + "开始连接ZK服务器");
connectedSemaphore.await();
} catch (Exception e) {
e.printStackTrace();
}
}
关闭连接
public void releaseConnection() {
if (this.zk != null) {
try {
this.zk.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
创建节点
public boolean createPath(String path, String data, boolean needWatch) {
try {
// 设置监控(由于zookeeper的监控都是一次性的所以 每次必须设置监控)
this.zk.exists(path, needWatch);
this.zk.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
System.out.println(LOG_PREFIX_OF_MAIN + "节点创建成功 ");
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
更新节点
public boolean writeData(String path, String data) {
try {
//zk.exists(PARENT_PATH, true);
zk.getData(path, true, null);
//zk.getChildren(PARENT_PATH, true);
System.out.println(LOG_PREFIX_OF_MAIN + "更新数据成功,path:" + path
+ ", stat: " + this.zk.setData(path, data.getBytes(), -1));
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
删除节点
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();
}
}
查询节点
public String readData(String path, boolean needWatch) {
try {
return new String(this.zk.getData(path, needWatch, null));
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
增删改查封装好了,下篇博客将介绍客户端怎样监控Znode的变化。