package com.msb.zk.ZkTest;
import org.apache.zookeeper.ZooKeeper;
import java.util.concurrent.CountDownLatch;
/**
* @author lcc
* @version V1.0
* @Package com.msb.zk.ZkTest
* @date 2022/4/22 10:30
*/
/*
获得zk
*/
public class ZkUtils {
private static ZooKeeper zk;
private static String address="192.168.1.136:2181,192.168.1.137:2181,192.168.1.138:2181,192.168.1.139:2181/testConf";
private static DefaultWatch watch=new DefaultWatch();
private static CountDownLatch init= new CountDownLatch(1);//这里定义计数器 是为了防止直接return zk 因为还没有去链接zk 就直接返回了
//什么时候去减? 再connected 之后再去减
public static ZooKeeper getZk(){
//这里new zk 的时候需要传入一个watch
try {
zk = new ZooKeeper(address,1000,watch);
//这里还有一个异步的操作 是去链接 创建session 的统一视图的过程
//在减之前设置 将init 穿过去让defaultwatch 执行--操作
watch.setCc(init);
init.await();//阻塞等待 CountDownLatch为0 可用了 再执行return
System.out.println("阻塞解除,已成功链接");
} catch (Exception e) {
e.printStackTrace();
}
return zk;
}
}
package com.msb.zk.ZkTest;
import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import java.util.concurrent.CountDownLatch;
/**
* @author lcc
* @version V1.0
* @Package com.msb.zk.ZkTest
* @date 2022/4/22 10:59
*/
public class WatchCallBack implements Watcher, AsyncCallback.StatCallback, AsyncCallback.DataCallback {
//因为 判断是否存在时需要watcher 和statCallback 而getData 需要Watcher 和dataCallBack
ZooKeeper zk;
MyConf conf;
CountDownLatch cc= new CountDownLatch(1);
public MyConf getConf() {
return conf;
}
public void setConf(MyConf conf) {
this.conf = conf;
}
public ZooKeeper getZk() {
return zk;
}
public void setZk(ZooKeeper zk) {
this.zk = zk;
}
public void aWait(){
//如果就这样的话还是 直接异步 就结束了 获取不到值 需要阻塞 就需要引入CountDownLatch
zk.exists("/AppConf", this,this , "oldData");
try {
cc.await();//等待 数据返回 节点数据存在 且数据取完了
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void processResult(int i, String s, Object o, byte[] data, Stat stat) {
//判断存在后再执行获取数据
if(data!=null){
//取到数据了
String s1 = new String(data);
//将从zk获取到的data 数据 转换成字符串 然后通过conf类接受 kafka 传过来的数据
conf.setConf(s1);//说明取到数据了
cc.countDown();//这里可以释放线程阻塞
}
}
@Override
public void processResult(int i, String s, Object o, Stat stat) {
//状态 判断是否存在
if(stat!=null){
//如果状态不为空说明 有数据 那么就可以取数据
zk.getData("/AppConf", this, this, "AAA");
}
}
@Override
public void process(WatchedEvent watchedEvent) {
//节点发生变化的环节 修改
switch (watchedEvent.getType()) {
case None:
break;
case NodeCreated:
System.out.println("节点数据创建");
zk.getData("/AppConf", this, this, "AAA");
break;
case NodeDeleted:
System.out.println("节点数据被删除");
conf.setConf("");
//当删除时 设置conf 内容是空
cc=new CountDownLatch(1);
//这里重新设置计数器为1 等待文件被创建后减一之后 释放阻塞线程
break;
case NodeDataChanged:
System.out.println("节点数据变更");
zk.getData("/AppConf", this, this, "AAA");
break;
case NodeChildrenChanged:
break;
}
}
}
package com.msb.zk.ZkTest;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import java.util.concurrent.CountDownLatch;
/**
* @author lcc
* @version V1.0
* @Package com.msb.zk.ZkTest
* @date 2022/4/22 10:36
*/
public class DefaultWatch implements Watcher {
CountDownLatch cc;
//这里引用CountDownLatch 是为了链接成功之后减一 就可以方形那边的阻塞await方法
public void setCc(CountDownLatch cc) {
this.cc = cc;
}
//这个default watch 是创建zk 使用的watch 与session 有关
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println(watchedEvent.toString());
//这里是watch 监听zk 的事件
switch (watchedEvent.getState()) {
case Unknown:
break;
case Disconnected:
break;
case NoSyncConnected:
break;
case SyncConnected:
//在connected 之后才能执行减1 操作
cc.countDown();
break;
case AuthFailed:
break;
case ConnectedReadOnly:
break;
case SaslAuthenticated:
break;
case Expired:
break;
}
}
}
package com.msb.zk.ZkTest;
/**
* @author lcc
* @version V1.0
* @Package com.msb.zk.ZkTest
* @date 2022/4/22 11:12
*/
/*这个类才是你最关心的地方
这里可能不止是放入的是字符串 可能是json 可能是xml
* */
public class MyConf {
private String conf;
public String getConf() {
return conf;
}
public void setConf(String conf) {
this.conf = conf;
}
}
package com.msb.zk.ZkTest;
import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* @author lcc
* @version V1.0
* @Package com.msb.zk.ZkTest
* @date 2022/4/22 10:29
*/
/*
* 获得zk
* 使用zk
* 断开zk
测试
*/
public class TestConfig {
ZooKeeper zk;
@Before
public void conn(){
//先执行这里的方法 设置zk
zk = ZkUtils.getZk();
}
@After
public void close(){
try {
zk.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Test
public void getConf() throws InterruptedException {
//怎么去取? 目录是否存在 先判断目录是否存在 如果存在 一定会执行回调方法 那么就可以执行取得操作
//因为我们在判断是否存在的时候 需要写两个watcher 和 callback 都是匿名内部类 而且 有数据后 执行取操作时 zk.getData 还有匿名内部类
//所以用一个工具类替代 简化代码
WatchCallBack watchCallBack=new WatchCallBack();
//将这里的zk 传过去
watchCallBack.setZk(zk);
//因为这是异步的方式 这里 不会阻塞 不会的等待callback返回的值 继续向下走
//另外一个 watchcall back 的获取到的数据怎么让这个线程知道 那么我们需要创建一个接受数据的类
MyConf myConf = new MyConf();
//需要让WatchCallBack 接受数据
watchCallBack.setConf(myConf);
//如果myconf 数据更新或者修改了 watchCallBack能取到结果 能获取到kafka传递过来的结果
//注意zk.exists 是异步的 如果不阻塞 那么会执行下面的方法 获取的值是空
//只有执行了callBack里面获取到值了才能继续向下走
watchCallBack.aWait();//这里我们将方法定义在watchCallBack里面的方法里 如果获得到数据 往下走 如果还没获取到数据 就在这里阻塞着
/*1节点不存在
虽然会执行getstat 但是状态为空 无法取到数据 那么就会一直阻塞 一直监听是否存在数据
//一旦创建一个节点 该节点会调用getData 里面的 watcher 和callback
就会有状态有数据了 线程不阻塞了
2节点存在
* */
while (true){
if(myConf.getConf().equals("")){
System.out.println("conf diu le ");
watchCallBack.aWait();//这里如果conf 内容为空说明文件被删除 那么久应该阻塞,等待文件创建
}else {
System.out.println(myConf.getConf());//将watchCallBack里面的数据取出来
}
Thread.sleep(200);
}
}
}