在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。