这里dubbo的官方文档有一个回调的例子,看看dubbo在内部是如何支持回调的。
CallbackServiceCodec类是用来编码和解码调用方法的参数的,dubbo正是通过判断参数的类型来处理回调的:
private static byte isCallBack(URL url, String methodName ,int argIndex){
//参数callback的规则是 方法名称.参数index(0开始).callback
byte isCallback = CALLBACK_NONE;
if (url != null ) {
String callback = url.getParameter(methodName+"."+argIndex+".callback");
if(callback != null) {
if (callback.equalsIgnoreCase("true")) {
isCallback = CALLBACK_CREATE;
}else if(callback.equalsIgnoreCase("false")){
isCallback = CALLBACK_DESTROY;
}
}
}
return isCallback;
}
isCallback用来判断参数的类型是否是回调。
public static Object encodeInvocationArgument(Channel channel, RpcInvocation inv, int paraIndex) throws IOException{
//encode时可直接获取url
URL url = inv.getInvoker() == null ? null : inv.getInvoker().getUrl();
byte callbackstatus = isCallBack(url, inv.getMethodName(), paraIndex);
Object[] args = inv.getArguments();
Class<?>[] pts = inv.getParameterTypes();
switch (callbackstatus) {
case CallbackServiceCodec.CALLBACK_NONE:
return args[paraIndex];
case CallbackServiceCodec.CALLBACK_CREATE:
inv.setAttachment(INV_ATT_CALLBACK_KEY + paraIndex , exportOrunexportCallbackService(channel, url, pts[paraIndex], args[paraIndex], true));
return null;
case CallbackServiceCodec.CALLBACK_DESTROY:
inv.setAttachment(INV_ATT_CALLBACK_KEY + paraIndex, exportOrunexportCallbackService(channel, url, pts[paraIndex], args[paraIndex], false));
return null;
default:
return args[paraIndex];
}
}
可以看到encodeInvocationArgument里面判断参数的类型如果是回调(CALLBACK_CREATE,值为1)的话,会执行exportOrunexportCallbackService,里面会为回调参数对象暴露一个接口:
Invoker<?> invoker = proxyFactory.getInvoker(inst, clazz, exporturl);
Exporter<?> exporter = protocol.export(invoker);
ok,客户端把回调接口暴漏出来了。
再来看服务器端:
同样在CallbackServiceCodec类里面:
public static Object decodeInvocationArgument(Channel channel, RpcInvocation inv, Class<?>[] pts, int paraIndex, Object inObject) throws IOException{
// ...
byte callbackstatus = isCallBack(url, inv.getMethodName(), paraIndex);
switch (callbackstatus) {
case CallbackServiceCodec.CALLBACK_NONE:
return inObject;
case CallbackServiceCodec.CALLBACK_CREATE:
return referOrdestroyCallbackService(channel, url, pts[paraIndex], inv, Integer.parseInt(inv.getAttachment(INV_ATT_CALLBACK_KEY + paraIndex)), true);
// ....
}
}
在解析参数时,判断如果是回调参数,那么生成一个代理:
Invoker<?> invoker = new ChannelWrappedInvoker(clazz, channel, referurl, String.valueOf(instid));
proxy = proxyFactory.getProxy(invoker);
可以看到根据url生成了一个代理,那么我们实际在调用回调方法的时候,会被传递到客户端去执行客户端的代码。
另外只需要在provider中配置回调参数,这些配置信息会通过registry传递到consumer中。