package io.craft.atom.rpc;
import io.craft.atom.protocol.rpc.model.RpcMessage;
import io.craft.atom.protocol.rpc.model.RpcMethod;
import io.craft.atom.rpc.spi.RpcApi;
import io.craft.atom.rpc.spi.RpcChannel;
import io.craft.atom.rpc.spi.RpcExecutorFactory;
import io.craft.atom.rpc.spi.RpcInvoker;
import io.craft.atom.rpc.spi.RpcProcessor;
import io.craft.atom.util.thread.MonitoringExecutorService;
import io.craft.atom.util.thread.NamedThreadFactory;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author mindwind
* @version 1.0, Aug 7, 2014
*/
public class DefaultRpcProcessor implements RpcProcessor {
private static final Logger LOG = LoggerFactory.getLogger(DefaultRpcProcessor.class);
@Getter @Setter private RpcInvoker invoker ;
@Getter @Setter private RpcExecutorFactory executorFactory;
@Getter @Setter private ExecutorService timeoutExecutor;
// ~ -------------------------------------------------------------------------------------------------------------
public DefaultRpcProcessor() {
this.timeoutExecutor = Executors.newCachedThreadPool(new NamedThreadFactory("craft-atom-rpc-timeout"));
}
// ~ -------------------------------------------------------------------------------------------------------------
@Override
public void process(RpcMessage req, RpcChannel channel) {
if (req == null) return;
if (req.isHeartbeat()) {
RpcMessage rsp = RpcMessages.newHbResponseRpcMessage(req.getId());
channel.write(rsp);
LOG.debug("[CRAFT-ATOM-RPC] Rpc server processor process heartbeat, |hbreq={}, hbrsp={}, channel={}|", req, rsp, channel);
return;
}
RpcApi api = api(req);
MonitoringExecutorService executor = null;
try {
executor = executor(api);
executor.execute(new ProcessTask(req, channel));
} catch (RejectedExecutionException e) {
LOG.warn("[CRAFT-ATOM-RPC] Rpc server processor overload, |executor={}|", executor);
channel.write(RpcMessages.newRsponseRpcMessage(req.getId(), new RpcException(RpcException.SERVER_OVERLOAD, "server overload")));
} catch (RpcException e) {
LOG.warn("[CRAFT-ATOM-RPC] Rpc server processor error", e);
channel.write(RpcMessages.newRsponseRpcMessage(req.getId(), e));
}
LOG.debug("[CRAFT-ATOM-RPC] Rpc server processor process request, |req={}, channel={}, executor={}|", req, channel, executor);
}
private RpcMessage process0(RpcMessage req) {
RpcMessage rsp;
try {
rsp = invoker.invoke(req);
} catch (RpcException e) {
rsp = RpcMessages.newRsponseRpcMessage(req.getId(), e);
}
return rsp;
}
private int rpcTimeoutInMillis(RpcMessage req) {
int timeout = req.getRpcTimeoutInMillis();
if (timeout == 0) { timeout = Integer.MAX_VALUE; }
return timeout;
}
private MonitoringExecutorService executor(RpcApi api) {
return executorFactory.getExecutor(api);
}
private RpcApi api(RpcMessage msg) {
String rpcId = msg.getBody().getRpcId();
RpcMethod rpcMethod = msg.getBody().getRpcMethod();
Class<?> rpcInterface = msg.getBody().getRpcInterface();
DefaultRpcApi api = new DefaultRpcApi(rpcId, rpcInterface, rpcMethod);
return api;
}
@Override
public int waitCount(RpcApi api) {
return executor(api).waitCount();
}
@Override
public int processingCount(RpcApi api) {
return executor(api).executingCount();
}
@Override
public long completeCount(RpcApi api) {
return executor(api).completeCount();
}
// ~ -------------------------------------------------------------------------------------------------------------
private class ProcessTask implements Runnable {
private RpcMessage req;
private RpcChannel channel;
public ProcessTask(RpcMessage req, RpcChannel channel) {
this.req = req;
this.channel = channel;
}
@Override
public void run() {
RpcMessage rsp;
try {
Future<RpcMessage> future = timeoutExecutor.submit(new Callable<RpcMessage>() {
@Override
public RpcMessage call() throws Exception {
return process0(req);
}
});
// One way request
if (req.isOneway()) return;
// Wait response
rsp = future.get(rpcTimeoutInMillis(req), TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
LOG.warn("[CRAFT-ATOM-RPC] Rpc server processor execute error", e);
rsp = RpcMessages.newRsponseRpcMessage(req.getId(), new RpcException(RpcException.SERVER_ERROR, "server error"));
} catch (TimeoutException e) {
LOG.warn("[CRAFT-ATOM-RPC] Rpc server processor execute timeout", e);
rsp = RpcMessages.newRsponseRpcMessage(req.getId(), new RpcException(RpcException.SERVER_TIMEOUT, "server timeout"));
} catch (Exception e) {
LOG.warn("[CRAFT-ATOM-RPC] Rpc server processor execute unknown error", e);
rsp = RpcMessages.newRsponseRpcMessage(req.getId(), new RpcException(RpcException.UNKNOWN, "unknown error"));
}
try {
channel.write(rsp);
LOG.debug("[CRAFT-ATOM-RPC] Rpc server processor process response, |rsp={}, channel={}|", rsp, channel);
} catch (Exception e) {
LOG.warn("[CRAFT-ATOM-RPC] Rpc server processor write back rpc response fail", e);
}
}
}
@Override
public void close() {
timeoutExecutor.shutdownNow();
executorFactory.shutdown();
}
}