当消费者调用接口
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);
}
}
此时,消费端的线程被唤醒,就拿到结果了。异步完美转为同步了。