/** *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.watcher; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.api6.zkclient.ZKClient; import com.api6.zkclient.event.ZKEvent; import com.api6.zkclient.event.ZKEventThreadPool; import com.api6.zkclient.exception.ZKNoNodeException; import com.api6.zkclient.listener.ZKChildDataListener; import com.api6.zkclient.listener.ZKListener; import com.api6.zkclient.listener.ZKNodeListener; import com.api6.zkclient.listener.ZKStateListener; /** * 事件处理类,接受监听器,并回调 * @author: zhaojie/[email protected] */ public class ZKWatcherProcess { private static Logger LOG = LoggerFactory.getLogger(ZKWatcherProcess.class); private final ZKEventThreadPool eventThreadPool; private final ZKClient client; public ZKWatcherProcess(ZKClient zkClient) { this.client = zkClient; //创建事件处理线程池 eventThreadPool = new ZKEventThreadPool(zkClient.getEventThreadPoolSize()); } /** * 停止处理 * @return void */ public void stop(){ eventThreadPool.destory(); } public void processStateChanged(WatchedEvent event) { final KeeperState keeperState = event.getState(); LOG.info("ZooKeeper state is changed [" + keeperState + "] ."); //这里需要更新一下,ZooKeeper客户端的状态 client.setCurrentState(keeperState); if (client.getShutdownTrigger()) { return; } //获取所有的事件监听器 Set<ZKStateListener> listeners = client.getStateListenerSet(); //状态改变事件处理 for (final ZKStateListener stateListener : listeners) { eventThreadPool.submit(new ZKEvent("State changed to " + keeperState + " sent to " + stateListener) { @Override public void run() throws Exception { stateListener.handleStateChanged(keeperState); } }); } //如果会话过期,要重新连接服务器 if (event.getState() == KeeperState.Expired) { try { //会话过期,重新连接 client.reconnect(); //会话过期事件处理 for (final ZKStateListener stateListener : listeners) { ZKEvent zkEvent = new ZKEvent("New session event sent to " + stateListener) { @Override public void run() throws Exception { stateListener.handleNewSession(); } }; eventThreadPool.submit(zkEvent); } } catch (final Exception e) { LOG.info("Unable to re-establish connection. Notifying consumer of the following exception: ", e); //会话过期后重连出错,事件处理 for (final ZKStateListener stateListener : listeners) { eventThreadPool.submit(new ZKEvent("Session establishment error[" + e + "] sent to " + stateListener) { @Override public void run() throws Exception { stateListener.handleSessionError(e); } }); } } } } /** * 处理子节点变化事件 * @param event * @return void */ public void processChildChanged(final WatchedEvent event){ final String path = event.getPath(); final Set<ZKListener> listeners = client.getChildListenerMap().get(path); //提交事件监听进行处理 submitChildEvent(listeners,path,event.getType()); } /** * 处理数据改变事件 * @param event * @return void */ public void processNodeChanged(final WatchedEvent event){ final String path = event.getPath(); final EventType eventType = event.getType(); final Set<ZKListener> listeners = client.getNodeListenerMap().get(path); if (listeners == null || listeners.isEmpty()) { return; } //如果listeners中如果有ZKChildDataListener类型的监听器, //证明是此节点是某个节点的子节点,并监听此节点的数据变化 //这里要单独拿出来,用于触发ZKChildDataListener final Set<ZKListener> childDataChangeListners = new CopyOnWriteArraySet<>(); final Set<ZKListener> nodeListners = new CopyOnWriteArraySet<>(); classifyListeners(listeners,nodeListners,childDataChangeListners); //提交事件监听进行处理 submitNodeEvent(nodeListners,childDataChangeListners,path,eventType); //当前节点作为子节点数据变化 if(eventType == EventType.NodeDataChanged){ //提交事件监听进行处理 submitChildDataEvent(childDataChangeListners,path,eventType); } } /** * 触发所有的监听器,用于在会话失效后调用。 * 会话失效后,服务端会取消watch, * 如果在会话失效后与重连这段时间内有数据发生变化,监听器是无法监听到的, * 所以要调用此方法,触发所有监听器,告诉监听器,会话失效,可能存在数据变化(不是一定有变化)。 * @param eventType * @return void * @author: zhaojie/[email protected] */ public void processAllNodeAndChildListeners(final WatchedEvent event){ LOG.debug("processAllNodeAndChildListeners...."); //对选取的监听器进行处理 for (Entry<String, CopyOnWriteArraySet<ZKListener>> entry : client.getNodeListenerMap().entrySet()) { Set<ZKListener> nodeListners = new CopyOnWriteArraySet<ZKListener>(); Set<ZKListener> childDataChangeListners = new CopyOnWriteArraySet<ZKListener>(); Set<ZKListener> listeners = entry.getValue(); classifyListeners(listeners,nodeListners,childDataChangeListners); //提交事件监听进行处理 submitNodeEvent(nodeListners, childDataChangeListners, entry.getKey(), event.getType()); } //获取所有的节点监听器,并进行处理 for (Entry<String, CopyOnWriteArraySet<ZKListener>> entry : client.getChildListenerMap().entrySet()) { //提交事件监听进行处理 submitChildEvent(entry.getValue(),entry.getKey(),event.getType()); } } /** * 对listeners进行分类整理,把{@link ZKNodeListener}类型的放入nodeListeners,把{@link ZKChildDataListener} 类型的放入childDataChangeListeners * @param listeners * @param nodeListeners * @param childDataChangeListeners * @return void */ private void classifyListeners(Set<ZKListener> listeners,Set<ZKListener> nodeListeners,Set<ZKListener> childDataChangeListeners){ for(ZKListener listener : listeners){ if(listener instanceof ZKChildDataListener){ if(!childDataChangeListeners.contains(listener)){ childDataChangeListeners.add(listener); } }else{ if(!nodeListeners.contains(listener)){ nodeListeners.add(listener); } } } } /** * 提交节点改变相关的事件进行处理 * @param listeners * @param childDataChangeListners * @param path * @param eventType * @return void */ private void submitNodeEvent(final Set<ZKListener> listeners,final Set<ZKListener> childDataChangeListners,final String path,final EventType eventType ){ if (listeners != null && !listeners.isEmpty()) { for (final ZKListener listener : listeners) { ZKEvent zkEvent = new ZKEvent("Node of " + path + " changed sent to " + listener) { @Override public void run() throws Exception { //原生的zookeeper 的监听只生效一次,重新注册监听 LOG.debug("Rewatch the path ["+path+"] by exists method"); boolean flag = client.exists(path, true); LOG.debug("Rewatched the path ["+path+"] by exists method"); try { LOG.debug("Rewatch and get changed data [path:"+path+" | EventType:"+eventType+"] by getData method"); Object data = client.getData(path, null); LOG.debug("Rewatched and return data ["+path+" | "+data+" | EventType:"+eventType+"] by getData method"); listener.handle(path, eventType, data); //响应了删除事件,但是在再次注册监听之前节点又被创建了,这样是无法重新监听到节点创建的 //这里主动触发节点创建事件。 if (eventType == EventType.NodeDeleted && flag) { listener.handle(path, EventType.NodeCreated, data); } } catch (ZKNoNodeException e) { //如果是节点不存在了,则只移除,ZKChildDataListener监听器 client.unlistenNodeChanges(path, childDataChangeListners); //如果路径不存在,在调用client.getData(path,null)会抛出异常 listener.handle(path, eventType, null); //如果是创建节点事件,并且在创建事件收到后,监听还没来得及重新注册,刚创建的节点已经被删除了。 //对于这种情况,客户端就无法重新监听到节点的删除事件的,这里做特殊处理 //主动触发删除的监听事件 if (eventType == EventType.NodeCreated && !flag) { listener.handle(path, EventType.NodeDeleted, null); } } } }; eventThreadPool.submit(zkEvent); } } } /** * 提交子节点改变相关的事件进行处理 * @param listeners * @param path * @param eventType * @return void */ private void submitChildEvent(final Set<ZKListener> listeners,final String path,final EventType eventType){ if (listeners != null && !listeners.isEmpty()) { try { for (final ZKListener listener : listeners) { //创建事件,并在独立的事件处理线程池中执行 ZKEvent zkEvent = new ZKEvent("Children of " + path + " changed sent to " + listener) { @Override public void run() throws Exception { //原生的zookeeper 的监听只生效一次,所以要重新注册父节点的监听 LOG.debug("Rewatch the path ["+path+"] by exists method"); client.exists(path); LOG.debug("Rewatched the path ["+path+"] by exists method"); try { LOG.debug("Rewatch and get chilldren [path:"+path+" | EventType:"+eventType+"] by getChildren method"); //获取数据并重新监听子节点变化 List<String> children = client.getChildren(path); LOG.debug("Rewatched and return children [children:"+children+" | EventType:"+eventType+"] by getChildren method"); //子节点数量改变,如果当前路径path设置了ZKChildDataListener。 //则尝试重新注册子节点数据变化的监听 client.listenNewChildPathWithData(path,children); listener.handle(path, eventType, children); } catch (ZKNoNodeException e) { //如果路径不存在,在调用client.getChildren(path)会抛出异常 listener.handle(path, eventType, null); } } }; eventThreadPool.submit(zkEvent); } } catch (Exception e) { LOG.error("Failed to fire child changed event. Unable to getChildren. ", e); } } } /** * 提交子节点数据改变的事件进行处理 * @param listeners * @param path * @param eventType * @return void */ private void submitChildDataEvent(final Set<ZKListener> listeners,final String path,final EventType eventType){ if (listeners != null && !listeners.isEmpty()) { for (final ZKListener listener : listeners) { ZKEvent zkEvent = new ZKEvent("Children of " + path + " changed sent to "+ listener) { @Override public void run() throws Exception { //原生的zookeeper 的监听只生效一次,重新注册监听 LOG.debug("rewatch the path ["+path+"]"); client.exists(path, true); LOG.debug("rewatched the path ["+path+"]"); try { LOG.debug("Try to get child changed data [path:"+path+" | EventType:"+eventType+"]"); //在事件触发后到获取数据之间的时间,节点的值是可能改变的, //所以此处的获取也只是,获取到最新的值,而不一定是事件触发时的值 //重新监听节点变化 Object data = client.getData(path, null); LOG.debug("Child changed data is [path:"+path+" | data:"+data+" | EventType:"+eventType+"]"); listener.handle(path, eventType, data); } catch (ZKNoNodeException e) { //ignore } } }; eventThreadPool.submit(zkEvent); } } } }