在url里面指定某個參數方法的第幾個參數是一個callback,也就是說這個實參是一個callback,雖然消費者把這個callback實例傳給了提供者,但是提供者實際本地沒有這個callback實例,提供者只能繼續遠程調用消費者這個callback拿到結果。
比如消費者和提供者都在url里面指定 xxx.0.callback=true,表示xxx這個method的第0個參數是一個callback。
提供者有這樣一個接口服務:
public void xxx(IDemoCallback callback, String arg1, int runs, int sleep);
提供者具體實現:
public void xxx(final IDemoCallback callback, String arg1, final int runs, final int sleep) {
callback.yyy("Sync callback msg .This is callback data. arg1:" + arg1);
System.out.println("xxx invoke complete");
}
對於提供者來說,這個callback是消費傳過來的一個實例,本地沒有,那么在調用的時候如果執行到callback只能遠程調用消費者這個callback實例拿到結果再繼續運行
System.out.println("xxx invoke complete");
消費者調用了方式:
demoProxy.xxx(new IDemoCallback() {
public String yyy(String msg) {
System.out.println("Recived callback: " + msg);
count.incrementAndGet();
return "ok";
}
}, "other custom args", 10, 100);
下面解釋下dubbo如何做的。對於一個rpc調用是這么序列化的:
method-name按照striing序列化
parameter-type按照類型的描述符的string進行序列化
對於arguments,如果不是callback,也就是沒有在url指定 xxx.0.callback=true這種的話,那么直接在hessian序列化
public static Object encodeInvocationArgument(Channel channel, RpcInvocation inv, int paraIndex) throws IOException {
// get URL directly
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];
}
}
那么如果是一個callback的話,return null沒有在argument進行序列化,而是把一個callback實例通過exportOrunexportCallbackService放到attachment里面,具體這個exportOrunexportCallbackService干了啥呢?具體比較復雜,總結來說就是構造了一個新的dubboinvoer,這個invoker的interface就是這個callback的interface+hashcode(加hashcode考慮有多個interface的callback,他們在不同的方法上),他提供的方法就是這個callback要做的方法。
也就是說消費者在把callback序列化的時候,實際上構造了一個dubboinvoker,等待提供者來調用。
提供者在接收到消息的時候,對method-name、parameter-type進行反序列化,並且通過url知道哪些參數是一個callback,接着以這個遠端的channel(在netty回調的時候decode方法可以輕松拿到這個channel)構造了一個referinvoker
這個referinvoker就是對消費者callback的一個抽象,同樣的這個invoker的interface就是這個callback的interface+hashcode(加hashcode考慮有多個interface的callback,他們在不同的方法上),他提供的方法就是這個callback要做的方法。
也就是說提供者反序列化的時候,對這個callback實現了一個referinvoker,等到真正要調用這個callback的時候,雖然是本地執行,實際上是遠程調用到消費者,而不是在本地執行callback。