在Dubbo作为消费者再通过NettyServer准备调用相应服务的发送请求的时候先通过NettyCodeAdapter来对具体的数据进行编码。
在编码的过程中,遍历方法中的参数,来判断如果消费者调用了相应参数的方法,是否有必要进行远程回调。
判断的依据为,在消费者组装需要远程调用的方法的参数的时候,对所需要回调的方法中的第几个参数进行了配置,生产者根据其方法的第几个参数的callBack属性来判断是否有必要进行回调,该参数在url中的形式为methodName.第几个参数.callBack。
private static byte isCallBack(URL url, String methodName, int argIndex) {
// parameter callback rule: method-name.parameter-index(starting from 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;
}
根据这个参数来确认是否有必要进行回调。
在判断生产者需要对相应的参数的调用进行远程回调的时候,则需要跟生产者一样暴露相应的回调方法。
private static String exportOrunexportCallbackService(Channel channel, URL url, Class clazz, Object inst, Boolean export) throws IOException {
int instid = System.identityHashCode(inst);
Map<String, String> params = new HashMap<String, String>(3);
// no need to new client again
params.put(Constants.IS_SERVER_KEY, Boolean.FALSE.toString());
// mark it's a callback, for troubleshooting
params.put(Constants.IS_CALLBACK_SERVICE, Boolean.TRUE.toString());
String group = url.getParameter(Constants.GROUP_KEY);
if (group != null && group.length() > 0) {
params.put(Constants.GROUP_KEY, group);
}
// add method, for verifying against method, automatic fallback (see dubbo protocol)
params.put(Constants.METHODS_KEY, StringUtils.join(Wrapper.getWrapper(clazz).getDeclaredMethodNames(), ","));
Map<String, String> tmpmap = new HashMap<String, String>(url.getParameters());
tmpmap.putAll(params);
tmpmap.remove(Constants.VERSION_KEY);// doesn't need to distinguish version for callback
tmpmap.put(Constants.INTERFACE_KEY, clazz.getName());
URL exporturl = new URL(DubboProtocol.NAME, channel.getLocalAddress().getAddress().getHostAddress(), channel.getLocalAddress().getPort(), clazz.getName() + "." + instid, tmpmap);
// no need to generate multiple exporters for different channel in the same JVM, cache key cannot collide.
String cacheKey = getClientSideCallbackServiceCacheKey(instid);
String countkey = getClientSideCountKey(clazz.getName());
if (export) {
// one channel can have multiple callback instances, no need to re-export for different instance.
if (!channel.hasAttribute(cacheKey)) {
if (!isInstancesOverLimit(channel, url, clazz.getName(), instid, false)) {
Invoker<?> invoker = proxyFactory.getInvoker(inst, clazz, exporturl);
// should destroy resource?
Exporter<?> exporter = protocol.export(invoker);
// this is used for tracing if instid has published service or not.
channel.setAttribute(cacheKey, exporter);
logger.info("export a callback service :" + exporturl + ", on " + channel + ", url is: " + url);
increaseInstanceCount(channel, countkey);
}
}
} else {
if (channel.hasAttribute(cacheKey)) {
Exporter<?> exporter = (Exporter<?>) channel.getAttribute(cacheKey);
exporter.unexport();
channel.removeAttribute(cacheKey);
decreaseInstanceCount(channel, countkey);
}
}
return String.valueOf(instid);
}
这里根据消费者的地址,直接构造了直连的url来完成远程回调的需要。之后与别的服务一样构造代理,并通过DubboProtocol的export()方法进行暴露。并将得到的exporter作为value,这样并通过参数的hashcode构造key,组成键值对存储在channel中,保证生产者能够接收到请求后根据key在channel中得到相应的exporter,完成代理的创建,使得可以完成远程回调的功能。