package com.snowcattle.game.service.rpc.client;
import com.snowcattle.game.manager.LocalMananger;
import com.snowcattle.game.common.config.GameServerConfigService;
import com.snowcattle.game.common.constant.Loggers;
import com.snowcattle.game.service.net.RpcRequest;
import com.snowcattle.game.service.net.RpcResponse;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.ReentrantLock;
/**
* RPCFuture for async RPC call
*/
public class RPCFuture implements Future<Object> {
private Logger logger = Loggers.rpcLogger;
private Sync sync;
private RpcRequest request;
private RpcResponse response;
private long startTime;
private long responseTimeThreshold = 5000;
private List<AsyncRPCCallback> pendingCallbacks = new ArrayList<AsyncRPCCallback>();
private ReentrantLock lock = new ReentrantLock();
public RPCFuture(RpcRequest request) {
this.sync = new Sync();
this.request = request;
this.startTime = System.currentTimeMillis();
}
@Override
public boolean isDone() {
return sync.isDone();
}
@Override
public Object get() throws InterruptedException, ExecutionException {
sync.acquire(-1);
if (this.response != null) {
return this.response.getResult();
} else {
return null;
}
}
@Override
public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
boolean success = sync.tryAcquireNanos(-1, unit.toNanos(timeout));
if (success) {
if (this.response != null) {
return this.response.getResult();
} else {
return null;
}
} else {
throw new RuntimeException("Timeout exception. Request id: " + this.request.getRequestId()
+ ". Request class name: " + this.request.getClassName()
+ ". Request method: " + this.request.getMethodName());
}
}
@Override
public boolean isCancelled() {
throw new UnsupportedOperationException();
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
throw new UnsupportedOperationException();
}
public void done(RpcResponse reponse) {
this.response = reponse;
sync.release(1);
invokeCallbacks();
// Threshold
long responseTime = System.currentTimeMillis() - startTime;
if (responseTime > this.responseTimeThreshold) {
logger.warn("Service response time is too slow. Request id = " + reponse.getRequestId() + ". Response Time = " + responseTime + "ms");
}
}
public boolean isTimeout(){
long responseTime = System.currentTimeMillis() - startTime;
GameServerConfigService gameServerConfigService = LocalMananger.getInstance().getLocalSpringServiceManager().getGameServerConfigService();
int timeOut = gameServerConfigService.getGameServerConfig().getRpcFutureDeleteTimeOut();
if (responseTime >= timeOut) {
return true;
}
return false;
}
private void invokeCallbacks() {
lock.lock();
try {
for (final AsyncRPCCallback callback : pendingCallbacks) {
runCallback(callback);
}
} finally {
lock.unlock();
}
}
public RPCFuture addCallback(AsyncRPCCallback callback) {
lock.lock();
try {
if (isDone()) {
runCallback(callback);
} else {
this.pendingCallbacks.add(callback);
}
} finally {
lock.unlock();
}
return this;
}
private void runCallback(final AsyncRPCCallback callback) {
final RpcResponse res = this.response;
RpcProxyService rpcProxyService = LocalMananger.getInstance().getLocalSpringServiceManager().getRpcProxyService();
rpcProxyService.submit(new Runnable() {
@Override
public void run() {
if (!res.isError()) {
callback.success(res.getResult());
} else {
callback.fail(new RuntimeException("Response error", new Throwable(res.getError())));
}
}
});
}
static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1L;
//future status
private final int done = 1;
private final int pending = 0;
protected boolean tryAcquire(int acquires) {
return getState() == done ? true : false;
}
protected boolean tryRelease(int releases) {
if (getState() == pending) {
if (compareAndSetState(pending, done)) {
return true;
}
}
return false;
}
public boolean isDone() {
getState();
return getState() == done;
}
}
}