package com.dianping.pigeon.remoting.invoker.cluster;
import java.util.ArrayList;
import java.util.List;
import com.dianping.pigeon.log.Logger;
import com.dianping.pigeon.log.LoggerLoader;
import com.dianping.pigeon.remoting.common.domain.InvocationRequest;
import com.dianping.pigeon.remoting.common.domain.InvocationResponse;
import com.dianping.pigeon.remoting.common.process.ServiceInvocationHandler;
import com.dianping.pigeon.remoting.common.util.Constants;
import com.dianping.pigeon.remoting.invoker.Client;
import com.dianping.pigeon.remoting.invoker.ClientManager;
import com.dianping.pigeon.remoting.invoker.config.InvokerConfig;
import com.dianping.pigeon.remoting.invoker.domain.InvokerContext;
import com.dianping.pigeon.remoting.invoker.exception.RemoteInvocationException;
import com.dianping.pigeon.remoting.invoker.exception.RequestTimeoutException;
import com.dianping.pigeon.remoting.invoker.exception.ServiceUnavailableException;
import com.dianping.pigeon.remoting.invoker.util.InvokerUtils;
public class FailoverCluster implements Cluster {
private ClientManager clientManager = ClientManager.getInstance();
private static final Logger logger = LoggerLoader.getLogger(FailoverCluster.class);
@Override
public InvocationResponse invoke(final ServiceInvocationHandler handler, final InvokerContext invocationContext)
throws Throwable {
InvokerConfig<?> invokerConfig = invocationContext.getInvokerConfig();
List<Client> selectedClients = new ArrayList<Client>();
Throwable lastError = null;
int retry = invokerConfig.getRetries(invocationContext.getMethodName());
int maxInvokeTimes = retry + 1;
boolean timeoutRetry = invokerConfig.isTimeoutRetry();
int invokeTimes = 0;
for (int index = 0; index < maxInvokeTimes; index++) {
InvocationRequest request = InvokerUtils.createRemoteCallRequest(invocationContext, invokerConfig);
Client clientSelected = null;
try {
clientSelected = clientManager.getClient(invokerConfig, request, selectedClients);
} catch (ServiceUnavailableException e) {
if (invokeTimes > 0) {
logger.error("Invoke method[" + invocationContext.getMethodName() + "] on service["
+ invokerConfig.getUrl() + "] failed with " + invokeTimes + " times");
throw lastError;
} else {
throw e;
}
}
selectedClients.add(clientSelected);
try {
invokeTimes++;
invocationContext.setClient(clientSelected);
InvocationResponse response = handler.handle(invocationContext);
if (lastError != null) {
logger.warn(
"Retry method[" + invocationContext.getMethodName() + "] on service["
+ invokerConfig.getUrl() + "] succeed after " + invokeTimes
+ " times, last failed error: " + lastError.getMessage(), lastError);
}
return response;
} catch (Throwable e) {
lastError = e;
if (e instanceof RequestTimeoutException) {
if (!timeoutRetry) {
throw e;
}
}
}
}
if (lastError != null) {
logger.error("Invoke method[" + invocationContext.getMethodName() + "] on service["
+ invokerConfig.getUrl() + "] failed with " + invokeTimes + " times");
throw lastError;
} else {
throw new RemoteInvocationException("Invoke method[" + invocationContext.getMethodName() + "] on service["
+ invokerConfig.getUrl() + "] failed with " + invokeTimes + " times, last error: "
+ (lastError != null ? lastError.getMessage() : ""), lastError != null
&& lastError.getCause() != null ? lastError.getCause() : lastError);
}
}
@Override
public String getName() {
return Constants.CLUSTER_FAILOVER;
}
}