dubbo源码分析-客户端DubboInvoker调用服务端体会Netty的非阻塞IO使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/matthew_zhang/article/details/70943109

本文会介绍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的阻塞用法。

猜你喜欢

转载自blog.csdn.net/matthew_zhang/article/details/70943109