/**
* Dianping.com Inc.
* Copyright (c) 2003-2013 All Rights Reserved.
*/
package com.dianping.pigeon.remoting.invoker.listener;
import com.dianping.pigeon.config.ConfigManager;
import com.dianping.pigeon.config.ConfigManagerLoader;
import com.dianping.pigeon.log.LoggerLoader;
import com.dianping.pigeon.registry.RegistryManager;
import com.dianping.pigeon.registry.listener.RegistryEventListener;
import com.dianping.pigeon.remoting.invoker.Client;
import com.dianping.pigeon.remoting.invoker.ClientSelector;
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.route.quality.RequestQualityManager;
import com.dianping.pigeon.threadpool.DefaultThreadFactory;
import com.dianping.pigeon.util.CollectionUtils;
import com.dianping.pigeon.util.ThreadPoolUtils;
import com.dianping.pigeon.log.Logger;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class DefaultClusterListener implements ClusterListener {
private static final Logger logger = LoggerLoader.getLogger(DefaultClusterListener.class);
private ConcurrentHashMap<String, List<Client>> serviceClients = new ConcurrentHashMap<String, List<Client>>();
private ConcurrentHashMap<String, Client> allClients = new ConcurrentHashMap<String, Client>();
private ScheduledThreadPoolExecutor closeExecutor = new ScheduledThreadPoolExecutor(3, new DefaultThreadFactory(
"Pigeon-Client-Cache-Close-ThreadPool"));
private ClusterListenerManager clusterListenerManager = ClusterListenerManager.getInstance();
private ConfigManager configManager = ConfigManagerLoader.getConfigManager();
public DefaultClusterListener(ProviderAvailableListener providerAvailableListener) {
providerAvailableListener.setWorkingClients(serviceClients);
}
public void clear() {
serviceClients = new ConcurrentHashMap<String, List<Client>>();
allClients = new ConcurrentHashMap<String, Client>();
}
public ConcurrentHashMap<String, List<Client>> getServiceClients() {
return serviceClients;
}
public List<Client> getClientList(InvokerConfig<?> invokerConfig) {
List<Client> clientList = this.serviceClients.get(invokerConfig.getUrl());
if (CollectionUtils.isEmpty(clientList)) {
throw new ServiceUnavailableException("no available provider for service:" + invokerConfig.getUrl()
+ ", group:" + RegistryManager.getInstance().getGroup(invokerConfig.getUrl()) + ", env:"
+ ConfigManagerLoader.getConfigManager().getEnv());
}
String vip = invokerConfig.getVip();
if (vip != null && vip.startsWith("console:")) {
Client localClient = allClients.get(configManager.getLocalIp() + vip.substring(vip.indexOf(":")));
if (localClient != null) {
return Arrays.asList(localClient);
}
}
return clientList;
}
@Override
public void addConnect(ConnectInfo connectInfo) {
if (logger.isInfoEnabled()) {
logger.info("[cluster-listener] add service provider:" + connectInfo);
}
Client client = this.allClients.get(connectInfo.getConnect());
if (clientExisted(connectInfo)) {
if (client != null) {
for (List<Client> clientList : serviceClients.values()) {
int idx = clientList.indexOf(client);
if (idx >= 0 && clientList.get(idx) != client) {
closeClientInFuture(client);
}
}
} else {
return;
}
}
if (client == null) {
client = ClientSelector.selectClient(connectInfo);
}
if (!this.allClients.containsKey(connectInfo.getConnect())) {
Client oldClient = this.allClients.putIfAbsent(connectInfo.getConnect(), client);
if (oldClient != null) {
client = oldClient;
}
} else {
client = this.allClients.get(connectInfo.getConnect());
}
try {
if (client.isClosed()) {
client.open();
} else {
logger.info("client already connected:" + client);
}
for (Entry<String, Integer> sw : connectInfo.getServiceNames().entrySet()) {
String serviceName = sw.getKey();
RegistryEventListener.serverInfoChanged(serviceName, connectInfo.getConnect());
List<Client> clientList = this.serviceClients.get(serviceName);
if (clientList == null) {
clientList = new CopyOnWriteArrayList<Client>();
List<Client> oldClientList = this.serviceClients.putIfAbsent(serviceName, clientList);
if (oldClientList != null) {
clientList = oldClientList;
}
}
if (!clientList.contains(client)) {
clientList.add(client);
}
}
} catch (Throwable e) {
logger.error("", e);
}
}
private boolean clientExisted(ConnectInfo connectInfo) {
for (String serviceName : connectInfo.getServiceNames().keySet()) {
List<Client> clientList = serviceClients.get(serviceName);
if (clientList != null) {
for (Client client : clientList) {
if (client != null && client.getAddress().equals(connectInfo.getConnect())) {
return true;
}
}
}
}
return false;
}
@Override
public void removeConnect(Client client) {
if (logger.isInfoEnabled()) {
logger.info("[cluster-listener] remove service provider:" + client);
}
for (String serviceName : this.serviceClients.keySet()) {
List<Client> clientList = this.serviceClients.get(serviceName);
if (clientList != null && clientList.contains(client)) {
clientList.remove(client);
}
}
}
@Override
public void doNotUse(String serviceName, String host, int port) {
if (logger.isInfoEnabled()) {
logger.info("[cluster-listener] do not use service provider:" + serviceName + ":" + host + ":" + port);
}
List<Client> cs = serviceClients.get(serviceName);
List<Client> newCS = new CopyOnWriteArrayList<Client>();
if (cs != null && !cs.isEmpty()) {
newCS.addAll(cs);
}
Client clientFound = null;
for (Client client : cs) {
if (client != null && client.getHost() != null && client.getHost().equals(host) && client.getPort() == port) {
newCS.remove(client);
clientFound = client;
}
}
serviceClients.put(serviceName, newCS);
// 一个client可能对应多个serviceName,仅当client不被任何serviceName使用时才关闭
if (clientFound != null) {
if (!isClientInUse(clientFound)) {
allClients.remove(clientFound.getAddress());
RequestQualityManager.INSTANCE.removeClientQualities(clientFound.getAddress());
closeClientInFuture(clientFound);
}
}
}
private boolean isClientInUse(Client clientToFind) {
for (List<Client> clientList : serviceClients.values()) {
if (clientList.contains(clientToFind)) {
return true;
}
}
return false;
}
private void closeClientInFuture(final Client client) {
Runnable command = new Runnable() {
@Override
public void run() {
client.close();
logger.info("close client:" + client.getAddress());
}
};
try {
closeExecutor.schedule(command, 3000, TimeUnit.MILLISECONDS);
} catch (Throwable e) {
logger.error("error schedule task to close client", e);
}
}
public void destroy() throws Exception {
ThreadPoolUtils.shutdown(closeExecutor);
}
public ConcurrentHashMap<String, Client> getAllClients() {
return allClients;
}
}