- 环境准备
1.1 准备三台装有zookeeper集群的主机(192.168.61.134,192.168.61.130,192.168.61.135),并启动zk。
1.2 开发环境springboot,jkd1.8。 - 代码实现
2.1 连接zk工具类
package com.my.zk.zookeeperdemo.conf;
import org.apache.zookeeper.ZooKeeper;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
public class ZkUtils {
private static ZooKeeper zk;
private static String zk_address = "192.168.61.134:2181,192.168.61.130:2181,192.168.61.135:2181/testConf";
// private static String zk_address = "192.168.61.134:2181,192.168.61.130:2181,192.168.61.135:2181/testLock";
private static DefaultWatch defaultWatch = new DefaultWatch();
//countDonwLatch是为了保证zk连接成功后返回可用的zk对象,连接成功之前阻塞
private static CountDownLatch init = new CountDownLatch(1);
public static ZooKeeper getZk(){
try {
zk = new ZooKeeper(zk_address,1000,defaultWatch);
defaultWatch.setCc(init);
init.await();
} catch (Exception e) {
e.printStackTrace();
}
return zk;
}
}
2.2 监控连接的Watch
package com.my.zk.zookeeperdemo.conf;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import java.util.concurrent.CountDownLatch;
public class DefaultWatch implements Watcher {
CountDownLatch cc;
public void setCc(CountDownLatch cc){
this.cc = cc;
}
@Override
public void process(WatchedEvent watchedEvent) {
switch(watchedEvent.getState()){
case Unknown:
break;
case Disconnected:
break;
case NoSyncConnected:
break;
case SyncConnected:
//连接成功后减一,执行Zkutils中的await()方法
cc.countDown();
break;
case AuthFailed:
break;
case ConnectedReadOnly:
break;
case SaslAuthenticated:
break;
case Expired:
break;
}
}
}
2.3 监控节点状态的Watch
package com.my.zk.zookeeperdemo.conf;
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;
public class WatchCallBack implements Watcher, AsyncCallback.StatCallback, AsyncCallback.DataCallback {
ZooKeeper zk;
//zookeeper异步线程与工作线程共享数据的对象(工作线程接收数据用的)
MyConf myConf;
CountDownLatch countDownLatch = new CountDownLatch(1);
public MyConf getMyConf() {
return myConf;
}
public void setMyConf(MyConf myConf) {
this.myConf = myConf;
}
public ZooKeeper getZk() {
return zk;
}
public void setZk(ZooKeeper zk) {
this.zk = zk;
}
public void aWait(){
//走这一步之前如果没有CountDownLatch(1)控制,执行该步骤的时候是不会阻塞的
zk.exists("/AppConf",this,this,"abc");
try {
//阻塞,等待判断节点存在,并且getData执行完,再countDown(),避免取不到数据
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void processResult(int i, String s, Object o, byte[] bytes, Stat stat) {
if(null != bytes){
String data = new String(bytes);
myConf.setConf(data);
//此时zookeeper的线程已经返回数据了
countDownLatch.countDown();
}
}
@Override
public void processResult(int i, String s, Object o, Stat stat) {
// stat 如果不是null,代表节点存在,可以调用getData()方法取数据了
if (null != stat){
//触发第一个回调
zk.getData("/AppConf",this,this,"abc");
}
}
@Override
public void process(WatchedEvent watchedEvent) {
switch (watchedEvent.getType()){
case None:
break;
case NodeCreated:
System.out.println("该节点被创建了。。。。。");
zk.getData("/AppConf",this,this,"abc");
break;
case NodeDeleted:
System.out.println("该节点被删除了,服务阻塞中。。。。。");
// 节点被删除了,对应的数据对象也要清空,重新阻塞工作线程
myConf.setConf("");
countDownLatch = new CountDownLatch(1);
break;
case NodeDataChanged:
System.out.println("该节点的值被改变了。。。。。");
zk.getData("/AppConf",this,this,"abc");
break;
case NodeChildrenChanged:
break;
}
}
}
2.4 数据共享层
package com.my.zk.zookeeperdemo.conf;
/**
* 用于接收zookeeper异步返回的数据
*/
public class MyConf {
private String conf;
public String getConf() {
return conf;
}
public void setConf(String conf) {
this.conf = conf;
}
}
2.5 测试类
package com.my.zk.zookeeperdemo;
import com.my.zk.zookeeperdemo.conf.MyConf;
import com.my.zk.zookeeperdemo.conf.WatchCallBack;
import com.my.zk.zookeeperdemo.conf.ZkUtils;
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 org.junit.After;
import org.junit.Before;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ZookeeperdemoApplicationTests {
ZooKeeper zk;
/*@Before
void conn() {
zk = ZkUtils.getZk();
System.out.println(null == zk?"zk连接失败":"zk连接成功");
}*/
@After
void close(){
try {
zk.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Test
void getConf(){
zk = ZkUtils.getZk();
System.out.println(null == zk?"zk连接失败":"zk连接成功");
WatchCallBack watchCallBack = new WatchCallBack();
watchCallBack.setZk(zk);
MyConf myConf = new MyConf();
watchCallBack.setMyConf(myConf);
watchCallBack.aWait();
//以下部分是真正的业务逻辑代码部分
while (true){
if (("").equals(myConf.getConf())){
System.out.println("节点被删除了,丢失了。。。。。。。");
watchCallBack.aWait();
}else {
// System.out.println(myConf.getConf());
}
try {
System.out.println("打印配置数据:"+myConf.getConf());
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.6 执行结果(获取/testConf/AppConf节点中的共享配置)
- 注意运行前,需要在zk中先创建好需要监控的节点