本文将详细分析Dubbo服务异步调用与事件回调机制。
1、异步调用与事件回调机制
1.1 异步回调
1.2 事件回调
2、源码分析异步调用与事件回调机制
在Dubbo中,引入特定的过滤器FutureFilter来处理异步调用相关的逻辑,其定义如下:
@Activate(group = Constants.CONSUMER)
public class FutureFilter implements Filter {
}
group=CONSUMER说明该过滤器属于消费端过滤器。
接下来从从invoke方法详细分析其实现逻辑。
public Result invoke(final Invoker> invoker, final Invocation invocation) throws RpcException {
final boolean isAsync = RpcUtils.isAsync(invoker.getUrl(), invocation); // @1
fireInvokeCallback(invoker, invocation); // @2
// need to configure if there's return value before the invocation in order to help invoker to judge if it's
// necessary to return future.
Result result = invoker.invoke(invocation); // @3
if (isAsync) {
asyncCallback(invoker, invocation); // @4
} else {
syncCallback(invoker, invocation, result); // @5
}
return result;
}
代码@1:首先从URL中获取是否是异步调用标志,其配置属性为< dubbo:service async=”“/>获取其子标签< dubbo:method async=”“/>。
代码@2:同步调用oninvoke事件,执行invoke方法之前的事件。
代码@3:继续沿着调用链调用,最终会到具体的协议Invoker,例如DubboInvoker,发生具体的服务调用,跟踪一下同步、异步调用的实现细节。
代码@4:如果调用方式是异步模式,则异步调用onreturn或onthrow事件。
代码@5:如果调用方式是同步模式,则同步调用onreturn或onthrow事件。
2.1 源码分析FutureFilter#fireInvokeCallback
private void fireInvokeCallback(final Invoker> invoker, final Invocation invocation) {
final Method onInvokeMethod = (Method) StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(),
Constants.ON_INVOKE_METHOD_KEY)); // @1
final Object onInvokeInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(),
Constants.ON_INVOKE_INSTANCE_KEY)); // @2
if (onInvokeMethod == null && onInvokeInst == null) { // @3
return;
}
if (onInvokeMethod == null || onInvokeInst == null) { // @4
throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() + " has a onreturn callback config , but no such " + (onInvokeMethod == null ? "method" :
"instance") + " found. url:" + invoker.getUrl());
}
if (!onInvokeMethod.isAccessible()) {
onInvokeMethod.setAccessible(true);
}
Object[] params = invocation.getArguments();
try {
onInvokeMethod.invoke(onInvokeInst, params); // @5
} catch (InvocationTargetException e) {
fireThrowCallback(invoker, invocation, e.getTargetException()); // @6
} catch (Throwable e) {
fireThrowCallback(invoker, invocation, e); // @7
}
}
代码@1:StaticContext.getSystemContext()中根据key:serviceKey + “.” + method + “.” + “oninvoke.method” 获取配置的oninvoke.method方法名。其中serviceKey为[group]/interface:[version],其中group与version可能为空,忽略。
代码@2:同样根据key:serviceKey + “.” + method + “.” + “oninvoke.instance” 从StaticContext.getSystemContext()获取oninvoke.method方法所在的实例名对象,也就是说该调用哪个对象的oninvoke.method指定的方法。这里就有一个疑问,这些数据是在什么时候存入StaticContext中的呢?下文会详细分析。
代码@3、@4:主要检测< dubbo:method oninvoke=”“/>配置的正确性,其正确的配置方式如下:“实例名.方法名”,例如:
代码@5:根据发射机制,调用oninvoke中指定的实例的指定方法,注意,这里传入的参数为调用远程RPC服务的参数。
注意:从这里可以看出,如果要实现事件通知,也即在调用远程RPC服务之前,之后、抛出异常时执行回调函数,该回调事件的方法的参数列表需要与被调用服务的参数列表一致。
代码@6、@7,如果在执行调用前方法(oninvoke)事件方法失败,则会同步调用onthrow中定义的方法(如有定义)。关于dubbo:method oninvoke属性的解析以及在什么时候会向StaticContext.getSystemContext()中添加信息,将在下文统一介绍。
2.2 源码分析DubboInvoker关于同步异步调用处理
在上文提到FutureFilter#invoke中的第三步调用invoker.invoker方法时,我们应该会有兴趣了解一下真实的invoker是如何处理同步、异步请求的。
我们以dubbo协议DubboInvoker来重点分析一下其实现原理:
DubboInvoker#doInvoke
try {
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation); // @1
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
if (isOneway) {
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent); // @2
RpcContext.getContext().setFuture(null);
return new RpcResult();
} else if (isAsync) {
ResponseFuture future = currentClient.request(inv, timeout); // @3
RpcContext.getContext().setFuture(new FutureAdapter
这里是通过Future模式来实现异步调用的,同步调用也是通过异步调用来实现,只是同步调用发起后,直接调用future#get的方法来同步等待结果的返回,而异步调用只返回Future Response,在用户需要关心其结果时才调用get方法。
2.3 源码分析asyncCallback与syncCallback
前面介绍了方法执行之前oninvoker事件的调用分析,接下来分析RPC服务调用完成后,onreturn和onthrow方法的调用逻辑。
异步回调与同步回调的区别就是调用onreturn(fireReturnCallback)和onthrow(fireThrowCallback)调用的地方不同,如果是同步调用,也就是在完成RPC服务调用后,立即调用相关的回调方法,如果是异步调用的话,RPC服务完成后,通过Future模式异步执行。其实关于onreturn、onthrow属性的解析,执行与oninvoker属性的解析完全一样,再这里也就不重复介绍了。
private void syncCallback(final Invoker> invoker, final Invocation invocation, final Result result) { if (result.hasException()) { fireThrowCallback(invoker, invocation, result.getException()); } else { fireReturnCallback(invoker, invocation, result.getValue()); } } private void asyncCallback(final Invoker> invoker, final Invocation invocation) { Future> f = RpcContext.getContext().getFuture(); if (f instanceof FutureAdapter) { ResponseFuture future = ((FutureAdapter>) f).getFuture(); future.setCallback(new ResponseCallback() { public void done(Object rpcResult) { if (rpcResult == null) { logger.error(new IllegalStateException("invalid result value : null, expected " + Result.class.getName())); return; } ///must be rpcResult if (!(rpcResult instanceof Result)) { logger.error(new IllegalStateException("invalid result type :" + rpcResult.getClass() + ", expected " + Result.class.getName())); return; } Result result = (Result) rpcResult; if (result.hasException()) { fireThrowCallback(invoker, invocation, result.getException()); } else { fireReturnCallback(invoker, invocation, result.getValue()); } } public void caught(Throwable exception) { fireThrowCallback(invoker, invocation, exception); } }); } }
public void setCallback(ResponseCallback callback) { if (isDone()) { invokeCallback(callback); } else { boolean isdone = false; lock.lock(); try { if (!isDone()) { this.callback = callback; } else { isdone = true; } } finally { lock.unlock(); } if (isdone) { invokeCallback(callback); } } }
private void invokeCallback(ResponseCallback c) { ResponseCallback callbackCopy = c; if (callbackCopy == null) { throw new NullPointerException("callback cannot be null."); } c = null; Response res = response; if (res == null) { throw new IllegalStateException("response cannot be null. url:" + channel.getUrl()); } if (res.getStatus() == Response.OK) { try { callbackCopy.done(res.getResult()); } catch (Exception e) { logger.error("callback invoke error .reasult:" + res.getResult() + ",url:" + channel.getUrl(), e); } } else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) { try { TimeoutException te = new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage()); callbackCopy.caught(te); } catch (Exception e) { logger.error("callback invoke error ,url:" + channel.getUrl(), e); } } else { try { RuntimeException re = new RuntimeException(res.getErrorMessage()); callbackCopy.caught(re); } catch (Exception e) { logger.error("callback invoke error ,url:" + channel.getUrl(), e); } } }
异步处理和异步回调的共同点都是 基于客户socket 有结果之后把结果放入到了future 对象里的response 字段里了。
异步处理和异步回调的区别:
异步处理是把future放到Rpccontext 里,调用线程通过Rpccontext 获取future ,然后获取对应值。
异步事件处理是 socket 获取到结果之后,直接调用 注册好的函数处理。