本文会介绍Dubbo客户端DubboInvoker调用服务端时候异步同步调用,借此理解Netty的阻塞非阻塞用法
先来看官网的描述:

上面的描述对应实现在DubboInvoker类。
DubboInvoker doInvoke(final Invocation invocation)方法:
....
try {
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY,Constants.DEFAULT_TIMEOUT);
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();
}
} catch (TimeoutException e) {
throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
}
....
关键参数说明:
1)isOneway: oneway调用,只发送请求,不接收返回结果 //RpcContext中如此描述。
2)isAsync: 异步调用
3)else: 本地调用(同步调用),线程等待返回结果
isOneway的用法 (只发送,不返回结果,发送时候阻塞保证发送成功)
对于1) currentClient.send(inv, isSent) 中的isSent参数作用:-
设置是否等待消息发出:(异步总是不等待返回结果)
● sent=”true” 等待消息发出,消息发送失败将抛出异常。
● sent=”false” 不等待消息发出,将消息放入IO队列,即刻返回。
注意:
首先,是否等待消息发出与是否接收返回结果是两回事。 例如, 该isOneway的做法是不接收返回结果,但是如果isSent=true则等待消息发出;
其次,对与oneway调用,NettyChannel 中send方法里的 sent = true
NettyChannel send(Object message, boolean sent)方法:
....
try {
ChannelFuture future = channel.write(message);//发送时候不阻塞
if (sent) {
timeout = getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
success = future.await(timeout); // 线程运行此会等待消息发出,同步
}
....
(HeaderExchangeChannel send –》到 NettyChannel send)
isAsync的用法 (调用发送不阻塞,发送时候不阻塞,获取返回结果时候阻塞, 获取返回结果的位置灵活,结果放在RpcContext中)
对于2)返回结果包装在Future中,并且Future放入到RpcContext中,RpcContext是线程安全,因为RpcContext里用ThreadLocal保存该RpcContext的具体内容
ResponseFuture future = currentClient.request(inv, timeout) ; //调用发送不阻塞
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
以上两个方法都是非阻塞的,也就是程序没有任何等待直接执行。注意,对与Async调用,NettyChannel 中send方法里的 sent=false
什么时候取回该结果? 按官网描述可知,用RpcContext.getFuture.get()则可以获取。但是当使用这个方法调用位置是阻塞的,因为要等待结果。
(HeaderExchangeChannel request–》到 NettyChannel send)
本地调用, (调用发送不阻塞,发送时候不阻塞,获取返回结果时候阻塞,获取返回结果的位置不灵活)
对于3)本地调用(同步调用),线程等待返回结果
RpcContext.getContext().setFuture(null);
return (Result) currentClient.request(inv, timeout).get();
注意,get()方法会阻塞,线程运行到这里等待返回发送结果。
注意,
首先,对于本地调用(同步调用),NettyChannel 中send方法里的 sent=false。
其次,重点理解!! currentClient.request(inv, timeout)里执行的是Netty里的非阻塞调用ChannelFuture future = channel.write(message); 而程序并没有在netty的调用层次上阻塞(估计目的是让netty高并发处理),但是get()会在主程序运行位置阻塞直到返回结果,该实现是dubbo的DefaultFuture自己实现,与Netty不相关。可以看出,为了等待返回结果,该阻塞是在业务线程里控制,并不涉及Netty的阻塞用法。