/** * Dianping.com Inc. * Copyright (c) 2003-2013 All Rights Reserved. */ package com.dianping.pigeon.remoting.invoker; import com.dianping.pigeon.config.ConfigManager; import com.dianping.pigeon.config.ConfigManagerLoader; import com.dianping.pigeon.domain.HostInfo; 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 com.dianping.pigeon.remoting.ServiceFactory; import com.dianping.pigeon.remoting.common.domain.Disposable; import com.dianping.pigeon.remoting.common.domain.InvocationRequest; import com.dianping.pigeon.remoting.common.util.Utils; import com.dianping.pigeon.remoting.invoker.config.InvokerConfig; import com.dianping.pigeon.remoting.invoker.domain.ConnectInfo; import com.dianping.pigeon.remoting.invoker.exception.ServiceUnavailableException; import com.dianping.pigeon.remoting.invoker.listener.ClusterListenerManager; import com.dianping.pigeon.remoting.invoker.listener.DefaultClusterListener; import com.dianping.pigeon.remoting.invoker.listener.ProviderAvailableListener; import com.dianping.pigeon.remoting.invoker.route.DefaultRouteManager; import com.dianping.pigeon.remoting.invoker.route.RouteManager; import com.dianping.pigeon.remoting.provider.config.ProviderConfig; import com.dianping.pigeon.remoting.provider.publish.ServicePublisher; import com.dianping.pigeon.threadpool.DefaultThreadFactory; import com.dianping.pigeon.threadpool.DefaultThreadPool; import com.dianping.pigeon.threadpool.ThreadPool; import com.dianping.pigeon.util.ThreadPoolUtils; import org.apache.commons.lang.StringUtils; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy; public class ClientManager { private static final Logger logger = LoggerLoader.getLogger(ClientManager.class); private ClusterListenerManager clusterListenerManager = ClusterListenerManager.getInstance(); private DefaultClusterListener clusterListener; private ProviderAvailableListener providerAvailableListener; private RouteManager routerManager = DefaultRouteManager.INSTANCE; private ConfigManager configManager = ConfigManagerLoader.getConfigManager(); private ServiceProviderChangeListener providerChangeListener = new InnerServiceProviderChangeListener(); private static ExecutorService providerAvailableThreadPool = Executors.newFixedThreadPool(1, new DefaultThreadFactory("Pigeon-Client-ProviderAvailable-ThreadPool")); private static int registerPoolCoreSize = ConfigManagerLoader.getConfigManager() .getIntValue("pigeon.invoker.registerpool.coresize", 10); private static int registerPoolMaxSize = ConfigManagerLoader.getConfigManager() .getIntValue("pigeon.invoker.registerpool.maxsize", 30); private static int registerPoolQueueSize = ConfigManagerLoader.getConfigManager() .getIntValue("pigeon.invoker.registerpool.queuesize", 50); private static ThreadPool registerThreadPool = new DefaultThreadPool("Pigeon-Client-Register-Pool", registerPoolCoreSize, registerPoolMaxSize, new LinkedBlockingQueue<Runnable>(registerPoolQueueSize), new CallerRunsPolicy()); private static ClientManager instance = new ClientManager(); private RegistryConnectionListener registryConnectionListener = new InnerRegistryConnectionListener(); private GroupChangeListener groupChangeListener = new InnerGroupChangeListener(); private static boolean enableVip = ConfigManagerLoader.getConfigManager() .getBooleanValue("pigeon.invoker.vip.enable", false); private static volatile boolean enableRegisterConcurrently = ConfigManagerLoader.getConfigManager() .getBooleanValue("pigeon.invoker.registerconcurrently.enable", true); public static ClientManager getInstance() { return instance; } private ClientManager() { this.providerAvailableListener = new ProviderAvailableListener(); this.clusterListener = new DefaultClusterListener(providerAvailableListener); this.clusterListenerManager.addListener(this.clusterListener); providerAvailableThreadPool.execute(this.providerAvailableListener); RegistryEventListener.addListener(providerChangeListener); RegistryEventListener.addListener(registryConnectionListener); RegistryEventListener.addListener(groupChangeListener); registerThreadPool.getExecutor().allowCoreThreadTimeOut(true); } public void registerClient(String serviceName, String host, int port, int weight) { ConnectInfo connectInfo = new ConnectInfo(serviceName, host, port, weight); this.clusterListenerManager.addConnect(connectInfo); RegistryManager.getInstance().addServiceAddress(serviceName, host, port, weight); } public Client getClient(InvokerConfig<?> invokerConfig, InvocationRequest request, List<Client> excludeClients) { List<Client> clientList = clusterListener.getClientList(invokerConfig); List<Client> clientsToRoute = new ArrayList<Client>(clientList); if (excludeClients != null) { clientsToRoute.removeAll(excludeClients); } return routerManager.route(clientsToRoute, invokerConfig, request); } public List<Client> getAvailableClients(InvokerConfig<?> invokerConfig, InvocationRequest request) { List<Client> clientList = clusterListener.getClientList(invokerConfig); return routerManager.getAvailableClients(clientList, invokerConfig, request); } public void destroy() throws Exception { if (clusterListenerManager instanceof Disposable) { ((Disposable) clusterListenerManager).destroy(); } if (routerManager instanceof Disposable) { ((Disposable) routerManager).destroy(); } RegistryEventListener.removeListener(providerChangeListener); ThreadPoolUtils.shutdown(providerAvailableThreadPool); // ThreadPoolUtils.shutdown(heartBeatThreadPool); // ThreadPoolUtils.shutdown(reconnectThreadPool); this.clusterListener.destroy(); } public String getServiceAddress(InvokerConfig invokerConfig) { String remoteAppkey = invokerConfig.getRemoteAppKey(); String serviceName = invokerConfig.getUrl(); String group = RegistryManager.getInstance().getGroup(serviceName); String vip = invokerConfig.getVip(); String serviceAddress = null; boolean useVip = false; if (StringUtils.isNotBlank(vip)) { if (enableVip) { useVip = true; } if (vip.startsWith("console:")) { useVip = true; vip = vip.replaceAll("console", configManager.getLocalIp()); } } try { if (useVip) { serviceAddress = vip; } else if (StringUtils.isNotBlank(remoteAppkey)) { serviceAddress = RegistryManager.getInstance().getServiceAddress(remoteAppkey, serviceName, group); } else { serviceAddress = RegistryManager.getInstance().getServiceAddress(serviceName, group); } } catch (Throwable e) { logger.error("cannot get service provider for service:" + serviceName, e); throw new ServiceUnavailableException("cannot get service provider for service:" + serviceName + ", remote app:" + remoteAppkey + ", env:" + configManager.getEnv(), e); } if (StringUtils.isBlank(serviceAddress)) { throw new ServiceUnavailableException("empty service address from registry for service:" + serviceName + ", group:" + group + ", remote app:" + remoteAppkey + ", env:" + configManager.getEnv()); } if (logger.isInfoEnabled()) { logger.info("selected service provider address is:" + serviceAddress + " with service:" + serviceName + ",group:" + group + ", remote app:" + remoteAppkey); } serviceAddress = serviceAddress.trim(); return serviceAddress; } public Set<HostInfo> registerClients(InvokerConfig invokerConfig) { String remoteAppkey = invokerConfig.getRemoteAppKey(); String serviceName = invokerConfig.getUrl(); String group = RegistryManager.getInstance().getGroup(serviceName); String vip = invokerConfig.getVip(); logger.info("start to register clients for service '" + serviceName + "#" + group + "'"); String localHost = null; if (vip != null && vip.startsWith("console:")) { localHost = configManager.getLocalIp() + vip.substring(vip.indexOf(":")); } String serviceAddress = getServiceAddress(invokerConfig); String[] addressArray = serviceAddress.split(","); Set<HostInfo> addresses = Collections.newSetFromMap(new ConcurrentHashMap<HostInfo, Boolean>()); for (int i = 0; i < addressArray.length; i++) { if (StringUtils.isNotBlank(addressArray[i])) { // addressList.add(addressArray[i]); String address = addressArray[i]; int idx = address.lastIndexOf(":"); if (idx != -1) { String host = null; int port = -1; try { host = address.substring(0, idx); port = Integer.parseInt(address.substring(idx + 1)); } catch (RuntimeException e) { logger.warn("invalid address:" + address + " for service:" + serviceName); } if (host != null && port > 0) { if (localHost != null && !localHost.equals(host + ":" + port)) { continue; } try { int weight = RegistryManager.getInstance().getServiceWeight(address, false); addresses.add(new HostInfo(host, port, weight)); } catch (Throwable e) { logger.error("error while registering service invoker:" + serviceName + ", address:" + address + ", env:" + configManager.getEnv(), e); throw new ServiceUnavailableException("error while registering service invoker:" + serviceName + ", address:" + address + ", env:" + configManager.getEnv(), e); } } } else { logger.warn("invalid address:" + address + " for service:" + serviceName); } } } final String url = serviceName; long start = System.nanoTime(); if (enableRegisterConcurrently) { final CountDownLatch latch = new CountDownLatch(addresses.size()); for (final HostInfo hostInfo : addresses) { Runnable r = new Runnable() { @Override public void run() { try { RegistryEventListener.providerAdded(url, hostInfo.getHost(), hostInfo.getPort(), hostInfo.getWeight()); RegistryEventListener.serverInfoChanged(url, hostInfo.getConnect()); } catch (Throwable t) { logger.error("failed to add provider client:" + hostInfo, t); } finally { latch.countDown(); } } }; registerThreadPool.submit(r); } try { latch.await(); } catch (InterruptedException e) { logger.info("", e); } } else { for (final HostInfo hostInfo : addresses) { RegistryEventListener.providerAdded(url, hostInfo.getHost(), hostInfo.getPort(), hostInfo.getWeight()); RegistryEventListener.serverInfoChanged(url, hostInfo.getConnect()); } } long end = System.nanoTime(); logger.info("end to register clients for service '" + serviceName + "#" + group + "', cost:" + ((end - start) / 1000000)); return addresses; } public Map<String, Set<HostInfo>> getServiceHosts() { return RegistryManager.getInstance().getAllReferencedServiceAddresses(); } /** * @return the clusterListener */ public DefaultClusterListener getClusterListener() { return clusterListener; } class InnerServiceProviderChangeListener implements ServiceProviderChangeListener { @Override public void providerAdded(ServiceProviderChangeEvent event) { if (logger.isInfoEnabled()) { logger.info("add " + event.getHost() + ":" + event.getPort() + ":" + event.getWeight() + " to " + event.getServiceName()); } registerClient(event.getServiceName(), event.getHost(), event.getPort(), event.getWeight()); } @Override public void providerRemoved(ServiceProviderChangeEvent event) { HostInfo hostInfo = new HostInfo(event.getHost(), event.getPort(), event.getWeight()); RegistryManager.getInstance().removeServiceAddress(event.getServiceName(), hostInfo); } @Override public void hostWeightChanged(ServiceProviderChangeEvent event) { } } class InnerRegistryConnectionListener implements RegistryConnectionListener { @Override public void reconnected() { Set<InvokerConfig<?>> services = ServiceFactory.getAllServiceInvokers().keySet(); Map<String, Set<HostInfo>> serviceAddresses = RegistryManager.getInstance() .getAllReferencedServiceAddresses(); logger.info("begin to sync service addresses:" + services.size()); for (InvokerConfig<?> invokerConfig : services) { String url = invokerConfig.getUrl(); try { Set<HostInfo> addresses = registerClients(invokerConfig); // remove unreferenced service address Set<HostInfo> currentAddresses = serviceAddresses.get(url); if (currentAddresses != null && addresses != null) { logger.info( url + " 's addresses, new:" + addresses.size() + ", old:" + currentAddresses.size()); currentAddresses.retainAll(addresses); } } catch (Throwable t) { logger.warn( "error while trying to sync service addresses:" + url + ", caused by:" + t.getMessage()); } } logger.info("succeed to sync service addresses"); } } private class InnerGroupChangeListener implements GroupChangeListener { @Override public void onGroupChange(String ip, RegistryConfig oldRegistryConfig, RegistryConfig newRegistryConfig) { // reconnect to new ip:port list for (InvokerConfig<?> invokerConfig : ServiceFactory.getAllServiceInvokers().keySet()) { try { logger.info("invoker group changed, service: " + invokerConfig.getUrl() + ", new group: " + RegistryManager.getInstance().getGroup(invokerConfig.getUrl())); String hosts = ""; try { hosts = getServiceAddress(invokerConfig); } catch (ServiceUnavailableException e) { logger.info("hosts is empty!", e); } List<String[]> hostDetail = Utils.getServiceIpPortList(hosts); DefaultServiceChangeListener.INSTANCE.onServiceHostChange(invokerConfig.getUrl(), hostDetail); } catch (Throwable e) { logger.warn("failed to change refresh invoker to new group, caused by: " + e.getMessage()); } } } } public void clear() { clusterListener.clear(); } public static ThreadPool getRegisterThreadPool() { return registerThreadPool; } }