package com.dianping.pigeon.registry.zookeeper;
import com.dianping.pigeon.log.Logger;
import com.dianping.pigeon.log.LoggerLoader;
import com.dianping.pigeon.registry.RegistryManager;
import com.dianping.pigeon.registry.config.RegistryConfig;
import com.dianping.pigeon.registry.exception.RegistryException;
import com.dianping.pigeon.registry.listener.*;
import com.dianping.pigeon.registry.util.Constants;
import org.apache.commons.lang.StringUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.api.CuratorEvent;
import org.apache.curator.framework.api.CuratorListener;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher.Event.EventType;
import java.util.List;
import java.util.Map;
public class CuratorEventListener implements CuratorListener {
private static Logger logger = LoggerLoader.getLogger(CuratorEventListener.class);
private static final int ADDRESS = 1;
private static final int WEIGHT = 2;
private static final int APP = 3;
private static final int VERSION = 4;
private static final int PROTOCOL = 5;
private static final int HOST_CONFIG= 6;
private CuratorClient client;
private final RegistryNotifyListener registryNotifyListener = RegistryNotifyListenerLoader.getRegistryNotifyListener();
public CuratorEventListener(CuratorClient client) {
this.client = client;
}
@Override
public void eventReceived(CuratorFramework client, CuratorEvent curatorEvent) throws Exception {
WatchedEvent event = (curatorEvent == null ? null : curatorEvent.getWatchedEvent());
if (event == null
|| (event.getType() != EventType.NodeCreated && event.getType() != EventType.NodeDataChanged
&& event.getType() != EventType.NodeDeleted && event.getType() != EventType.NodeChildrenChanged)) {
return;
}
if (logger.isInfoEnabled())
logEvent(event);
try {
PathInfo pathInfo = parsePath(event.getPath());
if (pathInfo == null) {
logger.warn("Failed to parse path " + event.getPath());
return;
}
if (pathInfo.type == ADDRESS) {
addressChanged(pathInfo);
} else if (pathInfo.type == WEIGHT) {
weightChanged(pathInfo);
} else if (pathInfo.type == APP) {
appChanged(pathInfo);
} else if (pathInfo.type == VERSION) {
versionChanged(pathInfo);
} else if (pathInfo.type == PROTOCOL) {
protocolChanged(pathInfo);
} else if (pathInfo.type == HOST_CONFIG) {
registryConfigChanged(pathInfo);
}
} catch (Throwable e) {
logger.error("Error in ZookeeperWatcher.process()", e);
return;
}
}
private void logEvent(WatchedEvent event) {
StringBuilder sb = new StringBuilder();
sb.append("zookeeper event received, type: ").append(event.getType()).append(", path: ")
.append(event.getPath());
logger.info(sb);
}
/*
* 1. Get newest value from ZK and watch again 2. Determine if changed
* against cache 3. notify if changed 4. pay attention to group fallback
* notification
*/
private void addressChanged(PathInfo pathInfo) throws Exception {
if (shouldNotify(pathInfo)) {
String hosts = client.get(pathInfo.path);
logger.info("Service address changed, path " + pathInfo.path + " value " + hosts);
List<String[]> hostDetail = Utils.getServiceIpPortList(hosts);
registryNotifyListener.onServiceHostChange(pathInfo.serviceName, hostDetail, Constants.REGISTRY_CURATOR_NAME);
}
// Watch again
client.watch(pathInfo.path);
}
private boolean shouldNotify(PathInfo pathInfo) throws Exception {
String serviceName = pathInfo.serviceName;
String currentGroup = RegistryManager.getInstance().getGroup(serviceName);
currentGroup = Utils.normalizeGroup(currentGroup);
if (currentGroup.equals(pathInfo.group))
return true;
if (StringUtils.isEmpty(currentGroup) && !StringUtils.isEmpty(pathInfo.group)) // 当前无group配置, 通知来自group
return false;
if (!StringUtils.isEmpty(currentGroup) && StringUtils.isEmpty(pathInfo.group)
&& RegistryManager.fallbackDefaultGroup) { // group中 && 默认group改变的通知 && 可fallback
String servicePath = Utils.getServicePath(pathInfo.serviceName, currentGroup);
if (!client.exists(servicePath)) { // group地址为空或被删除,fallback
return true;
}
String addr = client.get(servicePath); // group地址不为空,取出group的value
if (!Utils.isValidAddress(addr)) { // group地址的value有效
return true;
}
}
return false;
}
private void weightChanged(PathInfo pathInfo) throws RegistryException {
try {
String newValue = client.get(pathInfo.path);
logger.info("service weight changed, path " + pathInfo.path + " value " + newValue);
int weight = newValue == null ? 0 : Integer.parseInt(newValue);
registryNotifyListener.onHostWeightChange(pathInfo.server, weight, Constants.REGISTRY_CURATOR_NAME);
client.watch(pathInfo.path);
} catch (Exception e) {
throw new RegistryException(e);
}
}
private void appChanged(PathInfo pathInfo) throws RegistryException {
try {
String app = client.get(pathInfo.path);
logger.info("app changed, path " + pathInfo.path + " value " + app);
registryNotifyListener.serverAppChanged(pathInfo.server, app, Constants.REGISTRY_CURATOR_NAME);
client.watch(pathInfo.path);
} catch (Exception e) {
throw new RegistryException(e);
}
}
private void versionChanged(PathInfo pathInfo) throws RegistryException {
try {
String version = client.get(pathInfo.path);
logger.info("version changed, path " + pathInfo.path + " value " + version);
registryNotifyListener.serverVersionChanged(pathInfo.server, version, Constants.REGISTRY_CURATOR_NAME);
client.watch(pathInfo.path);
} catch (Exception e) {
throw new RegistryException(e);
}
}
private void protocolChanged(PathInfo pathInfo) throws Exception {
try {
String info = client.get(pathInfo.path);
Map<String, Boolean> infoMap = Utils.getProtocolInfoMap(info);
logger.info("protocol changed, path " + pathInfo.path + " value " + info);
registryNotifyListener.serverProtocolChanged(pathInfo.server, infoMap, Constants.REGISTRY_CURATOR_NAME);
} catch (Throwable e) {
throw new RegistryException(e);
} finally {
client.watch(pathInfo.path);
}
}
private void registryConfigChanged(PathInfo pathInfo) throws Exception {
try {
String info = client.get(pathInfo.path);
RegistryConfig registryConfig = Utils.getRegistryConfig(info);
logger.info("registry config changed, path " + pathInfo.path + " value " + info);
RegistryManager.getInstance().registryConfigChanged(pathInfo.server, registryConfig);
} catch (Throwable e) {
throw new RegistryException(e);
} finally {
client.watch(pathInfo.path);
}
}
public PathInfo parsePath(String path) {
if (path == null)
return null;
PathInfo pathInfo = null;
if (path.startsWith(Constants.SERVICE_PATH)) {
pathInfo = new PathInfo(path);
pathInfo.type = ADDRESS;
pathInfo.serviceName = path.substring(Constants.SERVICE_PATH.length() + 1);
int idx = pathInfo.serviceName.indexOf(Constants.PATH_SEPARATOR);
if (idx != -1) {
pathInfo.group = pathInfo.serviceName.substring(idx + 1);
pathInfo.serviceName = pathInfo.serviceName.substring(0, idx);
}
pathInfo.serviceName = Utils.unescapeServiceName(pathInfo.serviceName);
pathInfo.group = Utils.normalizeGroup(pathInfo.group);
} else if (path.startsWith(Constants.WEIGHT_PATH)) {
pathInfo = new PathInfo(path);
pathInfo.type = WEIGHT;
pathInfo.server = path.substring(Constants.WEIGHT_PATH.length() + 1);
} else if (path.startsWith(Constants.APP_PATH)) {
pathInfo = new PathInfo(path);
pathInfo.type = APP;
pathInfo.server = path.substring(Constants.APP_PATH.length() + 1);
} else if (path.startsWith(Constants.VERSION_PATH)) {
pathInfo = new PathInfo(path);
pathInfo.type = VERSION;
pathInfo.server = path.substring(Constants.VERSION_PATH.length() + 1);
} else if (path.startsWith(Constants.PROTOCOL_PATH)) {
pathInfo = new PathInfo(path);
pathInfo.type = PROTOCOL;
pathInfo.server = path.substring(Constants.PROTOCOL_PATH.length() + 1);
} else if (path.startsWith(Constants.HOST_CONFIG_PATH)) {
pathInfo = new PathInfo(path);
pathInfo.type = HOST_CONFIG;
// use ip as server(without port)
pathInfo.server = path.substring(Constants.HOST_CONFIG_PATH.length() + 1);
}
return pathInfo;
}
class PathInfo {
String path;
String serviceName;
String group;
String server;
int type;
PathInfo(String path) {
this.path = path;
}
}
}