package com.justdebugit.thrift.registry;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.NodeCache;
import org.apache.curator.framework.recipes.cache.NodeCacheListener;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent.Type;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException.NoNodeException;
import org.apache.zookeeper.KeeperException.NodeExistsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.justdebugit.thrift.registry.NodeEvent.EventType;
/**
* 注册、监听、解除注册、解除监听
*
* @author justdebugit@gmail.com
*
*/
public class ZkCuratorRegistry implements Registry {
private static final Logger LOGGER = LoggerFactory
.getLogger(ZkCuratorRegistry.class);
private final CuratorFactory curatorFactory;
private final CuratorFramework client;
private final NodeCacheManager nodeCacheManager;
private static final Set<Type> ignoreTypes = ImmutableSet.of(
Type.CONNECTION_SUSPENDED, Type.CONNECTION_RECONNECTED,
Type.CONNECTION_LOST, Type.INITIALIZED);
public ZkCuratorRegistry(CuratorFactory curatorFactory) {
Preconditions.checkNotNull(curatorFactory);
this.curatorFactory = curatorFactory;
this.client = curatorFactory.getCuratorClient();
nodeCacheManager = new NodeCacheManager();
}
@Override
public void register(String path, byte[] bytes) {
try {
client.create().creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL).forPath(path, bytes);
} catch (NodeExistsException e) {
LOGGER.warn(e.getMessage(), e);
} catch (Exception e) {
throw new IllegalStateException(" can not create path " + path
+ " " + e.getMessage(), e);
}
}
@Override
public void register(String path) {
try {
client.create().creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL).forPath(path);
} catch (NodeExistsException e) {
LOGGER.warn("node already Exists " + e.getMessage(), e);
} catch (Exception e) {
throw new IllegalStateException(" can not create path " + path
+ " " + e.getMessage(), e);
}
}
@Override
public void unregister(String path) {
try {
client.delete().guaranteed().forPath(path);
} catch (NoNodeException e) {
LOGGER.warn("no node exists " + e.getMessage(), e);
} catch (Exception e) {
throw new IllegalStateException(" can not delete path " + path
+ " " + e.getMessage(), e);
}
}
@Override
public void subscribe(String path, ChangeListener listener) {
nodeCacheManager.addListener(path, listener);
}
@Override
public void unsubscribe(String path, ChangeListener listener) {
nodeCacheManager.removeListener(path, listener);
}
@Override
public byte[] getData(String path) {
NodeCache nodeCache = nodeCacheManager.nodeCacheMap.get(path);
ChildData childData = null;
if (nodeCache != null
&& (childData = nodeCache.getCurrentData()) != null) {
return childData.getData();
}
return null;
}
@Override
public Map<String, byte[]> getChildren(String path) {
PathChildrenCache pathChildrenCache = nodeCacheManager.childNodeCacheMap
.get(path);
List<ChildData> children = null;
if (pathChildrenCache != null
&& (children = pathChildrenCache.getCurrentData()) != null) {
Map<String, byte[]> retMap = Maps.newHashMap();
for (ChildData childData : children) {
retMap.put(childData.getPath(), childData.getData());
}
return retMap;
}
return null;
}
/**
* 监听节点事件每个path存放一个nodeCache和childrenNodeCache,
* 通过nodeCache和childrenNodeCache不用关心重连、session失效等问题
*
*/
private class NodeCacheManager {
final ConcurrentMap<String, NodeCache> nodeCacheMap = new ConcurrentHashMap<String, NodeCache>();
final ConcurrentMap<String, PathChildrenCache> childNodeCacheMap = Maps
.newConcurrentMap();
final ConcurrentMap<ChangeListener, PathChildrenCacheListener> change2ChildListenerMap = Maps
.newConcurrentMap();
final ConcurrentMap<ChangeListener, NodeCacheListener> change2NodeListenerMap = Maps
.newConcurrentMap();
void addListener(String path, final ChangeListener listener) {
addToNodeCache(path, listener);
addToChildNodeCache(path, listener);
}
void removeListener(String path, final ChangeListener listener) {
NodeCache nodeCache = nodeCacheMap.get(path);
if (nodeCache != null) {
nodeCache.getListenable().removeListener(
change2NodeListenerMap.get(path));
}
PathChildrenCache pathChildrenCache = childNodeCacheMap.get(path);
if (pathChildrenCache != null) {
pathChildrenCache.getListenable().removeListener(
change2ChildListenerMap.get(path));
}
}
private void addToChildNodeCache(String path,
final ChangeListener listener) {
PathChildrenCache childrenCache = childNodeCacheMap.get(path);
if (childrenCache == null) {
childNodeCacheMap.putIfAbsent(path,
curatorFactory.getPathChildrenCache(client, path));
childrenCache = childNodeCacheMap.get(path);
}
PathChildrenCacheListener pathChildrenCacheListener = new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework client,
PathChildrenCacheEvent event) throws Exception {
if (ignoreTypes.contains(event.getType())) {
return;
}
ImmutablePair<String, byte[]> pair = ImmutablePair
.<String, byte[]> of(event.getData().getPath(),
event.getData().getData());
switch (event.getType()) {
case CHILD_ADDED:
listener.onChange(new NodeEvent(EventType.CHILD_ADDED,
pair));
break;
case CHILD_UPDATED:
listener.onChange(new NodeEvent(
EventType.CHILD_UPDATED, pair));
break;
case CHILD_REMOVED:
listener.onChange(new NodeEvent(
EventType.CHILD_REMOVED, pair));
break;
default:
break;
}
}
};
change2ChildListenerMap.putIfAbsent(listener,
pathChildrenCacheListener);
childrenCache.getListenable().addListener(
change2ChildListenerMap.get(listener));
}
private void addToNodeCache(String path, final ChangeListener listener) {
NodeCache nodeCache = nodeCacheMap.get(path);
if (nodeCache == null) {
nodeCacheMap.putIfAbsent(path,
curatorFactory.getNodeCache(client, path));
nodeCache = nodeCacheMap.get(path);
}
final NodeCache tmpCache = nodeCache;
NodeCacheListener nodeCacheListener = new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
ChildData childData = tmpCache.getCurrentData();
if (childData!=null) {
ImmutablePair<String, byte[]> pair = ImmutablePair
.<String, byte[]> of(childData.getPath(),
childData.getData());
listener.onChange(new NodeEvent(EventType.NODE_CHANAGED,
pair));
}else {
listener.onChange(new NodeEvent(EventType.NODE_CHANAGED,
null));
}
}
};
change2NodeListenerMap.putIfAbsent(listener, nodeCacheListener);
nodeCache.getListenable().addListener(
change2NodeListenerMap.get(listener));
}
}
}