/** * Dianping.com Inc. * Copyright (c) 2003-2013 All Rights Reserved. */ package com.dianping.pigeon.remoting.provider.publish; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang.StringUtils; import com.dianping.pigeon.config.ConfigManager; import com.dianping.pigeon.config.ConfigManagerLoader; import com.dianping.pigeon.log.Logger; import com.dianping.pigeon.log.LoggerLoader; import com.dianping.pigeon.registry.RegistryManager; import com.dianping.pigeon.registry.exception.RegistryException; import com.dianping.pigeon.remoting.common.util.Constants; import com.dianping.pigeon.remoting.provider.ProviderBootStrap; import com.dianping.pigeon.remoting.provider.Server; import com.dianping.pigeon.remoting.provider.config.ProviderConfig; import com.dianping.pigeon.remoting.provider.listener.HeartBeatListener; import com.dianping.pigeon.remoting.provider.service.DisposableService; import com.dianping.pigeon.remoting.provider.service.InitializingService; import com.dianping.pigeon.remoting.provider.service.method.ServiceMethodFactory; import com.dianping.pigeon.util.VersionUtils; /** * @author xiangwu * @Sep 30, 2013 */ public final class ServicePublisher { private static Logger logger = LoggerLoader.getLogger(ServicePublisher.class); private static ConcurrentHashMap<String, ProviderConfig<?>> serviceCache = new ConcurrentHashMap<String, ProviderConfig<?>>(); private static ConfigManager configManager = ConfigManagerLoader.getConfigManager(); private static ServiceChangeListener serviceChangeListener = new DefaultServiceChangeListener(); private static boolean DEFAULT_HEARTBEAT_ENABLE = true; private static ConcurrentHashMap<String, Integer> serverWeightCache = new ConcurrentHashMap<String, Integer>(); private static final int UNPUBLISH_WAITTIME = configManager.getIntValue(Constants.KEY_UNPUBLISH_WAITTIME, Constants.DEFAULT_UNPUBLISH_WAITTIME); private static final boolean THROW_EXCEPTION_IF_FORBIDDEN = configManager.getBooleanValue( "pigeon.publish.forbidden.throwexception", false); private static final boolean GROUP_FORBIDDEN = configManager.getBooleanValue("pigeon.publish.forbidden.group", false); private static final String registryBlackList = configManager.getStringValue("pigeon.registry.blacklist", ""); private static final String registryWhiteList = configManager.getStringValue("pigeon.registry.whitelist", ""); private static final boolean canRegisterDefault = configManager.getBooleanValue( "pigeon.registry.canregister.default", true); public static String getServiceUrlWithVersion(String url, String version) { String newUrl = url; if (!StringUtils.isBlank(version)) { newUrl = url + "_" + version; } return newUrl; } public static <T> void addService(ProviderConfig<T> providerConfig) throws Exception { if (logger.isInfoEnabled()) { logger.info("add service:" + providerConfig); } String version = providerConfig.getVersion(); String url = providerConfig.getUrl(); if (StringUtils.isBlank(version)) {// default version serviceCache.put(url, providerConfig); } else { String urlWithVersion = getServiceUrlWithVersion(url, version); if (serviceCache.containsKey(url)) { serviceCache.put(urlWithVersion, providerConfig); ProviderConfig<?> providerConfigDefault = serviceCache.get(url); String defaultVersion = providerConfigDefault.getVersion(); if (!StringUtils.isBlank(defaultVersion)) { if (VersionUtils.compareVersion(defaultVersion, providerConfig.getVersion()) < 0) { // replace existing service with this newer service as // the default provider serviceCache.put(url, providerConfig); } } } else { serviceCache.put(urlWithVersion, providerConfig); // use this service as the default provider serviceCache.put(url, providerConfig); } } T service = providerConfig.getService(); if (service instanceof InitializingService) { ((InitializingService) service).initialize(); } ServiceMethodFactory.init(url); } public static <T> void publishService(ProviderConfig<T> providerConfig) throws RegistryException { publishService(providerConfig, true); } // atom public static <T> void publishService(ProviderConfig<T> providerConfig, boolean forcePublish) throws RegistryException { String url = providerConfig.getUrl(); boolean existingService = false; for (String key : serviceCache.keySet()) { ProviderConfig<?> pc = serviceCache.get(key); if (pc.getUrl().equals(url)) { existingService = true; break; } } if (logger.isInfoEnabled()) { logger.info("try to publish service to registry:" + providerConfig + ", existing service:" + existingService); } if (existingService) { boolean autoPublishEnable = ConfigManagerLoader.getConfigManager().getBooleanValue( Constants.KEY_AUTOPUBLISH_ENABLE, true); if (autoPublishEnable || forcePublish) { List<Server> servers = ProviderBootStrap.getServers(providerConfig); int registerCount = 0; for (Server server : servers) { publishServiceToRegistry(url, server.getRegistryUrl(url), server.getPort(), RegistryManager.getInstance().getGroup(url), providerConfig.isSupported()); registerCount++; } if (registerCount > 0) { boolean isHeartbeatEnable = configManager.getBooleanValue(Constants.KEY_HEARTBEAT_ENABLE, DEFAULT_HEARTBEAT_ENABLE); if (isHeartbeatEnable) { HeartBeatListener.registerHeartBeat(providerConfig); } boolean isNotify = configManager .getBooleanValue(Constants.KEY_NOTIFY_ENABLE, false); if (isNotify && serviceChangeListener != null) { serviceChangeListener.notifyServicePublished(providerConfig); } boolean autoRegisterEnable = ConfigManagerLoader.getConfigManager().getBooleanValue( Constants.KEY_AUTOREGISTER_ENABLE, true); if (autoRegisterEnable) { ServiceOnlineTask.start(); } else { logger.info("auto register is disabled"); } providerConfig.setPublished(true); } } else { logger.info("auto publish is disabled"); } } } public static boolean isAutoPublish() { boolean autoPublishEnable = ConfigManagerLoader.getConfigManager().getBooleanValue( Constants.KEY_AUTOPUBLISH_ENABLE, true); boolean autoRegisterEnable = ConfigManagerLoader.getConfigManager().getBooleanValue( Constants.KEY_AUTOREGISTER_ENABLE, true); return autoPublishEnable && autoRegisterEnable; } public static void publishService(String url) throws RegistryException { if (logger.isInfoEnabled()) { logger.info("publish service:" + url); } ProviderConfig<?> providerConfig = serviceCache.get(url); if (providerConfig != null) { for (String key : serviceCache.keySet()) { ProviderConfig<?> pc = serviceCache.get(key); if (pc.getUrl().equals(url)) { publishService(pc, true); } } } } private synchronized static <T> void publishServiceToRegistry(String url, String registryUrl, int port, String group, boolean support) throws RegistryException { String ip = configManager.getLocalIp(); if (!canRegister(ip)) { boolean canRegister = false; if (StringUtils.isNotBlank(group) && !GROUP_FORBIDDEN) { canRegister = true; } if (!canRegister) { if (THROW_EXCEPTION_IF_FORBIDDEN) { throw new SecurityException("service registration of " + ip + " is not allowed!"); } else { logger.warn("service registration of " + ip + " is not allowed, url:" + registryUrl + ", port:" + port + ", group:" + group); return; } } } String serverAddress = ip + ":" + port; int weight = Constants.WEIGHT_INITIAL; boolean autoRegisterEnable = ConfigManagerLoader.getConfigManager().getBooleanValue( Constants.KEY_AUTOREGISTER_ENABLE, true); if (!autoRegisterEnable) { weight = 0; } /*boolean enableOnlineTask = ConfigManagerLoader.getConfigManager().getBooleanValue("pigeon.online.task.enable", true); if (!enableOnlineTask) { weight = Constants.WEIGHT_DEFAULT; }*/ if (serverWeightCache.containsKey(serverAddress)) { weight = -1; } if (logger.isInfoEnabled()) { logger.info("publish service to registry, url:" + registryUrl + ", port:" + port + ", group:" + group + ", address:" + serverAddress + ", weight:" + weight + ", support: " + support); } RegistryManager.getInstance().registerService(registryUrl, group, serverAddress, weight); RegistryManager.getInstance().registerSupportNewProtocol(serverAddress, registryUrl, support); if (weight >= 0) { if (!serverWeightCache.containsKey(serverAddress)) { RegistryManager.getInstance().setServerApp(serverAddress, configManager.getAppName()); RegistryManager.getInstance().setServerVersion(serverAddress, VersionUtils.VERSION); } serverWeightCache.put(serverAddress, weight); } } public static Map<String, Integer> getServerWeight() { return serverWeightCache; } public synchronized static void setServerWeight(int weight) throws RegistryException { if (weight < 0 || weight > 100) { throw new IllegalArgumentException("The weight must be within the range of 0 to 100:" + weight); } for (String serverAddress : serverWeightCache.keySet()) { if (logger.isInfoEnabled()) { logger.info("set weight, address:" + serverAddress + ", weight:" + weight); } RegistryManager.getInstance().setServerWeight(serverAddress, weight); if (!serverWeightCache.containsKey(serverAddress)) { RegistryManager.getInstance().setServerApp(serverAddress, configManager.getAppName()); RegistryManager.getInstance().setServerVersion(serverAddress, VersionUtils.VERSION); } serverWeightCache.put(serverAddress, weight); } } public synchronized static <T> void unpublishService(ProviderConfig<T> providerConfig) throws RegistryException { String url = providerConfig.getUrl(); boolean existingService = false; for (String key : serviceCache.keySet()) { ProviderConfig<?> pc = serviceCache.get(key); if (pc.getUrl().equals(url)) { existingService = true; break; } } if (logger.isInfoEnabled()) { logger.info("try to unpublish service from registry:" + providerConfig + ", existing service:" + existingService); } if (existingService) { List<Server> servers = ProviderBootStrap.getServers(providerConfig); for (Server server : servers) { String serverAddress = configManager.getLocalIp() + ":" + server.getPort(); String registryUrl = server.getRegistryUrl(providerConfig.getUrl()); RegistryManager.getInstance().unregisterService(registryUrl, RegistryManager.getInstance().getGroup(url), serverAddress); // unregister protocol, include http server RegistryManager.getInstance().unregisterSupportNewProtocol(serverAddress, registryUrl, providerConfig.isSupported()); Integer weight = serverWeightCache.remove(serverAddress); if (weight != null) { RegistryManager.getInstance().unregisterServerApp(serverAddress); RegistryManager.getInstance().unregisterServerVersion(serverAddress); } } boolean isHeartbeatEnable = configManager.getBooleanValue(Constants.KEY_HEARTBEAT_ENABLE, DEFAULT_HEARTBEAT_ENABLE); if (isHeartbeatEnable) { HeartBeatListener.unregisterHeartBeat(providerConfig); } boolean isNotify = configManager.getBooleanValue(Constants.KEY_NOTIFY_ENABLE, false); if (isNotify && serviceChangeListener != null) { serviceChangeListener.notifyServiceUnpublished(providerConfig); } providerConfig.setPublished(false); if (logger.isInfoEnabled()) { logger.info("unpublished service from registry:" + providerConfig); } } } public static void unpublishService(String url) throws RegistryException { if (logger.isInfoEnabled()) { logger.info("unpublish service:" + url); } ProviderConfig<?> providerConfig = serviceCache.get(url); if (providerConfig != null) { for (String key : serviceCache.keySet()) { ProviderConfig<?> pc = serviceCache.get(key); if (pc.getUrl().equals(url)) { unpublishService(pc); } } } } public static ProviderConfig<?> getServiceConfig(String url) { ProviderConfig<?> providerConfig = serviceCache.get(url); return providerConfig; } public static void removeService(String url) throws RegistryException { if (logger.isInfoEnabled()) { logger.info("remove service:" + url); } List<String> toRemovedUrls = new ArrayList<String>(); for (String key : serviceCache.keySet()) { ProviderConfig<?> pc = serviceCache.get(key); if (pc.getUrl().equals(url)) { unpublishService(pc); toRemovedUrls.add(key); Object service = pc.getService(); if (service instanceof DisposableService) { try { ((DisposableService) service).destroy(); } catch (Throwable e) { logger.warn("error while destroy service:" + url + ", caused by " + e.getMessage()); } } } } for (String key : toRemovedUrls) { serviceCache.remove(key); } } public static void removeAllServices() throws RegistryException { if (logger.isInfoEnabled()) { logger.info("remove all services"); } unpublishAllServices(); serviceCache.clear(); } public static void unpublishAllServices() throws RegistryException { if (logger.isInfoEnabled()) { logger.info("unpublish all services"); } ServiceOnlineTask.stop(); setServerWeight(0); try { Thread.sleep(UNPUBLISH_WAITTIME); } catch (InterruptedException e) { } for (String url : serviceCache.keySet()) { ProviderConfig<?> providerConfig = serviceCache.get(url); if (providerConfig != null) { unpublishService(providerConfig); } } } public static void publishAllServices() throws RegistryException { publishAllServices(true); } public static void publishAllServices(boolean forcePublish) throws RegistryException { if (logger.isInfoEnabled()) { logger.info("publish all services, " + forcePublish); } for (String url : serviceCache.keySet()) { ProviderConfig<?> providerConfig = serviceCache.get(url); if (providerConfig != null) { publishService(providerConfig, forcePublish); } } } public static Map<String, ProviderConfig<?>> getAllServiceProviders() { return serviceCache; } public static void notifyServiceOnline() { for (String url : serviceCache.keySet()) { ProviderConfig<?> providerConfig = serviceCache.get(url); if (providerConfig != null) { // do notify if (serviceChangeListener != null) { serviceChangeListener.notifyServiceOnline(providerConfig); } } } } public static void notifyServiceOffline() { for (String url : serviceCache.keySet()) { ProviderConfig<?> providerConfig = serviceCache.get(url); if (providerConfig != null) { // do notify if (serviceChangeListener != null) { serviceChangeListener.notifyServiceOffline(providerConfig); } } } } public static boolean canRegister(String ip) { String[] whiteArray = registryWhiteList.split(","); for (String addr : whiteArray) { if (StringUtils.isBlank(addr)) { continue; } if (ip.startsWith(addr)) { return true; } } String[] blackArray = registryBlackList.split(","); for (String addr : blackArray) { if (StringUtils.isBlank(addr)) { continue; } if (ip.startsWith(addr)) { return false; } } return canRegisterDefault; } public static Class<?> getInterface(String url) { ProviderConfig<?> config = getServiceConfig(url); return config.getServiceInterface(); } }