一、常用接口定义
1.构建zookeeper常用功能封装接口ZookeeperClient:
package com.cc.zookeeper.api;
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.data.Stat;
/**
* zookeeper常用功能封装接口
*/
public interface ZookeeperClient {
//创建持久化节点
void addPersistentNode(String path,String data) throws Exception;
//创建临时节点
String addEphemeralNode(String path,String data) throws Exception;
//修改节点String path修改路径,String data修改内容,返回值是org.apache.zookeeper.data.Stat
Stat setData(String path,String data)throws Exception;
//获取节点数据
String getData(String path)throws Exception;
//删除节点
void deletePath(String path)throws Exception;
//查看zookeeper是否连接
boolean isConnected();
//获得节点状态
Stat stat(String path)throws Exception;
/**
* @param parent 传入子节点
* @param listener 节点监听器(此处自定义接口)
*/
void listener4ChildrenPath(final String parent,final NodeListener listener)throws Exception;
//关闭节点
void close();
//获取Apche的Curator对象
CuratorFramework getClient();
}
2.自定义节点监听NodeListener接口:
package com.cc.zookeeper.api;
public interface NodeListener {
/**
* 定义当监听到节点改变时如何操作的方法
* @param client 自定义的zookeeper常用功能封装接口
* @param event 变更的节点(ChangedEvent是一个对应的接口类,自定义,当节点发生变化是直接调用其内部的方法,做具体处理)
*/
void nodeChanged(ZookeeperClient client,ChangedEvent event)throws Exception;
}
3.自定义ChangedEvent
package com.cc.zookeeper.api;
public class ChangedEvent {
//针对节点出现的变更,定义枚举
public static enum Type{
CHILD_ADDED,
CHILD_UPDATED,
CHILD_REMOVED;
}
private String path;
private String data;
private Type type;
public ChangedEvent(String path,String data,Type type) {
this.path = path;
this.data = data;
this.type = type;
}
public String getPath() {
return path;
}
public String getData() {
return data;
}
public Type getType() {
return type;
}
}
二、Cuartor封装具体实现:
1.CuratorImpl实现ZookeeperClient接口实现其封装的操作方法,实现InitializingBean接口,通过其afterPropertiesSet方法,在CuratorImpl类加载(创建)完成后创建zookeeper连接:
package com.cc.zookeeper.api.imol;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCache.StartMode;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.retry.RetryNTimes;
import org.apache.curator.utils.ThreadUtils;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import com.cc.zookeeper.api.ChangedEvent;
import com.cc.zookeeper.api.NodeListener;
import com.cc.zookeeper.api.ZookeeperClient;
import com.google.common.util.concurrent.MoreExecutors;
/**
* @ConfigurationProperties(prefix="zookeeper")
* 声明在配置实现类中配置的属性前缀为zookeeper
*/
@ConfigurationProperties(prefix="zookeeper")
public class CuratorImpl implements ZookeeperClient,InitializingBean{
//关键的日志应用,应用slf4j
private static final Logger LOGGER = LoggerFactory.getLogger(CuratorImpl.class);
//定义构建连接的参数属性,需要get/set方法
private String address;
private int sessionTimeout;
private int connectionTimeout;
//定义成员变量引用,接收方法构建的Curator对象
private CuratorFramework client;
//监听线程池:一个线程的线程池,应用threadFactory是在出现异常可以快速定位此线程
private final ExecutorService EVENT_THREAD_POOL = Executors.newFixedThreadPool(1, ThreadUtils.newThreadFactory("T-pathchildrenCache"));
//在监听过程中使用线程池,提升线程使用率 ,此处应用guava中MoreExecutors.newDirectExecutorService()静态方法直接创建,在pom中引入高版本
private final ExecutorService DIRECT_EXECUTOR = MoreExecutors.newDirectExecutorService();
//私有化构造器,禁止通过new来创建CuratorImpl对象,需要通过方法创建并返回;
private CuratorImpl() {}
@Override
public void afterPropertiesSet() throws Exception {
//创建CuratorImpl
creator();
}
private void creator() {
// 按照Apache创建Curator的流程,实例化Curator对象
client = CuratorFrameworkFactory.builder()
.connectString(address) //建立zookeeper连接地址
.retryPolicy(new RetryNTimes(Integer.MAX_VALUE, 1000)) //重试策略:Integer.MAX_VALUE无限重试,每次间隔1秒
.connectionTimeoutMs(connectionTimeout) //连接超时时间
.sessionTimeoutMs(sessionTimeout) //session过期时间
.build(); //创建连接
//启动服务
client.start();//启动Curator
}
//外部获取Curator对象的方法
@Override
public CuratorFramework getClient() {
if(client == null) {
creator();
}
//如果不为空直接返回
return client;
}
@Override
public void addPersistentNode(String path, String data) throws Exception {
// 通过链式编程创建节点,功能操作属于底层化所有一定要加try/catch;
try {
client.create()
.creatingParentsIfNeeded() //如果没有创建
.withMode(CreateMode.PERSISTENT) //创建持久化节点
.forPath(path,data.getBytes(Charset.defaultCharset()));//Charset.defaultCharset()设置字符集、默认UTF-8
}catch(KeeperException.NodeExistsException e) {
//先要考虑如果节点存在,那么要捕获KeeperException异常,那么要启动日志
LOGGER.warn("Node already exists:{}",path);
}catch(Exception e) {
//如果创建失败那么要捕获失败异常
throw new Exception("addPersistentNode error",e);
}
}
@Override
public String addEphemeralNode(String path, String data) throws Exception {
return client.create()
.creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.forPath(path,data.getBytes(Charset.defaultCharset()));
}
@Override
public Stat setData(String path, String data) throws Exception {
return client.setData()
.forPath(path, data.getBytes(Charset.defaultCharset()));
}
@Override
public String getData(String path) throws Exception {
return new String(client.getData().forPath(path),Charset.defaultCharset());
}
@Override
public void deletePath(String path) throws Exception {
client.delete().forPath(path);
}
@Override
public boolean isConnected() {
return client.getZookeeperClient().isConnected();
}
@Override
public Stat stat(String path) throws Exception {
//返回非空就存在,返回null值就代表不存在
return client.checkExists().forPath(path);
}
/**
* 描述:当Curator出现watch机制发现变更时,调用NodeListener的nodeChanged方法,进行处理;
* NodeListener在此定义为接口,jar使用者根据具体业务逻辑,去实现NodeListener,然后调用
* nodeChanged()方法,返回所需信息;
* 封装的真正目的不在于细节实现,而是逻辑实现,为调用者提供接口。
*/
@Override
public void listener4ChildrenPath(final String parent, final NodeListener listener) throws Exception {
/**
*1. this client 当前类对象
*2. parent 监听传入节点
*3. (cacheData)true 应用缓存
*4. (dataIsCompressed)false 不压缩
*5. (executorService)EVENT_THREAD_POOL 自定义线程池参数
*/
PathChildrenCache cache = new PathChildrenCache(this.client, parent, true, false, EVENT_THREAD_POOL);
//启动
cache.start(StartMode.POST_INITIALIZED_EVENT);//POST_INITIALIZED_EVENT加载后事件
LOGGER.info("add listener parent path start,paht:{}",parent);
//针对传入节点parent的变更加入监听
cache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curator, PathChildrenCacheEvent event) throws Exception {
//
ChildData cd = event.getData();
if(cd == null) return;
/**
* 如果不为空进行分支判断,符合那种状态就进入那个分支操作;
* 目前所做的是基础组件封装jar,所以根据不同状态的分支具体操作应该由jar包调用者去实现;
* 所以此处的思想在于逻辑架构的设计,而非具体细节实现;
* 使用NodeListener的nodeChanged方法,传入CuratorImpl工具类本身,和ChangedEvent对象,
* 参数:cd.getPath()变更的节点路径,new String(cd.getData())变更的内容,ChangedEvent.Type.{XX}的状态;
*
*/
switch(event.getType()) {
case CHILD_ADDED:
listener.nodeChanged(CuratorImpl.this,
new ChangedEvent(cd.getPath(), new String(cd.getData()), ChangedEvent.Type.CHILD_ADDED));
break;
case CHILD_UPDATED:
listener.nodeChanged(CuratorImpl.this,
new ChangedEvent(cd.getPath(), new String(cd.getData()), ChangedEvent.Type.CHILD_UPDATED));
break;
case CHILD_REMOVED:
listener.nodeChanged(CuratorImpl.this,
new ChangedEvent(cd.getPath(), new String(cd.getData()), ChangedEvent.Type.CHILD_REMOVED));
break;
}
}
},DIRECT_EXECUTOR);
}
@Override
public void close() {
//任何服务的开启和关闭都要进行try/catch捕获
if(null != client) {
try {
client.close();
}catch(Exception e) {
e.printStackTrace();
LOGGER.error("zookeeper client is closed error:{}",e);
}
}
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getSessionTimeout() {
return sessionTimeout;
}
public void setSessionTimeout(int sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
}