1、版本 springboot
2.2.7.RELEASE jdk 1.8 zookeeper 3.5.6 curator 3.2.0
2、pom文件:
这里只列出zookeeper 和 curator
<dependencies>
<dependency>
<groupId>com.zz</groupId>
<artifactId>zz-log</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.5.6</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- zookeeper 框架 curator-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
<version>3.2.0</version>
</dependency>
</dependencies>
3、工具类:
package com.zz.springbootproject.demo.day3;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.*;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* @description: curator 工具类 练习
* curator 解决问题:
* 解决Watch注册一次就会失效的问题
* 支持直接创建多级结点
* 提供的 API 更加简单易用
* 提供更多解决方案并且实现简单,例如:分布式锁
* 提供常用的ZooKeeper工具类
* 编程风格更舒服
* @create: 2020-08-22 14:42
*/
@Slf4j
public class CuratorUtil {
// 一个zookeeper集群只需要一个 client。劲量保证单例
private static CuratorFramework client;
// zk 服务端集群地址
private String connectString = "192.168.133.129:2181,192.168.133.129:2182,192.168.133.129:2183";
// session 超时时间
private int timeOut = 60000;
// zkclient 重试间隔时间
private int baseSleepTimeMs = 5000;
//zkclient 重试次数
private int retryCount = 5;
public CuratorUtil() {
this(null);
}
public CuratorUtil(String path) {
init(path);
}
/**
* @param
* @Description: 创建客户端
* retryPolicy,重试连接策略,有四种实现
* ExponentialBackoffRetry(重试指定的次数, 且每一次重试之间停顿的时间逐渐增加)、
* RetryNtimes(指定最大重试次数的重试策略)、
* RetryOneTimes(仅重试一次)、
* RetryUntilElapsed(一直重试直到达到规定的时间)
* @Date: 2020-08-22 14:57
*/
public void init(String path) {
if (null == client) {
synchronized (CuratorUtil.this) {
if (null == client) {
client = CuratorFrameworkFactory
.builder()
.connectString(connectString)
.sessionTimeoutMs(timeOut)
.retryPolicy(new ExponentialBackoffRetry(baseSleepTimeMs, retryCount))
.namespace(path)
.build();
client.start();
log.info("client is created at ================== {}", LocalDateTime.now());
}
}
}
}
/**
* @param
* @Description: 关闭连接
* @Date: 2020-08-22 15:15
*/
public void closeConnection() {
if (null != client) {
client.close();
}
}
/**
* @param path
* @param value
* @Description: 创建节点
* @Date: 2020-08-22 15:15
*/
public String createNode(String path, String value) throws Exception {
if (null == client) {
throw new RuntimeException("there is not connect to zkServer...");
}
String node = client
.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL_SEQUENTIAL) // 临时顺序节点
.forPath(path, value.getBytes());
log.info("create node : {}", node);
return node;
}
/**
* @param path
* @Description: 删除节点信息
* @Date: 2020-08-22 16:01
*/
public void deleteNode(String path) throws Exception {
if (null == client) {
throw new RuntimeException("there is not connect to zkServer...");
}
client
.delete()
.deletingChildrenIfNeeded()
.forPath(path);
log.info("{} is deleted ", path);
}
/**
* @param path
* @Description: 获取节点存储的值
* @Date: 2020-08-22 16:11
*/
public String getNodeData(String path) throws Exception {
if (null == client) {
throw new RuntimeException("there is not connect to zkServer...");
}
Stat stat = new Stat();
byte[] bytes = client.getData().storingStatIn(stat).forPath(path);
log.info("{} data is : {}", path, new String(bytes));
log.info("current stat version is {}, createTime is {}", stat.getVersion(), stat.getCtime());
return new String(bytes);
}
/**
* @Description: 设置节点 数据
* @param path
* @param value
* @Date: 2020-08-22 16:17
*/
public void setNodeData(String path,String value) throws Exception {
if (null == client) {
throw new RuntimeException("there is not connect to zkServer...");
}
Stat stat = client.checkExists().forPath(path);
if(null == stat){
log.info(String.format("{} Znode is not exists",path));
throw new RuntimeException(String.format("{} Znode is not exists",path));
}
String nodeData = getNodeData(path);
client.setData().withVersion(stat.getVersion()).forPath(path, value.getBytes());
log.info("{} Znode data is set. old vaule is {}, new data is {}",path,nodeData,value);
}
/**
* @Description: 创建 给定节点的监听事件 监听一个节点的更新和创建事件(不包括删除)
* @param path
* @Date: 2020-08-22 16:47
*/
public void addWatcherWithNodeCache(String path) throws Exception {
if (null == client) {
throw new RuntimeException("there is not connect to zkServer...");
}
// dataIsCompressed if true, data in the path is compressed
NodeCache nodeCache = new NodeCache(client, path,false);
NodeCacheListener listener = () -> {
ChildData currentData = nodeCache.getCurrentData();
log.info("{} Znode data is chagnge,new data is --- {}", currentData.getPath(), new String(currentData.getData()));
};
nodeCache.getListenable().addListener(listener);
nodeCache.start();
}
/**
* @Description: 监听给定节点下的子节点的创建、删除、更新
* @param path 给定节点
* @Date: 2020-08-22 17:14
*/
public void addWatcherWithChildCache(String path) throws Exception {
if (null == client) {
throw new RuntimeException("there is not connect to zkServer...");
}
//cacheData if true, node contents are cached in addition to the stat
PathChildrenCache pathChildrenCache = new PathChildrenCache(client,path,false);
PathChildrenCacheListener listener = (client, event) -> {
log.info("event path is --{} ,event type is {}" , event.getData().getPath(), event.getType());
};
pathChildrenCache.getListenable().addListener(listener);
// StartMode : NORMAL BUILD_INITIAL_CACHE POST_INITIALIZED_EVENT
pathChildrenCache.start(PathChildrenCache.StartMode.NORMAL);
}
/**
* @Description: 监听 给定节点的创建、更新(不包括删除) 以及 该节点下的子节点的创建、删除、更新动作。
* @param path
* @Date: 2020-08-22 17:35
*/
public void addWatcherWithTreeCache(String path) throws Exception {
if (null == client) {
throw new RuntimeException("there is not connect to zkServer...");
}
TreeCache treeCache = new TreeCache(client, path);
TreeCacheListener listener = (client, event) -> {
log.info("节点路径 --{} ,节点事件类型: {} , 节点值为: {}" , Objects.nonNull(event.getData()) ? event.getData().getPath() : "无数据", event.getType());
};
treeCache.getListenable().addListener(listener);
treeCache.start();
}
}
4、遇到的坑
- 版本问题:zookeeper 和curator版本兼容问题。最开始我curator 版本用的是4.2.0但是启动报错。然后换了3.2.0之后正常启动。
11:04:22.830 [main-EventThread] ERROR org.apache.curator.framework.imps.CuratorFrameworkImpl - Background exception was not retry-able or retry gave up
java.lang.NullPointerException: null
at org.apache.curator.framework.imps.EnsembleTracker.configToConnectionString(EnsembleTracker.java:185)
at org.apache.curator.framework.imps.EnsembleTracker.processConfigData(EnsembleTracker.java:206)
at org.apache.curator.framework.imps.EnsembleTracker.access$300(EnsembleTracker.java:50)
at org.apache.curator.framework.imps.EnsembleTracker$2.processResult(EnsembleTracker.java:150)
at org.apache.curator.framework.imps.CuratorFrameworkImpl.sendToBackgroundCallback(CuratorFrameworkImpl.java:883)
at org.apache.curator.framework.imps.CuratorFrameworkImpl.processBackgroundOperation(CuratorFrameworkImpl.java:653)
at org.apache.curator.framework.imps.WatcherRemovalFacade.processBackgroundOperation(WatcherRemovalFacade.java:152)
at org.apache.curator.framework.imps.GetConfigBuilderImpl$2.processResult(GetConfigBuilderImpl.java:222)
at org.apache.zookeeper.ClientCnxn$EventThread.processEvent(ClientCnxn.java:598)
at org.apache.zookeeper.ClientCnxn$EventThread.run(ClientCnxn.java:510)
- session超时,连接关闭报错。解决办法:修改配置文件:syncLimit=5
Unable to reconnect to ZooKeeper service, session 0x2000000848e0006 has expired
INFO org.apache.zookeeper.ClientCnxn - EventThread shut down for session: 0x2000000848e0006