package com.dianping.pigeon.remoting.invoker.cluster;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import com.dianping.pigeon.config.ConfigManagerLoader;
import com.dianping.pigeon.log.Logger;
import com.dianping.pigeon.log.LoggerLoader;
import com.dianping.pigeon.remoting.common.domain.CallMethod;
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.common.util.InvocationUtils;
import com.dianping.pigeon.remoting.invoker.Client;
import com.dianping.pigeon.remoting.invoker.ClientManager;
import com.dianping.pigeon.remoting.invoker.concurrent.FutureFactory;
import com.dianping.pigeon.remoting.invoker.config.InvokerConfig;
import com.dianping.pigeon.remoting.invoker.domain.DefaultInvokerContext;
import com.dianping.pigeon.remoting.invoker.domain.InvokerContext;
import com.dianping.pigeon.remoting.invoker.util.InvokerUtils;
import com.dianping.pigeon.remoting.invoker.util.InvokerUtils.FutureResponse;
import com.dianping.pigeon.threadpool.NamedThreadFactory;
public class ForkingCluster implements Cluster {
private ClientManager clientManager = ClientManager.getInstance();
private static final Logger logger = LoggerLoader.getLogger(ForkingCluster.class);
private final ExecutorService executor = Executors
.newCachedThreadPool(new NamedThreadFactory("Pigeon-Client-Fork-Processor", true));
private static final String KEY_FORKING_SIZE = "pigeon.invoker.forking.size";
private Random r = new Random();
public ForkingCluster() {
ConfigManagerLoader.getConfigManager().getIntValue(KEY_FORKING_SIZE, 0);
}
private List<Client> randomList(List<Client> clients) {
List<Client> randomClients = clients;
int size = ConfigManagerLoader.getConfigManager().getIntValue(KEY_FORKING_SIZE, 0);
int len = clients.size();
if (size > 0 && size < len) {
randomClients = new ArrayList<Client>(size);
int startIndex = (int) (r.nextDouble() * len);
for (int i = startIndex; i < startIndex + size; i++) {
int idx = i < len ? i : (i - len);
randomClients.add(clients.get(idx));
}
}
return randomClients;
}
@Override
public InvocationResponse invoke(final ServiceInvocationHandler handler, final InvokerContext invocationContext)
throws Throwable {
final InvokerConfig<?> invokerConfig = invocationContext.getInvokerConfig();
InvocationRequest request = InvokerUtils.createRemoteCallRequest(invocationContext, invokerConfig);
final List<Client> clients = clientManager.getAvailableClients(invokerConfig, request);
final AtomicInteger count = new AtomicInteger();
final BlockingQueue<Object> ref = new LinkedBlockingQueue<Object>();
final List<Client> selectedClients = randomList(clients);
for (final Client client : selectedClients) {
executor.execute(new Runnable() {
public void run() {
InvokerContext ctxt = new DefaultInvokerContext(invokerConfig, invocationContext.getMethodName(),
invocationContext.getParameterTypes(), invocationContext.getArguments());
ctxt.setClient(client);
ctxt.setRequest(null);
ctxt.setRequest(InvokerUtils.createRemoteCallRequest(ctxt, invokerConfig));
try {
InvocationResponse resp = handler.handle(ctxt);
ref.offer(resp);
} catch (Throwable e) {
int value = count.incrementAndGet();
if (value >= selectedClients.size()) {
ref.offer(e);
}
}
}
});
}
Object ret = null;
if (request.getTimeout() > 0) {
ret = ref.poll(request.getTimeout(), TimeUnit.MILLISECONDS);
} else {
ret = ref.take();
}
if (ret instanceof Throwable) {
throw (Throwable) ret;
} else if ((ret instanceof FutureResponse)
&& CallMethod.isFuture(invokerConfig.getCallType())) {
FutureFactory.setFuture(((FutureResponse) ret).getServiceFuture());
} else if (ret == null) {
throw InvocationUtils.newTimeoutException("timeout while waiting forking response:" + request);
}
return (InvocationResponse) ret;
}
@Override
public String getName() {
return Constants.CLUSTER_FORKING;
}
}