0. 添加jar包
我这里由于是使用的之前搭建好的springboot框架,所以直接添加maven依赖
<!--引入zookeeper-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.3.6</version>
</dependency>
1. 创建连接
创建zookeeper连接类,该类用于提供zookeeper对象,值得注意的是zookeeper创建连接是异步进行的,也就是说在我们走完new Zookeeper()这一步,程序继续向下执行,但是zookeeper并不一定已经连接成功,所以我们这里使用CountDownLatch来阻塞当前进程,直到连接成功。
如何判断已经连接成功呢?我们在第一篇博客里面介绍过zookeeper的watcher机制,这里我们在new zookeeper对象时传入一个watcher,用来监控连接是否已经成功,该项操作需要我们传入一个实现了Watcher接口的类的对象,我这里使用匿名内部类来实现,通过判断状态来执行countDownLatch.countDown(),结束线程阻塞
public class ZookeeperConnection {
private static ZooKeeper zoo;
static CountDownLatch countDownLatch=new CountDownLatch(1);
public static ZooKeeper connect(String host) throws IOException, InterruptedException {
if (zoo != null) {
return zoo;
}
zoo=new ZooKeeper(host, 5000, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState()== Event.KeeperState.SyncConnected){
countDownLatch.countDown();
}
}
});
countDownLatch.await();
return zoo;
}
public static void close() throws InterruptedException {
zoo.close();
}
}
2. 具体操作
@Controller
@RequestMapping("/zoo")
public class ZooController {
ZooKeeper zooKeeper;
String path = "/test1";
/**
* 创建节点
*
* create(String path, byte[] data, List<ACL> acl, CreateMode createMode)
*/
@RequestMapping("createZoo")
public String createZoo() throws IOException, InterruptedException, KeeperException {
zooKeeper = ZookeeperConnection.connect("127.0.0.1");
zooKeeper.create(path, "666".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
return "/index";
}
/**
* 判断节点是否存在
*
* exists(String path, boolean watcher)
*/
@RequestMapping("existZoo")
public String existZoo() throws IOException, InterruptedException, KeeperException {
zooKeeper = ZookeeperConnection.connect("127.0.0.1");
Stat stat = zooKeeper.exists(path, true);
if (stat != null) {
System.out.println("stat = " + stat);
System.out.println("stat.getVersion() = " + stat.getVersion());
}
zooKeeper.close();
return "/index";
}
/**
* 获取节点数据
*
* getData(String path, boolean watch, Stat stat)
*/
@RequestMapping("getDataZoo")
public String getDataZoo() throws IOException, InterruptedException, KeeperException {
zooKeeper = ZookeeperConnection.connect("127.0.0.1");
byte[] data = zooKeeper.getData(path, false, null);
System.out.println(new String(data));
zooKeeper.close();
return "/index";
}
/**
* 数据变更自动获取
*
* getData(String path, Watcher watcher, Stat stat)
*/
@RequestMapping("getDataZoo2")
public String getDataZoo2() throws IOException, InterruptedException, KeeperException {
final CountDownLatch countDownLatch = new CountDownLatch(1);
zooKeeper = ZookeeperConnection.connect("127.0.0.1");
byte[] data = zooKeeper.getData(path, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
if (Event.EventType.None == watchedEvent.getType()) {
switch (watchedEvent.getState()) {
case Expired:
countDownLatch.countDown();
break;
}
} else {
try {
byte[] data1 = zooKeeper.getData(path, false, null);
System.out.println("data1 = " + new String(data1));
countDownLatch.countDown();
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, null);
return "/index";
}
/**
* 修改数据
*
* setData(String path, byte[] data, int version)
*/
@RequestMapping("setDataZoo")
public String setDataZoo() throws IOException, InterruptedException, KeeperException {
zooKeeper = ZookeeperConnection.connect("127.0.0.1");
zooKeeper.setData(path, "999".getBytes(), zooKeeper.exists(path, true).getVersion());
return "/index";
}
}
值得说明的是,watcher注册好后,使用一次就会失效,要想继续监听当前节点,就需要重新注册监听
3. zkCilent
通过上面的演示,我们已经知道了zookeeper的基本操作方法,但是这种官方提供的原生方法不仅使用起来麻烦,而且watcher用完即扔,想要再次使用还得重新注册,简直不要太麻烦!那么有没有一种好的工具让我们操作起来简单点呢?当然是有的了! 感谢开放的互联网平台,早就有大神帮我们造好了轮子,我们只需要点一下就够了!咳咳,说回正题,主角zkCilent登场了
ZkClient是由Datameer的工程师开发的开源客户端,对Zookeeper的原生API进行了包装,实现了超时重连、Watcher反复注册等功能。
首先也是maven依赖
<!--引入zkclient-->
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
写一个创建客户端的类
public class ZkcilentUtil {
private static ZkClient zkClient;
public static ZkClient getZkClient(String host){
if (zkClient != null) {
return zkClient;
}
zkClient=new ZkClient(host,10000);
return zkClient;
}
}
之后就是把我们之前的操作使用zkClient来完成
@Controller
@RequestMapping("/zoo")
public class ZooController {
ZkClient zkClient;
String path2 = "/test2222";
/** 创建节点*/
@RequestMapping("createZnode")
public String createZnodeZk() {
zkClient = ZkcilentUtil.getZkClient("127.0.0.1");
// zkClient.createEphemeral(path2,"111");//创建临时节点
zkClient.createPersistent(path2,"111");//创建永久节点
/** 设置子节点监听*/
zkClient.subscribeChildChanges(path2, new IZkChildListener() {
@Override
public void handleChildChange(String path, List<String> currentChrildren) throws Exception {
System.out.println("path = " + path);
System.out.println("currentChrildren = " + currentChrildren);
}
});
/** 设置数据监听*/
zkClient.subscribeDataChanges(path2, new IZkDataListener() {
@Override
public void handleDataChange(String path, Object data) throws Exception {
System.out.println("path = " + path+"改变了数据");
System.out.println("data = " + data);
}
@Override
public void handleDataDeleted(String path) throws Exception {
System.out.println("path = " + path+"删除了数据");
}
});
return "/index";
}
/** 获得子节点*/
@RequestMapping("getChrildrenZk")
public String getChrildrenZk(){
zkClient=ZkcilentUtil.getZkClient("127.0.0.1");
List<String> children = zkClient.getChildren(path);
System.out.println("children = " + children);
return "/index";
}
/** 获取节点数据*/
@RequestMapping("getDataZk")
public String getDataZk(){
zkClient = ZkcilentUtil.getZkClient("127.0.0.1");
String data = zkClient.readData(path2);
System.out.println("data = " + data);
return "/index";
}
/** 设置节点数据*/
@RequestMapping("setDataZk")
public String setDataZk(){
zkClient=ZkcilentUtil.getZkClient("127.0.0.1");
zkClient.writeData(path2,"222");
return "/index";
}
}
完成!