@Override
protected Result doInvoke(final Invocation invocation) throws Throwable {
RpcInvocation inv = (RpcInvocation) invocation;
final String methodName = RpcUtils.getMethodName(invocation);
inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
inv.setAttachment(Constants.VERSION_KEY, version);
ExchangeClient currentClient;
if (clients.length == 1) {
currentClient = clients[0];
} else {
currentClient = clients[index.getAndIncrement() % clients.length];
}
try {
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
//分支1: 如果是异步并且调用方直接表示不会调用其返回值
// <dubbo : method name="findFoo" async="true" return="false"/>
if (isOneway) {
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent);
RpcContext.getContext().setFuture(null);
return new RpcResult();
//分支2: 如果是异步且需要返回值, 意味着要提供给用户一个future: RpcContext.getContext().getFuture()
//用户才能通过此方法获取future再处理. 如果用户并不需要就要使用 分支1. 免得dubbo给你维护个future. 浪费性能
} else if (isAsync) {
ResponseFuture future = currentClient.request(inv, timeout);
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
return new RpcResult();
} else {
RpcContext.getContext().setFuture(null);
//分支3, 同步, 当然也不用futrue.
// 这里的get()会让future陷入有条件的限时等待, 或者说 超时或者被signal有结果会返回
return (Result) currentClient.request(inv, timeout).get();
}
// ps1: 超时对每个分支都需要, 所以还是需要一个定时任务(RemotingInvocationTimeoutScan 一个线程sleep 30毫秒轮询, 2.7版本是使用时间轮)去检查有条件的触发超时返回. (当然, 如果是同步调用本身get就自带超时属性, 个人认为, 同步的不需要放在时间轮)
// ps2: 超时时间, 只有一个, 在生成协议的时候就决定了(由一定的优先级取出决胜者)
} catch (TimeoutException e) {
throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
} catch (RemotingException e) {
throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
}
}