https://blog.csdn.net/lyzx_in_csdn/article/details/79429864
1、简述过程
ZooKeeper 的 Watcher 机制主要包括客户端线程、客户端 WatchManager 和 ZooKeeper 服务器三部分;
在创建一个 ZooKeeper 客户端对象实例时,可以向构造方法中传入一个默认的 Watcher;
这个 Watcher 将作为整个 ZooKeeper会话期间的默认 Watcher,会一直被保存在客户端 ZKWatchManager 的 defaultWatcher 中。另外,ZooKeeper 客户端也可以通过 getData、exists 和 getChildren 三个接口来向 ZooKeeper 服务器注册 Watcher,无论哪种方式,注册 Watcher 的工作原理都是一致的;
2、测试代码:
import java.io.IOException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
public class ZkUtil {
public static ZooKeeper zk = getZk();
public static ZooKeeper getZk() {
try {
final String url = "192.168.4.232:2181";
ZooKeeper zk = new ZooKeeper(url, 300, new Watcher() {
public void process(WatchedEvent event) {
System.out.println(">>::" + event.getType());
}
});
System.out.println("zk客户端初始化完毕.....");
return zk;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public void closeZk(ZooKeeper zk) {
try {
if (null != zk)
zk.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void operate(String path, String data, int operateType) {
if (operateType == 1) {
//create
try {
zk.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
} catch (Exception e) {
e.printStackTrace();
}
} else if (operateType == 2) {
//delete
try {
zk.delete(path, -1);
} catch (Exception e) {
e.printStackTrace();
}
} else if (operateType == 3) {
//setData
try {
zk.setData(path, data.getBytes(), -1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.util.List;
class Subscriber implements Runnable {
private String path;
private ZooKeeper zk = ZkUtil.getZk();
public Subscriber(String path) {
this.path = path;
}
public void subScribe(final String name) {
try {
zk.getChildren(path, new Watcher() {
public void process(WatchedEvent event) {
Event.EventType t = event.getType();
只有子节点被改变(子节点被删除或者新增)才会通知它,所以在这里不做判断
System.out.println("我是消息订阅者[" + name + "],当前发生的事件是:" + t);
try {
List<String> children = zk.getChildren(path, false);
System.out.println("我是消息订阅者[" + name + "],当前的子节点是:" + children);
} catch (Exception e) {
e.printStackTrace();
}
一次注册事件完成后再次注册,当然上面的代码只是演示注册事件接到通知后的demo,实际业务要比这个复杂的多
subScribe(name);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
public void run() {
subScribe(Thread.currentThread().getName());
}
}
import java.util.concurrent.TimeUnit;
class Publisher implements Runnable {
private String[] paths;
private String[] datas;
private int[] operates;
public Publisher(String[] paths, String[] datas, int[] operates) {
this.paths = paths;
this.datas = datas;
this.operates = operates;
}
public void run() {
ZkUtil zkUtil = new ZkUtil();
int count = datas.length;
for (int i = 0; i < count; i++) {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
zkUtil.operate(paths[i], datas[i], operates[i]);
System.out.println("消息发布者[" + Thread.currentThread().getName() + "],path:" + paths[i] + ",data:" + datas[i] + ",operate:" + operates[i]);
}
}
}
public class MainTest {
public static void main(String[] args) {
/*
* 首先在新增/test下新增 z1、z2、z3节点、然后删除掉z2节点(对于子节点的修改未涉及)
*/
String[] paths = {"/test/z1", "/test/z2", "/test/z3", "/test/z2"};
String[] data = {"v_z1", "v_z2", "v_z3", ""};
int[] operates = {1, 1, 1, 2};
//一个发布者
new Thread(new Publisher(paths, data, operates), "X-MAX").start();
//3个订阅者
new Thread(new Subscriber("/test"), "A").start();
new Thread(new Subscriber("/test"), "B").start();
new Thread(new Subscriber("/test"), "C").start();
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}