/**
* Dianping.com Inc.
* Copyright (c) 2003-2013 All Rights Reserved.
*/
package com.dianping.pigeon.remoting.invoker.concurrent;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import com.dianping.pigeon.log.Logger;
import com.dianping.pigeon.log.LoggerLoader;
import com.dianping.pigeon.registry.RegistryManager;
import com.dianping.pigeon.remoting.common.monitor.trace.InvokerMonitorData;
import com.dianping.pigeon.remoting.common.domain.InvocationContext.TimePhase;
import com.dianping.pigeon.remoting.common.domain.InvocationContext.TimePoint;
import com.dianping.pigeon.remoting.common.domain.InvocationResponse;
import com.dianping.pigeon.remoting.common.exception.ApplicationException;
import com.dianping.pigeon.remoting.common.exception.BadResponseException;
import com.dianping.pigeon.remoting.common.exception.RpcException;
import com.dianping.pigeon.remoting.common.monitor.SizeMonitor;
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.domain.InvokerContext;
import com.dianping.pigeon.remoting.invoker.exception.RequestTimeoutException;
import com.dianping.pigeon.remoting.invoker.process.DegradationManager;
import com.dianping.pigeon.remoting.invoker.process.ExceptionManager;
import com.dianping.pigeon.remoting.invoker.process.filter.DegradationFilter;
import com.dianping.pigeon.remoting.invoker.util.InvokerUtils;
public class ServiceFutureImpl extends CallbackFuture implements Future {
private static final Logger logger = LoggerLoader.getLogger(ServiceFutureImpl.class);
private long timeout = Long.MAX_VALUE;
protected Thread callerThread;
protected InvokerContext invocationContext;
public ServiceFutureImpl(InvokerContext invocationContext, long timeout) {
super();
this.invocationContext = invocationContext;
this.timeout = timeout;
callerThread = Thread.currentThread();
}
@Override
public Object get() throws InterruptedException, ExecutionException {
return get(this.timeout);
}
public Object get(long timeoutMillis) throws InterruptedException, ExecutionException {
InvocationResponse response = null;
String addr = null;
if (client != null) {
addr = client.getAddress();
}
String callInterface = InvocationUtils.getRemoteCallFullName(invocationContext.getInvokerConfig().getUrl(),
invocationContext.getMethodName(), invocationContext.getParameterTypes());
transaction = monitor.createTransaction("PigeonFuture", callInterface, invocationContext);
if (transaction != null) {
transaction.setStatusOk();
transaction.logEvent("PigeonCall.callType", invocationContext.getInvokerConfig().getCallType(), "");
transaction.logEvent("PigeonCall.serialize", ""
+ (request == null ? invocationContext.getInvokerConfig().getSerialize() : request.getSerialize()),
"");
transaction.logEvent("PigeonCall.timeout", timeoutMillis + "",
invocationContext.getInvokerConfig().getTimeout() + "");
Client client = invocationContext.getClient();
if (client != null) {
String targetApp = RegistryManager.getInstance().getReferencedAppFromCache(client.getAddress());
transaction.logEvent("PigeonCall.app", targetApp, "");
transaction.logEvent("PigeonCall.server", client.getAddress(), "");
}
invocationContext.getTimeline().add(new TimePoint(TimePhase.F, System.currentTimeMillis()));
}
boolean isSuccess = false;
try {
try {
response = super.waitResponse(timeoutMillis);
if (transaction != null && response != null) {
String size = SizeMonitor.getInstance().getLogSize(response.getSize());
if (size != null) {
transaction.logEvent("PigeonCall.responseSize", size, "" + response.getSize());
}
invocationContext.getTimeline().add(new TimePoint(TimePhase.R, response.getCreateMillisTime()));
invocationContext.getTimeline().add(new TimePoint(TimePhase.F, System.currentTimeMillis()));
}
} catch (RuntimeException e) {
// failure degrade condition
InvocationResponse degradedResponse = null;
if (DegradationManager.INSTANCE.needFailureDegrade(invocationContext)) {
try {
invocationContext.getDegradeInfo().setFailureDegrade(true);
invocationContext.getDegradeInfo().setCause(e);
degradedResponse = DegradationFilter.degradeCall(invocationContext, true);
} catch (Throwable t) {
// won't happen
logger.warn("failure degrade in future call type error: " + t.toString());
}
}
if (degradedResponse != null) {// 返回同步调用模式的失败降级结果
return degradedResponse.getReturn();
}
// not failure degrade
DegradationManager.INSTANCE.addFailedRequest(invocationContext, e);
ExceptionManager.INSTANCE.logRpcException(addr, invocationContext.getInvokerConfig().getUrl(),
invocationContext.getMethodName(), "error with future call", e, request, response, transaction);
throw e;
}
setResponseContext(response);
if (response.getMessageType() == Constants.MESSAGE_TYPE_SERVICE) {
isSuccess = true;
return response.getReturn();
} else if (response.getMessageType() == Constants.MESSAGE_TYPE_EXCEPTION) {
// failure degrade condition
InvocationResponse degradedResponse = null;
if (DegradationManager.INSTANCE.needFailureDegrade(invocationContext)) {
try {
invocationContext.getDegradeInfo().setFailureDegrade(true);
invocationContext.getDegradeInfo().setCause(InvokerUtils.toRpcException(response));
degradedResponse = DegradationFilter.degradeCall(invocationContext, true);
} catch (Throwable t) {
// won't happen
logger.warn("failure degrade in future call type error: " + t.toString());
}
}
if (degradedResponse != null) {// 返回同步调用模式的失败降级结果
return degradedResponse.getReturn();
}
// not failure degrade
RpcException e = ExceptionManager.INSTANCE.logRemoteCallException(addr,
invocationContext.getInvokerConfig().getUrl(), invocationContext.getMethodName(),
"remote call error with future call", request, response, transaction);
if (e != null) {
DegradationManager.INSTANCE.addFailedRequest(invocationContext, e);
throw e;
}
} else if (response.getMessageType() == Constants.MESSAGE_TYPE_SERVICE_EXCEPTION) {
Throwable e = ExceptionManager.INSTANCE
.logRemoteServiceException("remote service biz error with future call", request, response);
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else if (e != null) {
throw new ApplicationException(e);
}
}
RpcException e = new BadResponseException(response.toString());
throw e;
} finally {
if (transaction != null) {
DegradationManager.INSTANCE.monitorDegrade(invocationContext, transaction);
invocationContext.getTimeline().add(new TimePoint(TimePhase.E, System.currentTimeMillis()));
try {
transaction.complete();
} catch (RuntimeException e) {
monitor.logMonitorError(e);
}
}
InvokerMonitorData monitorData = (InvokerMonitorData) invocationContext.getMonitorData();
if (monitorData != null) {
monitorData.setIsSuccess(isSuccess);
monitorData.complete();
}
}
}
@Override
public Object get(long timeout, TimeUnit unit) throws java.lang.InterruptedException,
java.util.concurrent.ExecutionException, java.util.concurrent.TimeoutException {
long timeoutMs = unit.toMillis(timeout);
try {
return get(timeoutMs);
} catch (RequestTimeoutException e) {
throw new TimeoutException(timeoutMs + "ms timeout:" + e.getMessage());
} catch (InterruptedException e) {
throw e;
}
}
protected void processContext() {
Thread currentThread = Thread.currentThread();
if (currentThread == callerThread) {
super.processContext();
}
}
@Override
public void dispose() {
super.dispose();
if (transaction != null) {
try {
transaction.complete();
} catch (RuntimeException e) {
}
}
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return cancel();
}
}