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; } } }