/**
* Dianping.com Inc.
* Copyright (c) 2003-2013 All Rights Reserved.
*/
package com.dianping.pigeon.registry;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import com.dianping.pigeon.registry.config.RegistryConfig;
import com.dianping.pigeon.registry.config.ServiceConfig;
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import org.apache.commons.lang.StringUtils;
import com.dianping.pigeon.config.ConfigChangeListener;
import com.dianping.pigeon.config.ConfigManager;
import com.dianping.pigeon.config.ConfigManagerLoader;
import com.dianping.pigeon.domain.HostInfo;
import com.dianping.pigeon.extension.ExtensionLoader;
import com.dianping.pigeon.log.Logger;
import com.dianping.pigeon.log.LoggerLoader;
import com.dianping.pigeon.monitor.Monitor;
import com.dianping.pigeon.monitor.MonitorLoader;
import com.dianping.pigeon.registry.exception.RegistryException;
import com.dianping.pigeon.registry.listener.RegistryEventListener;
import com.dianping.pigeon.registry.listener.ServerInfoListener;
import com.dianping.pigeon.registry.util.Constants;
import com.dianping.pigeon.registry.util.HeartBeatSupport;
import com.dianping.pigeon.registry.util.Utils;
import com.dianping.pigeon.util.VersionUtils;
public class RegistryManager {
private static final Logger logger = LoggerLoader.getLogger(RegistryManager.class);
private static volatile boolean isInit = false;
private static Throwable initializeException = null;
private static final RegistryManager instance = new RegistryManager();
private static volatile Registry registry = null;
private static final String KEY_PIGEON_REGISTRY_CUSTOMIZED = "pigeon.registry.customized.active";
private static final String BLANK_GROUP = "";
private static ConcurrentHashMap<String, Set<HostInfo>> referencedServiceAddresses = new ConcurrentHashMap<String, Set<HostInfo>>();
private static ConcurrentHashMap<String, HostInfo> referencedAddresses = new ConcurrentHashMap<String, HostInfo>();
private static final Interner<String> stringInterner = Interners.newWeakInterner();
private volatile static RegistryConfig registryConfig = new RegistryConfig();
private static final ConfigManager configManager = ConfigManagerLoader.getConfigManager();
private static ConcurrentHashMap<String, Object> registeredServices = new ConcurrentHashMap<String, Object>();
private static final Monitor monitor = MonitorLoader.getMonitor();
public static final boolean fallbackDefaultGroup = configManager.getBooleanValue("pigeon.registry.group.fallback",
true);
private static boolean enableLocalConfig = ConfigManagerLoader.getConfigManager()
.getBooleanValue("pigeon.registry.config.local", false);
private RegistryManager() {
}
public static boolean isInitialized() {
return isInit;
}
public static Throwable getInitializeException() {
return initializeException;
}
public static RegistryManager getInstance() {
if (!isInit) {
synchronized (RegistryManager.class) {
if (!isInit) {
instance.init();
initializeException = null;
RegistryEventListener.addListener(new InnerServerInfoListener());
isInit = true;
}
}
}
return instance;
}
private void init() {
List<Registry> _registryList = ExtensionLoader.getExtensionList(Registry.class);
try {
if (_registryList.size() > 0) {
String customizedRegistryName = configManager.getStringValue(KEY_PIGEON_REGISTRY_CUSTOMIZED,
Constants.REGISTRY_MIX_NAME);
for (Registry registry : _registryList) {
if (registry.getName().equals(customizedRegistryName)) {
registry.init();
RegistryManager.registry = registry;
logger.info(registry.getName() + " registry started.");
}
}
} else {
throw new RegistryException("failed to find registry extension type, please check dependencies!");
}
configManager.registerConfigChangeListener(new InnerConfigChangeListener());
} catch (Throwable t) {
initializeException = t;
throw new RuntimeException(t);
}
}
public Registry getRegistry() {
return registry;
}
public Set<String> getReferencedServices() {
return referencedServiceAddresses.keySet();
}
public Set<String> getRegisteredServices() {
return registeredServices.keySet();
}
public Set<String> getReferencedServices(String serverAddress) {
HostInfo hostInfo = referencedAddresses.get(serverAddress);
if (hostInfo != null) {
return hostInfo.getServices();
}
return new HashSet<>();
}
public HostInfo getHostInfo(String serverAddress) {
return referencedAddresses.get(serverAddress);
}
public boolean isReferencedService(String serviceName, String group) {
return referencedServiceAddresses.containsKey(serviceName);
}
public List<String> getServiceAddressList(String serviceName, String group) throws RegistryException {
String serviceAddress = getServiceAddress(serviceName, group);
return Utils.getAddressList(serviceName, serviceAddress);
}
// invoker
public String getServiceAddress(String remoteAppkey, String serviceName, String group) throws RegistryException {
String serviceKey = getServiceKey(serviceName, group);
if (enableLocalConfig) {
String addr = configManager.getLocalStringValue(Utils.escapeServiceName(serviceKey));
if (addr == null) {
try {
addr = configManager.getLocalStringValue(serviceKey);
} catch (Throwable t) {
}
}
if (!StringUtils.isBlank(addr)) {
if (logger.isDebugEnabled()) {
logger.debug("get service address from local properties, service name:" + serviceName + " address:"
+ addr);
}
return addr;
}
}
if (registry != null) {
String addr = registry.getServiceAddress(remoteAppkey, serviceName, group, fallbackDefaultGroup);
return addr;
}
return "";
}
// invoker
public String getServiceAddress(String serviceName, String group) throws RegistryException {
String serviceKey = getServiceKey(serviceName, group);
if (enableLocalConfig) {
String addr = configManager.getLocalStringValue(Utils.escapeServiceName(serviceKey));
if (addr == null) {
try {
addr = configManager.getLocalStringValue(serviceKey);
} catch (Throwable t) {
}
}
if (!StringUtils.isBlank(addr)) {
if (logger.isDebugEnabled()) {
logger.debug("get service address from local properties, service name:" + serviceName + " address:"
+ addr);
}
return addr;
}
}
if (registry != null) {
String addr = registry.getServiceAddress(serviceName, group, fallbackDefaultGroup);
return addr;
}
return "";
}
private String getServiceKey(String serviceName, String group) {
if (StringUtils.isBlank(group)) {
return serviceName;
} else {
return serviceName + "?" + group;
}
}
// invoker
public int getServiceWeightFromCache(String serverAddress) {
HostInfo hostInfo = referencedAddresses.get(serverAddress);
if (hostInfo != null) {
return hostInfo.getWeight();
}
return Constants.DEFAULT_WEIGHT;
}
// invoker
public int getServiceWeight(String serverAddress, boolean readCache) {
if (readCache) {
HostInfo hostInfo = referencedAddresses.get(serverAddress);
if (hostInfo != null) {
return hostInfo.getWeight();
}
}
int weight = Constants.DEFAULT_WEIGHT;
if (registry != null) {
try {
weight = registry.getServerWeight(serverAddress);
HostInfo hostInfo = referencedAddresses.get(serverAddress);
if (hostInfo != null) {
hostInfo.setWeight(weight);
}
} catch (Throwable t) {
logger.error("failed to get weight for " + serverAddress, t);
}
}
return weight;
}
// invoker
public int getServiceWeight(String serverAddress) {
return getServiceWeight(serverAddress, true);
}
/**
* For invoker to update service weight in local cache. Will not update to
* registry center.
*
* @param serviceAddress
* @param weight
*/
public void setServiceWeight(String serviceAddress, int weight) {
HostInfo hostInfo = referencedAddresses.get(serviceAddress);
if (hostInfo == null) {
if (!serviceAddress.startsWith(configManager.getLocalIp())) {
logger.warn("weight not found for address:" + serviceAddress);
}
return;
}
hostInfo.setWeight(weight);
logger.info("set " + serviceAddress + " weight to " + weight);
}
// provider
public void registerService(String serviceName, String group, String serviceAddress, int weight)
throws RegistryException {
if (registry != null) {
registry.registerService(serviceName, group, serviceAddress, weight);
registeredServices.putIfAbsent(serviceName, serviceAddress);
monitor.logEvent("PigeonService.register", serviceName, "weight=" + weight + "&group=" + group);
}
}
// provider
public void setServerWeight(String serverAddress, int weight) throws RegistryException {
if (registry != null) {
registry.setServerWeight(serverAddress, weight);
monitor.logEvent("PigeonService.weight", weight + "", "");
}
}
// provider
public void unregisterService(String serviceName, String serviceAddress) throws RegistryException {
unregisterService(serviceName, Constants.DEFAULT_GROUP, serviceAddress);
}
// provider
public void unregisterService(String serviceName, String group, String serviceAddress) throws RegistryException {
if (registry != null) {
registry.unregisterService(serviceName, group, serviceAddress);
registeredServices.remove(serviceName);
monitor.logEvent("PigeonService.unregister", serviceName, "group=" + group);
}
}
// invoker
public void addServiceAddress(String serviceName, String host, int port, int weight) {
Utils.validateWeight(host, port, weight);
String serviceAddress = host + ":" + port;
HostInfo hostInfo = referencedAddresses.get(serviceAddress);
if (hostInfo == null) {
synchronized (stringInterner.intern(serviceAddress)) {
hostInfo = referencedAddresses.get(serviceAddress);
if (hostInfo == null) {
hostInfo = new HostInfo(host, port, weight);
referencedAddresses.put(serviceAddress, hostInfo);
if (registry != null) {
try {
String app = registry.getServerApp(serviceAddress);
hostInfo.setApp(app);
} catch (RegistryException e) {
logger.info("failed to update app in cache for: " + serviceAddress);
}
try {
String version = registry.getServerVersion(serviceAddress);
hostInfo.setVersion(version);
} catch (RegistryException e) {
logger.info("failed to update version in cache for: " + serviceAddress);
}
try {
byte heartBeatSupport = registry.getServerHeartBeatSupport(serviceAddress);
hostInfo.setHeartBeatSupport(heartBeatSupport);
} catch (RegistryException e) {
logger.info("failed to update heartBeatSupport in cache for: " + serviceAddress);
}
// invoker读取注册中心的协议信息并且put进去
try {
Map<String, Boolean> serviceProtocols
= registry.getServiceProtocols(serviceAddress);
hostInfo.setServiceProtocols(serviceProtocols);
} catch (RegistryException e) {
logger.info("failed to update service protocols in cache for: " + serviceAddress);
}
}
}
}
}
hostInfo.addService(serviceName);
Set<HostInfo> hostInfos = referencedServiceAddresses.get(serviceName);
if (hostInfos == null) {
hostInfos = Collections.newSetFromMap(new ConcurrentHashMap<HostInfo, Boolean>());
Set<HostInfo> oldHostInfos = referencedServiceAddresses.putIfAbsent(serviceName, hostInfos);
if (oldHostInfos != null) {
hostInfos = oldHostInfos;
}
}
hostInfos.add(hostInfo);
}
public void removeServiceAddress(String serviceName, HostInfo hostInfo) {
Set<HostInfo> hostInfos = referencedServiceAddresses.get(serviceName);
if (hostInfos == null || !hostInfos.contains(hostInfo)) {
logger.info("address:" + hostInfo + " is not in address list of service " + serviceName);
return;
}
hostInfos.remove(hostInfo);
logger.info("removed address:" + hostInfo + " from service:" + serviceName);
HostInfo cachedHostInfo = referencedAddresses.get(hostInfo.getConnect());
if (cachedHostInfo != null) {
cachedHostInfo.removeService(serviceName);
}
// If server is not referencd any more, remove from server list
if (!isAddressReferenced(hostInfo)) {
referencedAddresses.remove(hostInfo.getConnect());
}
}
private boolean isAddressReferenced(HostInfo hostInfo) {
for (String key : referencedServiceAddresses.keySet()) {
Set<HostInfo> hostInfos = referencedServiceAddresses.get(key);
if (hostInfos.contains(hostInfo)) {
logger.info("address:" + hostInfo + " still been referenced for service:" + key);
return true;
}
}
return false;
}
public Set<HostInfo> getReferencedServiceAddresses(String serviceName) {
Set<HostInfo> hostInfos = referencedServiceAddresses.get(serviceName);
if (hostInfos == null || hostInfos.size() == 0) {
logger.info("empty address list for service:" + serviceName);
}
return hostInfos;
}
public Map<String, Set<HostInfo>> getAllReferencedServiceAddresses() {
return referencedServiceAddresses;
}
public String getReferencedAppFromCache(String serverAddress) {
HostInfo hostInfo = referencedAddresses.get(serverAddress);
String app = "";
if (hostInfo != null) {
app = hostInfo.getApp();
}
return app;
}
public Map<String, Boolean> getProtocolInfoFromCache(String serviceAddress) {
HostInfo hostInfo = referencedAddresses.get(serviceAddress);
Map<String, Boolean> protocolInfoMap = new HashMap<String, Boolean>();
if (hostInfo != null) {
protocolInfoMap = hostInfo.getServiceProtocols();
}
if (protocolInfoMap != null) {
return protocolInfoMap;
}
return new HashMap<String, Boolean>();
}
public boolean isSupportNewProtocolFromCache(String serviceAddress, String serviceName) {
HostInfo hostInfo = referencedAddresses.get(serviceAddress);
Map<String, Boolean> protocolInfoMap = new HashMap<String, Boolean>();
if (hostInfo != null) {
protocolInfoMap = hostInfo.getServiceProtocols();
}
if (protocolInfoMap != null && protocolInfoMap.containsKey(serviceName)) {
return protocolInfoMap.get(serviceName);
}
return false;
}
public String getReferencedApp(String serverAddress) {
String app = "";
if (registry != null) {
try {
app = registry.getServerApp(serverAddress);
setReferencedApp(serverAddress, app);
} catch (RegistryException e) {
logger.info("failed to update app in cache for: " + serverAddress);
}
}
return app;
}
public void setReferencedApp(String serverAddress, String app) {
HostInfo hostInfo = referencedAddresses.get(serverAddress);
if (hostInfo != null) {
hostInfo.setApp(app);
}
}
public void setServerApp(String serverAddress, String app) {
if (registry != null) {
registry.setServerApp(serverAddress, app);
}
}
public void unregisterServerApp(String serverAddress) {
if (registry != null) {
registry.unregisterServerApp(serverAddress);
}
}
public String getReferencedVersionFromCache(String serverAddress) {
HostInfo hostInfo = referencedAddresses.get(serverAddress);
String version = null;
if (hostInfo != null) {
version = hostInfo.getVersion();
// if (version == null && registry != null) {
// version = registry.getServerVersion(serverAddress);
// hostInfo.setVersion(version);
// }
return version;
}
return null;
}
public String getReferencedVersion(String serverAddress) {
String version = "";
if (registry != null) {
try {
version = registry.getServerVersion(serverAddress);
setReferencedVersion(serverAddress, version);
} catch (RegistryException e) {
logger.info("failed to update version in cache for: " + serverAddress);
}
}
return version;
}
public void setReferencedVersion(String serverAddress, String version) {
HostInfo hostInfo = referencedAddresses.get(serverAddress);
if (hostInfo != null) {
hostInfo.setVersion(version);
}
}
public boolean getReferencedProtocol(String serverAddress, String serviceName) {
boolean support = false;
try {
Map<String, Boolean> serviceProtocols = registry.getServiceProtocols(serverAddress);
setReferencedProtocols(serverAddress, serviceProtocols);
Boolean _support = serviceProtocols.get(serviceName);
if (_support != null) {
support = _support;
}
} catch (Throwable t) {
logger.info("failed to get protocol for " + serverAddress + "#" + serviceName, t);
}
return support;
}
private void setReferencedProtocols(String serverAddress, Map<String, Boolean> serviceProtocols) {
HostInfo hostInfo = referencedAddresses.get(serverAddress);
if (hostInfo != null) {
hostInfo.setServiceProtocols(serviceProtocols);
}
}
public void setServerVersion(String serverAddress, String version) {
if (registry != null) {
registry.setServerVersion(serverAddress, version);
}
}
public void unregisterServerVersion(String serverAddress) {
if (registry != null) {
registry.unregisterServerVersion(serverAddress);
}
}
public void initRegistryConfig(String ip) throws RegistryException {
if (registry != null) {
registryConfig = registry.getRegistryConfig(ip);
}
}
public String getGroup(String serviceName) {
if (StringUtils.isNotBlank(configManager.getGroup())) { // swimlane is set, do not cache and watch
return configManager.getGroup();
}
String group = null;
ServiceConfig serviceConfig = registryConfig.getServices().get(serviceName);
if (serviceConfig != null) {
group = serviceConfig.getGroup();
}
return StringUtils.isBlank(group) ? BLANK_GROUP : group;
}
public static RegistryConfig getRegistryConfig() {
return registryConfig;
}
public synchronized void registryConfigChanged(String ip, RegistryConfig registryConfig) {
RegistryConfig oldRegistryConfig = RegistryManager.registryConfig;
RegistryManager.registryConfig = registryConfig;
RegistryEventListener.registryConfigChanged(ip, oldRegistryConfig, registryConfig);
}
static class InnerServerInfoListener implements ServerInfoListener {
@Override
public void onServerAppChange(String serverAddress, String app) {
HostInfo hostInfo = referencedAddresses.get(serverAddress);
if (hostInfo != null) {
hostInfo.setApp(app);
}
}
@Override
public void onServerVersionChange(String serverAddress, String version) {
HostInfo hostInfo = referencedAddresses.get(serverAddress);
if (hostInfo != null) {
hostInfo.setVersion(version);
}
}
@Override
public void onServerProtocolChange(String serverAddress, Map<String, Boolean> protocolInfoMap) {
// 更新invoker缓存的服务端协议详情
HostInfo hostInfo = referencedAddresses.get(serverAddress);
if (hostInfo != null) {
hostInfo.setServiceProtocols(protocolInfoMap);
}
}
@Override
public void onServerHeartBeatSupportChange(String serverAddress, byte heartBeatSupport) {
HostInfo hostInfo = referencedAddresses.get(serverAddress);
if (hostInfo != null) {
hostInfo.setHeartBeatSupport(heartBeatSupport);
}
}
}
public void updateHeartBeat(String serviceAddress, Long heartBeatTimeMillis) {
if (registry != null) {
registry.updateHeartBeat(serviceAddress, heartBeatTimeMillis);
}
}
public void deleteHeartBeat(String serviceAddress) {
if (registry != null) {
registry.deleteHeartBeat(serviceAddress);
}
}
// invoker
public byte getServerHeartBeatSupportFromCache(String serverAddress) {
HostInfo hostInfo = referencedAddresses.get(serverAddress);
byte heartBeatSupport;
if (hostInfo != null) {
heartBeatSupport = hostInfo.getHeartBeatSupport();
return heartBeatSupport;
}
return HeartBeatSupport.BothSupport.getValue();
}
// invoker
public byte getServerHeartBeatSupport(String serviceAddress) {
byte heartBeatSupport = HeartBeatSupport.BothSupport.getValue();
if (registry != null) {
try {
heartBeatSupport = registry.getServerHeartBeatSupport(serviceAddress);
setServerHeartBeatSupport(serviceAddress, heartBeatSupport);
} catch (RegistryException e) {
logger.info("failed to update heartBeatSupport in cache for: " + serviceAddress);
}
}
return heartBeatSupport;
}
// invoker
public void setServerHeartBeatSupport(String serverAddress, byte heartBeatSupport) {
HostInfo hostInfo = referencedAddresses.get(serverAddress);
if (hostInfo != null) {
hostInfo.setHeartBeatSupport(heartBeatSupport);
}
}
public boolean isSupportNewProtocol(String serviceAddress) throws RegistryException {
// 1. load from cache
String version = getReferencedVersionFromCache(serviceAddress);
if (version != null) {
return VersionUtils.isThriftSupported(version);
}
// 2. load from registry
boolean support = false;
try {
support = registry.isSupportNewProtocol(serviceAddress);
} catch (Throwable t) {
logger.info("failed to get protocol for " + serviceAddress, t);
}
return support;
}
public boolean isSupportNewProtocol(String serviceAddress, String serviceName) throws RegistryException {
return isSupportNewProtocol(serviceAddress, serviceName, true);
}
public boolean isSupportNewProtocol(String serviceAddress, String serviceName, boolean readCache)
throws RegistryException {
if (readCache) {
HostInfo hostInfo = referencedAddresses.get(serviceAddress);
Map<String, Boolean> serviceProtocols = new HashMap<String, Boolean>();
if (hostInfo != null) {
serviceProtocols = hostInfo.getServiceProtocols();
}
if (serviceProtocols != null && serviceProtocols.containsKey(serviceName)) {
return serviceProtocols.get(serviceName);
}
}
boolean support = false;
try {
Map<String, Boolean> serviceProtocols = registry.getServiceProtocols(serviceAddress);
setReferencedProtocols(serviceAddress, serviceProtocols);
Boolean _support = serviceProtocols.get(serviceName);
if (_support != null) {
support = _support;
}
} catch (Throwable t) {
logger.info("failed to get protocol for " + serviceAddress + "#" + serviceName, t);
}
return support;
}
/**
* For provider to register protocol to registry center.
*
* @param serviceAddress
* @param serviceName
* @param support
* @throws RegistryException
*/
public void registerSupportNewProtocol(String serviceAddress, String serviceName, boolean support)
throws RegistryException {
if (registry != null) {
registry.setSupportNewProtocol(serviceAddress, serviceName, support);
}
monitor.logEvent("PigeonService.protocol", serviceName, "support=" + support);
}
public void unregisterSupportNewProtocol(String serviceAddress, String serviceName, boolean support)
throws RegistryException {
if (registry != null) {
registry.unregisterSupportNewProtocol(serviceAddress, serviceName, support);
}
monitor.logEvent("PigeonService.protocol", serviceName, "unregister");
}
/**
* for governor: manual update service and set weight to 1
*
* @param serviceName
* @param group
* @param hosts
* @author chenchongze
*/
public void setServerService(String serviceName, String group, String hosts) throws RegistryException {
if (registry != null) {
registry.setServerService(serviceName, group, hosts);
}
monitor.logEvent("PigeonGovernor.setHosts", serviceName, "swimlane=" + group + "&hosts=" + hosts);
}
public void setHostsWeight(String serviceName, String group, String hosts, int weight) throws RegistryException {
if (registry != null) {
registry.setHostsWeight(serviceName, group, hosts, weight);
}
monitor.logEvent("PigeonGovernor.setWeight", hosts, weight + "");
}
/**
* for governor: manual delete service
*
* @param serviceName
* @param group
* @throws RegistryException
*/
public void delServerService(String serviceName, String group) throws RegistryException {
if (registry != null) {
registry.delServerService(serviceName, group);
}
monitor.logEvent("PigeonGovernor.delService", serviceName, "swimlane=" + group);
}
/**
* for governor: getServiceHosts from zk
*
* @param serviceName
* @param group
* @return
* @throws RegistryException
*/
public String getServiceHosts(String serviceName, String group) throws RegistryException {
String addr = "";
try {
addr = registry.getServiceAddress(serviceName, group, false, false);
} catch (Throwable t) {
logger.info("failed to get service hosts for " + serviceName + "#" + group + ", msg: " + t.getMessage());
throw new RegistryException(t);
}
return addr;
}
public void setConsoleAddress(String consoleAddress) {
if (registry != null) {
registry.setConsoleAddress(consoleAddress);
}
}
public void unregisterConsoleAddress(String consoleAddress) {
if (registry != null) {
registry.unregisterConsoleAddress(consoleAddress);
}
}
private class InnerConfigChangeListener implements ConfigChangeListener {
@Override
public void onKeyUpdated(String key, String value) {
if (key.endsWith(KEY_PIGEON_REGISTRY_CUSTOMIZED)) {
try {
for (Registry registry : ExtensionLoader.getExtensionList(Registry.class)) {
if (registry.getName().equals(value)) {
registry.init();
RegistryManager.registry = registry;
logger.info("change to registry: " + value);
}
}
} catch (Throwable t) {
logger.error(t);
}
}
}
@Override
public void onKeyAdded(String key, String value) {
}
@Override
public void onKeyRemoved(String key) {
}
}
}