/** *Copyright 2016 zhaojie * *Licensed under the Apache License, Version 2.0 (the "License"); *you may not use this file except in compliance with the License. *You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * *Unless required by applicable law or agreed to in writing, software *distributed under the License is distributed on an "AS IS" BASIS, *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *See the License for the specific language governing permissions and *limitations under the License. */ package com.api6.zkclient; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.api6.zkclient.connection.ZKConnection; import com.api6.zkclient.connection.ZKConnectionImpl; import com.api6.zkclient.event.ZKEventLock; import com.api6.zkclient.exception.ZKException; import com.api6.zkclient.exception.ZKInterruptedException; import com.api6.zkclient.exception.ZKNoNodeException; import com.api6.zkclient.exception.ZKNodeExistsException; import com.api6.zkclient.listener.ZKChildCountListener; import com.api6.zkclient.listener.ZKChildDataListener; import com.api6.zkclient.listener.ZKListener; import com.api6.zkclient.listener.ZKStateListener; import com.api6.zkclient.serializer.SerializableSerializer; import com.api6.zkclient.serializer.ZKSerializer; import com.api6.zkclient.watcher.ZKWatcher; /** * ZooKeeper客户端 * 断线重连,会话过期处理,永久监听,子节点数据变化监听 * @author: zhaojie/[email protected] */ public class ZKClient { private static Logger LOG = LoggerFactory.getLogger(ZKClient.class); //子节点监听器 private final Map<String, CopyOnWriteArraySet<ZKListener>> childListenerMap = new ConcurrentHashMap<String, CopyOnWriteArraySet<ZKListener>>(); //节点监听器 private final Map<String, CopyOnWriteArraySet<ZKListener>> nodeListenerMap = new ConcurrentHashMap<String, CopyOnWriteArraySet<ZKListener>>(); //状态监听器 private final Set<ZKStateListener> stateListenerSet = new CopyOnWriteArraySet<ZKStateListener>(); //保存EPHEMERAL类型的节点,用于在断开重连,以及会话失效后的自动创建节点 private final Map<String, ZKNode> ephemeralNodeMap = new ConcurrentHashMap<String,ZKNode>(); private final ZKConnection connection;//连接 private final ZKWatcher watcher;//监听器 private final ZKSerializer serializer;//序列化工具 private final int eventThreadPoolSize; //线程池的大小,同时也是并发线程数 private volatile boolean closed;//是否已关闭客户端的标记 private volatile boolean shutdownTrigger;//触发关闭的标记,如果为true证明正在关闭客户端及连接 /** * 创建ZKClient客户端 * ZKClient. * @param servers 服务器地址 */ public ZKClient(String servers) { this(new ZKConnectionImpl(servers)); } /** * 创建ZKClient客户端 * ZKClient. * @param servers * 服务器地址 * @param sessionTimeOut * 会话超时时间,单位毫秒,这里并不是真实值,只是参考计算的值。 * 会话超时时间和服务端的配置有关,一般是2 * tickTime ~ 20 * tickTime之间 */ public ZKClient(String servers, int sessionTimeOut) { this(new ZKConnectionImpl(servers, sessionTimeOut)); } /** * 创建ZKClient客户端 * ZKClient. * @param servers 服务器地址 * @param sessionTimeOut 会话超时时间,单位毫秒,这里并不是真实值,只是参考计算的值。 * 会话超时时间和服务端的配置有关,一般是2 * tickTime ~ 20 * tickTime之间 * @param operationRetryTimeoutInMillis 重新连接超时时间,单位毫秒。 * 在会话过期或连接断开后,重新尝试连接的失效时间 */ public ZKClient(String servers, int sessionTimeOut,int operationRetryTimeoutInMillis) { this(new ZKConnectionImpl(servers, sessionTimeOut,operationRetryTimeoutInMillis),new SerializableSerializer()); } /** * 创建ZKClient客户端 * ZKClient. * @param servers 服务器地址 * @param sessionTimeOut 会话超时时间,单位毫秒,这里并不是真实值,只是参考计算的值。 * 会话超时时间和服务端的配置有关,一般是2 * tickTime ~ 20 * tickTime之间 * @param operationRetryTimeoutInMillis 重新连接超时时间,单位毫秒。 * 在会话过期或连接断开后,重新尝试连接的失效时间 * @param zkSerializer 序列化类 */ public ZKClient(String servers, int sessionTimeOut,int operationRetryTimeoutInMillis,final ZKSerializer zkSerializer) { this(new ZKConnectionImpl(servers, sessionTimeOut,operationRetryTimeoutInMillis),zkSerializer,Integer.MAX_VALUE); } /** * 创建ZKClient客户端 * ZKClient. * @param servers 服务器地址 * @param sessionTimeOut 会话超时时间,单位毫秒,这里并不是真实值,只是参考计算的值。 * 会话超时时间和服务端的配置有关,一般是2 * tickTime ~ 20 * tickTime之间 * @param operationRetryTimeoutInMillis 重新连接超时时间,单位毫秒。 * 在会话过期或连接断开后,重新尝试连接的失效时间 * @param zkSerializer 序列化类 * @param connectionTimeout 连接超时时间,单位毫秒 * 启动时连接到服务器的超时时间 */ public ZKClient(String servers, int sessionTimeOut,int operationRetryTimeoutInMillis,final ZKSerializer zkSerializer,final int connectionTimeout) { this(new ZKConnectionImpl(servers, sessionTimeOut,operationRetryTimeoutInMillis),zkSerializer,connectionTimeout,1); } /** * 创建ZKClient客户端 * ZKClient. * @param servers 服务器地址 * @param sessionTimeOut 会话超时时间,单位毫秒,这里并不是真实值,只是参考计算的值。 * 会话超时时间和服务端的配置有关,一般是2 * tickTime ~ 20 * tickTime之间 * @param operationRetryTimeoutInMillis 重新连接超时时间,单位毫秒。 * 在会话过期或连接断开后,重新尝试连接的失效时间 * @param zkSerializer 序列化类 * @param connectionTimeout 连接超时时间,单位毫秒 * 启动时连接到服务器的超时时间 * @param eventThreadPoolSize 事件处理线程池的并发线程数,默认值:1。 * 1.如果想要串行执行监听器并且保证严格的顺序,请将设置为 1。 * 2.此值不易太大,推荐1~3即可。一般情况下1个线程就足够,因为一般ZooKeeper监听的响应并不会有很大的并发 */ public ZKClient(String servers, int sessionTimeOut,int operationRetryTimeoutInMillis, final ZKSerializer zkSerializer,final int connectionTimeout,final int eventThreadPoolSize) { this(new ZKConnectionImpl(servers, sessionTimeOut,operationRetryTimeoutInMillis),zkSerializer,connectionTimeout,eventThreadPoolSize); } /** * 创建ZKClient客户端 * ZKClient. * @param connection ZKConnection的连接对象 */ public ZKClient(final ZKConnection connection) { this(connection,new SerializableSerializer()); } /** * 创建ZKClient客户端 * ZKClient. * @param connection ZKConnection的连接对象 * @param zkSerializer 序列化工具类 */ public ZKClient(final ZKConnection connection,final ZKSerializer zkSerializer) { this(connection,zkSerializer,Integer.MAX_VALUE); } public ZKClient(final ZKConnection connection,final ZKSerializer zkSerializer,final int connectionTimeout) { this(connection,zkSerializer,connectionTimeout,1); } /** * 创建ZKClient客户端 * ZKClient. * @param connection ZKConnection的连接对象 * @param zkSerializer 序列化工具类 * @param connectionTimeout 连接超时时间,单位毫秒 * 启动时连接到服务器的超时时间 */ public ZKClient(final ZKConnection connection,final ZKSerializer zkSerializer,final int connectionTimeout, final int eventThreadPoolSize){ this.connection = connection; this.serializer = zkSerializer; this.eventThreadPoolSize = eventThreadPoolSize; this.watcher = new ZKWatcher(this); start(connectionTimeout,watcher); } /** * 连接服务端并注册监听watcher * @param connectionTimeout 连接超时时间 * @param watcher 事件监听器 * @throws ZKInterruptedException * @throws ZKException * @throws IllegalStateException * @return void * @author: zhaojie/[email protected] */ private void start(final long connectionTimeout, Watcher watcher) throws ZKInterruptedException, ZKException, IllegalStateException { boolean started = false; //获取可中断锁 acquireEventLockInterruptibly(); LOG.info("The ZKClient is starting...."); try { shutdownTrigger = false; connection.connect(watcher); boolean waitSuccessful = connection.waitUntilConnected(connectionTimeout, TimeUnit.MILLISECONDS); if (!waitSuccessful) { throw new ZKException("Unable to start ZKClient within timeout [" + connectionTimeout+"]."); } closed = false; started = true; LOG.info("The ZkClient is started."); } finally { releaseEventLock(); //如果没有连接成功,则需要关闭连接,防止连接继续等待连接成功 if (!started) { close(); } } } /** * 重新连接服务器并注册监听 * @return void * @author: zhaojie/[email protected] * @version: 2016年5月24日 上午9:12:02 */ public void reconnect() { LOG.info("reconnecting......"); connection.reconnect(watcher); closed = false; //重新设置监听器 relisten(); //重新创建,需要创建的临时类型的节点 recreatEphemeraleNode(); LOG.info("reconnecting...done"); } /** * 重新设置监听 * @return void * @author: zhaojie/[email protected] * @version: 2016年5月26日 上午12:18:36 */ public void relisten(){ LOG.info(" Relisten......."); for (Entry<String, CopyOnWriteArraySet<ZKListener>> entry : nodeListenerMap.entrySet()) { watchForData(entry.getKey()); LOG.debug("Relisten node:{}", entry.getKey()); } for (Entry<String, CopyOnWriteArraySet<ZKListener>> entry : childListenerMap.entrySet()) { watchForChilds(entry.getKey()); LOG.debug("Relisten child:{}", entry.getKey()); } } /** * 关闭客户端,同时会取消与ZooKeeper的连接,清空并取消所有的监听 * @throws ZKInterruptedException * @return void */ public void close() throws ZKInterruptedException { if (closed) { return; } LOG.debug("Closing ZKClient ..."); acquireEventLock(); try { shutdownTrigger = true; connection.close(); closed = true; //停止监听 unlistenAll(); watcher.stop(); } catch (InterruptedException e) { throw new ZKInterruptedException(e); } finally { releaseEventLock(); } LOG.debug("Closed the ZKClient ."); } /** * 监听子节点的变化,只是监听数量的变化。 * @param path 父路径 * @param listener 监听器 * @return * @return List<String> 返回当前子节点路径集合 */ public List<String> listenChildCountChanges(String path, ZKChildCountListener listener) { LOG.debug("Listen child count changes ["+path+"--"+listener+"] "); synchronized (childListenerMap) { CopyOnWriteArraySet<ZKListener> listeners = childListenerMap.get(path); if (listeners == null) { listeners = new CopyOnWriteArraySet<ZKListener>(); childListenerMap.put(path, listeners); } listeners.add(listener); } return watchForChilds(path); } /** * 监听子节点变化,包括数量的变化,和子节点内容的变化。 * 这里的内容的变化指的版本的变化,例如修改的值与原来的值一样,也会被视为数据已改变。 * @param path * @param listener * @return * @return List<String> */ public List<String> listenChildDataChanges(String path, ZKChildDataListener listener) { LOG.debug("Listen child count and data changes ["+path+"--"+listener+"] "); synchronized (childListenerMap) { CopyOnWriteArraySet<ZKListener> listeners = childListenerMap.get(path); if (listeners == null) { listeners = new CopyOnWriteArraySet<ZKListener>(); childListenerMap.put(path, listeners); } listeners.add(listener); } List<String> children = watchForChilds(path); //监听子节点数据 listenNewChildPathWithData(path,children); return children; } /** * 为路径下的新增的子节点添加数据变化的监听 * @param path 路径 * @param children 子节点集合 * @return void */ public void listenNewChildPathWithData(String path,List<String> children){ if(children==null || children.size()==0){ return; } CopyOnWriteArraySet<ZKListener> chidldDataListeners = new CopyOnWriteArraySet<ZKListener>(); synchronized (childListenerMap) { Set<ZKListener> listeners = childListenerMap.get(path); if(listeners == null || listeners.size() == 0 ){ return; } for(ZKListener listener : listeners){ if(listener instanceof ZKChildDataListener){ if(!chidldDataListeners.contains(listener)){ chidldDataListeners.add(listener); } } } } List<String> newWatchPaths = new ArrayList<String>(); synchronized (nodeListenerMap) { for(String childPath : children){ childPath = path+"/"+childPath; CopyOnWriteArraySet<ZKListener> nodeListeners = nodeListenerMap.get(childPath); boolean isNewPath = true; if(nodeListeners!=null && nodeListeners.size()>0){ isNewPath = false; } if(nodeListeners == null){ nodeListeners = new CopyOnWriteArraySet<ZKListener>(); nodeListenerMap.put(childPath, nodeListeners); } for(ZKListener listener : chidldDataListeners){ if(!nodeListeners.contains(listener)){ nodeListeners.add(listener); } } if(isNewPath){ if(nodeListeners!=null && nodeListeners.size()>0){ newWatchPaths.add(childPath); } } } } for(String newPath : newWatchPaths){ LOG.debug("listen new child path ["+newPath+"]"); watchForData(newPath); } } /** * 解除对子节点的监听 * @param path * @param childListener * @param withData * @return void */ public void unlistenChildChanges(String path, ZKListener childListener) { LOG.debug("Unlisten child ["+path+"--"+childListener+"] "); //解除对子节点的监听 synchronized (childListenerMap) { final Set<ZKListener> listeners = childListenerMap.get(path); if (listeners != null) { listeners.remove(childListener); LOG.debug("unlistenChildChanges:Unlistener path["+path+"--"+childListener+"] "); } if(listeners == null || listeners.isEmpty()){ childListenerMap.remove(path); } } //如果监听了子节点的数据变化,则要同时解除对子节点的数据变化的监听。 if(childListener instanceof ZKChildDataListener){ synchronized (nodeListenerMap) { Set<String> dataPaths = nodeListenerMap.keySet(); //遍历所有的节点监听器,查找是path节点的子节点,并移除监听器 for(String dataPath : dataPaths){ //查找path的下一级节点的监听器,并删除注册的ZKChildDataListener监听器 int index = dataPath.lastIndexOf("/"); if(index>0){ String parentPath = dataPath.substring(0, index); if(path.equals(parentPath)){//如果dataPath是下一级节点 CopyOnWriteArraySet<ZKListener> nodeListeners = nodeListenerMap.get(dataPath); if(nodeListeners != null) { for(ZKListener nodeListener : nodeListeners){ if(nodeListener instanceof ZKChildDataListener){ nodeListeners.remove(nodeListener); LOG.debug(":Unlistener child data changes ["+dataPath+"--"+nodeListener+"] "); } } } if(nodeListeners == null || nodeListeners.isEmpty()){ nodeListenerMap.remove(dataPath); } } } } } } } /** * 对节点进行监听,包括节点的创建、删除、数据改变 * @param path 监听的路径 * @param listener 监听器 * @return void */ public void listenNodeChanges(String path, ZKListener nodeListener) { LOG.debug("listen Node ["+path+"--"+nodeListener+"] "); CopyOnWriteArraySet<ZKListener> listeners; synchronized (nodeListenerMap) { listeners = nodeListenerMap.get(path); if (listeners == null) { listeners = new CopyOnWriteArraySet<ZKListener>(); nodeListenerMap.put(path, listeners); } listeners.add(nodeListener); } watchForData(path); LOG.debug("listened path: " + path); } /** * 解除对节点的监听 * @param path 监听路径 * @param nodeListener 监听器 * @return void */ public void unlistenNodeChanges(String path, ZKListener nodeListener) { LOG.debug("Unlisten Node ["+path+"--"+nodeListener+"] "); synchronized (nodeListenerMap) { final Set<ZKListener> listeners = nodeListenerMap.get(path); if (listeners != null) { listeners.remove(nodeListener); } if (listeners == null || listeners.isEmpty()) { nodeListenerMap.remove(path); LOG.debug("unlistened path: " + path); } } } /** * 解除对节点的监听 * @param path 路径 * @param dataListeners 监听器集合 * @return void */ public void unlistenNodeChanges(String path, Set<ZKListener> dataListeners) { LOG.debug("Unlisten Node ["+path+"--"+dataListeners+"] "); synchronized (nodeListenerMap) { final Set<ZKListener> listeners = nodeListenerMap.get(path); if (listeners != null) { listeners.removeAll(dataListeners); } if (listeners == null || listeners.isEmpty()) { nodeListenerMap.remove(path); } } } /** * 监听ZooKeeper客户端状态 * @param listener * @return void */ public void listenStateChanges(final ZKStateListener listener) { LOG.debug("listen state changes. ["+listener+"]"); synchronized (stateListenerSet) { stateListenerSet.add(listener); } } /** * 解除监听ZooKeeper的状态 * @param stateListener * @return void * @author: zhaojie/[email protected] * @version: 2016年5月26日 下午3:23:11 */ public void unlistenStateChanges(ZKStateListener stateListener) { synchronized (stateListenerSet) { stateListenerSet.remove(stateListener); } } /** * 清除所有的监听器 * @return void */ public void unlistenAll() { synchronized (childListenerMap) { childListenerMap.clear(); } synchronized (nodeListenerMap) { nodeListenerMap.clear(); } synchronized (stateListenerSet) { stateListenerSet.clear(); } } /** * 判断路径是否设置了监听 * @param path * @return * @return boolean */ private boolean hasListeners(String path) { Set<ZKListener> dataListeners = nodeListenerMap.get(path); if (dataListeners != null && dataListeners.size() > 0) { return true; } Set<ZKListener> childListeners = childListenerMap.get(path); if (childListeners != null && childListeners.size() > 0) { return true; } return false; } /** * 等待直到数据被创建 * @param path * @param timeUnit * @param time * @return * @throws ZKInterruptedException * @return boolean * @author: zhaojie/[email protected] * @version: 2016年5月24日 上午12:08:36 */ public boolean waitUntilExists(String path, TimeUnit timeUnit, long time) throws ZKInterruptedException { Date timeout = new Date(System.currentTimeMillis() + timeUnit.toMillis(time)); LOG.debug("Waiting until node '" + path + "' becomes available."); if (exists(path)) { return true; } //可中断锁 acquireEventLockInterruptibly(); try { //如果节点暂时不存在,则监听该节点。 //同时当前线程处于等待状态,如果节点被创建,则会触发监听事件,同时线程被唤醒 while (!exists(path, true)) { boolean gotSignal = getEventLock().getNodeEventCondition().awaitUntil(timeout); if (!gotSignal) { return false; } } LOG.debug("Node '" + path + "' is available."); return true; } catch (InterruptedException e) { throw new ZKInterruptedException(e); } finally { releaseEventLock(); } } /** * 监听数据变化 * @param path * @return void * @author: zhaojie/[email protected] * @version: 2016年5月23日 下午11:44:54 */ private void watchForData(final String path) { LOG.debug("watch data change for path["+path+"]"); retryUntilConnected(new Callable<Object>() { @Override public Object call() throws Exception { getZooKeeper().exists(path, true); return null; } }); } /** * 监听子节点变化 * * @param path 父节点路径 * @return 如果路径存在则返回路径下的子节点路径列表,否则返回null. */ private List<String> watchForChilds(final String path) { return retryUntilConnected(new Callable<List<String>>() { @Override public List<String> call() throws Exception { //监听父节点 exists(path, true); try { //返回并监听子节点 return getChildren(path, true); } catch (ZKNoNodeException e) { // 忽略,父节点没有被创建报错,因为设置了监听父节点,此处忽略 } return null; } }); } /** * 判断节点是否存在 * @param path * @param watch * true:将监听该节点的状态,包括创建、删除、修改节点数据,状态改变则会触发监听事件。 * false:不启用监听 * @return boolean 存在返回true,不存在返回false * @author: zhaojie/[email protected] * @version: 2016年5月23日 下午11:50:27 */ public boolean exists(final String path, final boolean watch) { return retryUntilConnected(new Callable<Boolean>() { @Override public Boolean call() throws Exception { return getZooKeeper().exists(path, watch) != null; } }); } /** * 获取子节点 * @param path 父节点路径 * @param watch * true:监听该节点子节点状态,包括删除此节点、创建子节点,删除子节点,状态改变则会触发监听事件。 * false:不启用监听 * @return List<String> 子节点路径列表 * @author: zhaojie/[email protected] * @version: 2016年5月23日 下午11:52:55 */ public List<String> getChildren(final String path, final boolean watch) { return retryUntilConnected(new Callable<List<String>>() { @Override public List<String> call() throws Exception { return getZooKeeper().getChildren(path, watch); } }); } /** * 判断节点是否存在,如果当前client中设置了该节点的监听器,则启用对该节点的监听 * @param path * @return * @return boolean * @author: zhaojie/[email protected] * @version: 2016年5月24日 上午12:11:14 */ public boolean exists(final String path) { return exists(path, hasListeners(path)); } /** * 获得子节点,如果当前client中设置了该节点的子节点的监听器,则启用对该节点的子节点进行监听 * @param path * @return * @return List<String> * @author: zhaojie/[email protected] * @version: 2016年5月24日 上午12:12:03 */ public List<String> getChildren(String path) { return getChildren(path, hasListeners(path)); } /** * 递归创建节点,如果节点的上层节点不存在,则自动创建 * 上层节点都被创建为PERSISTENT类型的节点 * @param path * @param data * @param createMode * @throws ZKInterruptedException * @throws ZKException * @throws RuntimeException * @return void */ public void createRecursive(String path, Object data, CreateMode createMode) throws ZKInterruptedException, ZKException, RuntimeException { createRecursive(path,data, ZooDefs.Ids.OPEN_ACL_UNSAFE,createMode); } /** * 递归创建节点,如果节点的上层节点不存在,则自动创建 * 上层节点都被创建为PERSISTENT类型的节点 * @param path * @param data * @param acl * @param createMode * @throws ZKInterruptedException * @throws ZKException * @throws RuntimeException * @return void */ public void createRecursive(String path , Object data, List<ACL> acl,CreateMode createMode) throws ZKInterruptedException, ZKException, RuntimeException { try { create(path, data, acl, createMode); System.out.println(path+" is created with data " + data +" mode "+ createMode); } catch (ZKNodeExistsException e) { System.out.println(path+" not exists"); //ignore } catch (ZKNoNodeException e) { String parentDir = path.substring(0, path.lastIndexOf('/')); createRecursive(parentDir, null, acl,createMode.PERSISTENT); create(path, data, acl,createMode); System.out.println(path+" is created...with data " + data +" mode "+ createMode); } } /** * 创建节点 * @param path 节点路径 * @param data 节点数据 * @param createMode 创建的类型 * @return * @throws ZKInterruptedException * @throws ZKException * @throws RuntimeException * @return String * @author: zhaojie/[email protected] */ public String create(final String path, Object data, final CreateMode createMode) throws ZKInterruptedException, ZKException, RuntimeException { return create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, createMode); } /** * 创建节点并设置ACL(访问权限控制) * * @param path 路径 * @param data 数据 * @param acl 访问控制配置 * @param createMode 节点类型 * @return 节点路径 * @throws ZKInterruptedException * @throws ZKException * @throws RuntimeException */ public String create(final String path, Object data, final List<ACL> acl, final CreateMode createMode) { if (path == null) { throw new NullPointerException("Missing value for path"); } if (acl == null || acl.size() == 0) { throw new NullPointerException("Missing value for ACL"); } final byte[] bytes = data == null ? null : serializer.serialize(data); LOG.debug("create path ["+path+"]"); String retPath = retryUntilConnected(new Callable<String>() { @Override public String call() throws Exception { return getZooKeeper().create(path, bytes, acl, createMode); } }); LOG.debug("created path ["+path+"]"); return retPath; } /** * 创建EPHEMERAL类型节点,该节点在被服务器删除后,重新连接会被自动创建。 * 而使用{@link ZKClient#create(String, Object, CreateMode)}创建的EPHEMERAL类型节点, * 会话失效后临时节点会被删除,并且在重新连接后并不会重新创建。 * @param path 路径 * @param data 数据 * @param sequential * true:会创建{@link CreateMode#EPHEMERAL_SEQUENTIAL}类型的节点. * false:会创建{@link CreateMode#EPHEMERAL}类型的节点. * @return String * 创建的节点路径 */ public String createEphemerale(final String path, Object data,Boolean sequential){ CreateMode createMode = CreateMode.EPHEMERAL; if(sequential){ createMode = createMode.EPHEMERAL_SEQUENTIAL; } String retPath = create(path, data, createMode); //将节点放入ephemeralNodeMap,用于重连后的自动创建 ephemeralNodeMap.put(path, new ZKNode(path,data,createMode)); return retPath; } /** * 创建EPHEMERAL类型节点,该节点在被服务器删除后,重新连接会被自动创建。 * 而使用{@link ZKClient#create(String, Object, CreateMode)}创建的EPHEMERAL类型节点, * 会在会话失效后消失,并且在重新连接后并不会重新创建。 * @param path 路径 * @param data 数据 * @param acl 访问控制配置 * @param sequential * true:会创建{@link CreateMode#EPHEMERAL_SEQUENTIAL}类型的节点. * false:会创建{@link CreateMode#EPHEMERAL}类型的节点. * @return String * 创建的节点路径 */ public String createEphemerale(final String path, Object data, final List<ACL> acl,Boolean sequential){ CreateMode createMode = CreateMode.EPHEMERAL; if(sequential){ createMode = createMode.EPHEMERAL_SEQUENTIAL; } String retPath = create(path, data, acl, createMode); //将节点放入ephemeralNodeMap,用于重连后的自动创建 ephemeralNodeMap.put(path, new ZKNode(path,data,createMode)); return retPath; } /** * 重连后重新创建临时节点 * @return void */ private void recreatEphemeraleNode(){ for (Entry<String, ZKNode> entry : ephemeralNodeMap.entrySet()) { ZKNode zkNode = entry.getValue(); LOG.debug("recreate ephemerale node " + entry.getKey()); try { create(entry.getKey(), zkNode.getData(), zkNode.getCreateMode()); } catch (ZKNodeExistsException e) { //ignore } } } /** * 根据路径获取数据 * @param path 节点路径 * @return * @return T */ @SuppressWarnings("unchecked") public <T extends Object> T getData(String path) { return (T) getData(path, false); } /** * 根据路径获取数据,如果returnNullIfPathNotExists为true, * 则在路径不存在的情况下返回null,而不是抛出异常 * 如果此路径设置了监听器,则会监听此节点 * @param path 路径 * @param returnNullIfPathNotExists * 值为true,在路径不存在的时候返回null,值为false,路径不存在的时候抛出异常 * @return * @return T */ @SuppressWarnings("unchecked") public <T extends Object> T getData(String path, boolean returnNullIfPathNotExists) { T data = null; try { data = (T) getData(path, null); } catch (ZKNoNodeException e) { if (!returnNullIfPathNotExists) { throw e; } } return data; } /** * 根据路径返回数据及节点的stat信息。 * 如果此路径设置了监听器,则会监听此节点 * @param path 节点路径 * @param stat 返回stat信息 * @return * @return T */ @SuppressWarnings("unchecked") public <T extends Object> T getData(String path, Stat stat) { return (T) getData(path,hasListeners(path), stat); } /** * 根据路径返回节点信息 * @param path 节点路径 * @param watch 是否监听 * @param stat stat信息 * @return * @return T */ @SuppressWarnings("unchecked") private <T extends Object> T getData(final String path,final boolean watch,final Stat stat) { byte[] data = retryUntilConnected(new Callable<byte[]>() { @Override public byte[] call() throws Exception { return getZooKeeper().getData(path, watch, stat); } }); if (data == null) { return null; } return (T) serializer.deserialize(data); } /** * 设置节点的值 * @param path 节点路径 * @param object 需要设置的值 * @return Stat 返回stat信息 */ public Stat setData(String path, Object object) { return setData(path, object, -1); } /** * 设置节点的值 * @param path 节点路径 * @param object 需要设置的值 * @param version 期望版本号 * 如果version为-1,则是对最新版本的更新; * 如果version大于等于0,如果更新的时候发现真实的版本号不等于期望的版本号,会更新失败。 * @return Stat Stat 返回stat信息 */ public Stat setData(final String path, Object datat, final int version) { final byte[] data = serializer.serialize(datat); LOG.debug("set data path["+path+"]"); return (Stat) retryUntilConnected(new Callable<Object>() { @Override public Object call() throws Exception { Stat stat = getZooKeeper().setData(path, data, version); LOG.debug("set data path["+path+"] done"); return stat; } }); } /** * 删除节点所有版本的信息,只能删除子节点 * @param path 要删除的节点的路径 * @return * @return boolean */ public boolean delete(final String path) { return delete(path, -1); } /** * 删除节点的指定版本的信息,只能删除子节点 * @param path 节点路径 * @param version 版本号 * @return boolean 成功返回true,否则返回false * @author: zhaojie/[email protected] */ public boolean delete(final String path, final int version) { try { LOG.debug("delete path ["+path+"].."); retryUntilConnected(new Callable<Object>() { @Override public Object call() throws Exception { getZooKeeper().delete(path, version); LOG.debug("deleted path ["+path+"]"); return null; } }); return true; } catch (ZKNoNodeException e) { return false; } } /** * 删除节点,并递归删除所有的子节点 * @param path 节点路径 * @return * @return boolean * @author: zhaojie/[email protected] */ public boolean deleteRecursive(String path) { List<String> children; try { children = getChildren(path, false); } catch (ZKNoNodeException e) { return true; } for (String subPath : children) { if (!deleteRecursive(path + "/" + subPath)) { return false; } } return delete(path); } /** * 设置访问权限 * @param path * @param acl * @throws ZKException * @return void * @author: zhaojie/[email protected] */ public void setAcl(final String path, final List<ACL> acl) throws ZKException { if (path == null) { throw new NullPointerException("Missing value for path"); } if (acl == null || acl.size() == 0) { throw new NullPointerException("Missing value for ACL"); } if (!exists(path)) { throw new RuntimeException("trying to set acls on non existing node " + path); } retryUntilConnected(new Callable<Void>() { @Override public Void call() throws Exception { Stat stat = new Stat(); getZooKeeper().getData(path,false,stat); getZooKeeper().setACL(path,acl,stat.getAversion()); return null; } }); } /** * 获取访问权限 * @param path * @return * @throws ZKException * @return Map.Entry<List<ACL>,Stat> * @author: zhaojie/[email protected] * @version: 2016年5月27日 上午8:17:52 */ public Map.Entry<List<ACL>, Stat> getAcl(final String path) throws ZKException { if (path == null) { throw new NullPointerException("Missing value for path"); } if (!exists(path)) { throw new RuntimeException("trying to get acls on non existing node " + path); } return retryUntilConnected(new Callable<Map.Entry<List<ACL>, Stat>>() { @Override public Map.Entry<List<ACL>, Stat> call() throws Exception { Stat stat = new Stat(); List<ACL> acl = getZooKeeper().getACL(path, stat); return new SimpleEntry(acl, stat); } }); } /** * 重试直到重新连接位置,参考{@link ZKConnectionImpl#retryUntilConnected(Callable)}} * @param callable 连接成功后回调函数 * @return * @throws ZKInterruptedException * @throws ZKException * @throws RuntimeException * @return T */ private <T> T retryUntilConnected(Callable<T> callable) throws ZKInterruptedException, ZKException, RuntimeException { if(closed){ throw new ZKException("ZKClient is closed!"); } return connection.retryUntilConnected(callable); } /** * 添加认证信息,用于访问被ACL保护的节点 * @param scheme * @param auth * @return void */ public void addAuthInfo(final String scheme, final byte[] auth) { connection.addAuthInfo(scheme, auth); } /** * 获得锁 * @return void * @author: zhaojie/[email protected] */ public void acquireEventLock(){ connection.acquireEventLock(); } /** * 释放锁 * @return void * @author: zhaojie/[email protected] */ public void releaseEventLock(){ connection.releaseEventLock(); } /** * 尝试获得可中断锁 * @return void * @author: zhaojie/[email protected] */ public void acquireEventLockInterruptibly() { connection.acquireEventLockInterruptibly(); } public boolean getShutdownTrigger() { return shutdownTrigger; } public KeeperState getCurrentState() { return connection.getCurrentState(); } public void setCurrentState(KeeperState currentState) { connection.setCurrentState(currentState); } public Map<String, CopyOnWriteArraySet<ZKListener>> getChildListenerMap() { return childListenerMap; } public Map<String, CopyOnWriteArraySet<ZKListener>> getNodeListenerMap() { return nodeListenerMap; } public Set<ZKStateListener> getStateListenerSet() { return stateListenerSet; } public ZKConnection getConnection() { return connection; } public ZKEventLock getEventLock() { return connection.getEventLock(); } public ZooKeeper getZooKeeper(){ return this.connection.getZooKeeper(); } public int getEventThreadPoolSize() { return eventThreadPoolSize; } }