上一篇我们提及到
DubboInvoker#doInvoker中有这么一段代码
if (isOneway) {
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent);
RpcContext.getContext().setFuture(null);
return new RpcResult();
} else if (isAsync) {
ResponseFuture future = currentClient.request(inv, timeout) ;
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
return new RpcResult();
} else {
RpcContext.getContext().setFuture(null);
return (Result) currentClient.request(inv, timeout).get();
}
这里涉及到dubbo的三种通信模式第二种是有返回值异步通信,配置方式如下
<dubbo:method name="sayHello" async="true" return="true"/>
产生的结果就是立刻返回,但是可以从RpcContext中获取到一个Future,然后从Future中获取到值
第一个if分支是无返回值的异步通信,配置方式是
<dubbo:method name="sayHello" async="true" return="false"/>
产生的结果就是立刻返回,并且RpcContext中无法获取Future
最后一种是同步调用
但实际上dubbo采用的是netty的异步通信方法,这里体现为同步是因为dubbo做了一些处理
currentClient.request(inv, timeout)得到的结果是一个DefaultFuture,原因是在
HeaderExchangerChannel#request中封装了
DefaultFuture future = new DefaultFuture(channel, req, timeout); try{ channel.send(req); }catch (RemotingException e) { future.cancel(); throw e; } return future;
进入DefaultFuture的get方法
public Object get(int timeout) throws RemotingException { if (timeout <= 0) { timeout = Constants.DEFAULT_TIMEOUT; } if (! isDone()) { long start = System.currentTimeMillis(); lock.lock(); try { while (! isDone()) { done.await(timeout, TimeUnit.MILLISECONDS); if (isDone() || System.currentTimeMillis() - start > timeout) { break; } } } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } if (! isDone()) { throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false)); } } return returnFromResponse(); }
关键在于
while (! isDone()) { done.await(timeout, TimeUnit.MILLISECONDS); if (isDone() || System.currentTimeMillis() - start > timeout) { break; } }
done是一个conditon,熟悉多线程的都知道这会阻碍线程
而isDone方法是
return response != null;
也就是说repsonse为null就会一直阻塞
再看上一节最后的
DefaultFuture.received(channel, response);
future.doReceived(response);
private void doReceived(Response res) { lock.lock(); try { response = res; if (done != null) { done.signal(); } } finally { lock.unlock(); } if (callback != null) { invokeCallback(callback); } }
done.signal();这句话是关键,这里会唤醒刚刚阻塞的线程。。
这里就解决了异步变同步的问题
还有一个问题,由于socket通信是全双工通信模式,
附一张图说明全半双工
那么问题来了,dubbo是如何保证request和response一一匹配的呢
回到DefaultFuture的构造函数
public DefaultFuture(Channel channel, Request request, int timeout){ this.channel = channel; this.request = request; this.id = request.getId(); this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT); // put into waiting map. FUTURES.put(id, this); CHANNELS.put(id, channel); }
关注最后面两个Map
这里会把Futrure和一个id放在一个map中,
received方法中有一个
DefaultFuture future = FUTURES.remove(response.getId());
从Map中获取这个id得对应的DefaultFutrue并删除。。
异步变同步的方式分析完毕
---------------------------------------------------------------
在前面几篇文章我们分析源码过程中发现有很多invoker
invoker是dubbo最重要的一个组件
public interface Invoker<T> extends Node { Class<T> getInterface(); Result invoke(Invocation invocation) throws RpcException; }
这是invoker接口,里面最重要的invoke方法,Invocation是入参 Result是返回值
dubbo中一共有三种类型的invoker
本地执行invoker
server端:InjvmExporter.getInvoker,通过反射来执行具体方法
远程执行invoker
client端:DubboInvoker
server端:AbstractProxyInvoker
多个远程通信伪装中一个invoker
client端:AbstractClusterInvoker
server端:AbstractProxyInvoker
本节完