Dubbo 异步变为同步

2,592 阅读1分钟

当消费者调用接口

String hello = demoService.sayHello("world"); 

这里是需要阻塞结果出来的。但是 demoService.sayHello("world")发出去的请求是异步发出的(基于netty NIO)。

那么这里是怎么同步拿到结果的呢?我们进入代码

在消费端发出去时,会走到

 //DubboInvoker
protected Result doInvoke(final Invocation invocation) throws Throwable {
      
          //...
            if (isOneway) {//2.异步没返回值
                boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
                currentClient.send(inv, isSent);
                RpcContext.getContext().setFuture(null);
                return new RpcResult();
            } else if (isAsync) {//1.异步有返回值--发送者
                ResponseFuture future = currentClient.request(inv, timeout);
                FutureAdapter<Object> futureAdapter = new FutureAdapter<>(future);
                RpcContext.getContext().setFuture(futureAdapter);
                Result result;
                if (isAsyncFuture) {
                    result = new AsyncRpcResult(futureAdapter, futureAdapter.getResultFuture(), false);
                } else {
                    result = new SimpleAsyncRpcResult(futureAdapter, futureAdapter.getResultFuture(), false);
                }
                return result;
            } else {//3.异步变同步
                RpcContext.getContext().setFuture(null);
                return (Result) currentClient.request(inv, timeout)//返回下面的future
                                            .get();//进入get()方法,是当前线程阻塞。那么当有结果返回时,唤醒这个线程
            }
        }


//--HeaderExchangeChannel
 public ResponseFuture request(Object request, int timeout) throws RemotingException {
        Request req = new Request();
        req.setVersion(Version.getProtocolVersion());
        req.setTwoWay(true);
        req.setData(request);
    //在发送的那一刻,当前线程是得到future这个返回值
        DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout);
        try {
            channel.send(req);//通过netty发送出去
        } catch (RemotingException e) {
            future.cancel();
            throw e;
        }
        return future;
    }

此时,调用线程已经阻塞住了。等待结果返回来唤醒。

好,那么我们看看提供者的响应是怎么过来的?并且怎么找到当前调用者的?

当提供者用netty把消息发出时,我们接收端会进入

//--DefaultFuture
  public static void received(Channel channel, Response response) {
        try {
            DefaultFuture future = FUTURES.remove(response.getId());
            if (future != null) {
                future.doReceived(response);
            } else {
            }
        } finally {
            CHANNELS.remove(response.getId());
        }
    }
  private void doReceived(Response res) {
        lock.lock();
        try {
            response = res;//拿到了响应
            if (done != null) {
                done.signal();//唤醒线程
            }
        } finally {
            lock.unlock();
        }
        if (callback != null) {
            invokeCallback(callback);
        }
    }

此时,消费端的线程被唤醒,就拿到结果了。异步完美转为同步了。