dubbo同步调用、异步调用和是否返回结果源码分析和实例

0. dubbo同步调用、异步调用和是否返回结果配置

(1)dubbo认为同步调用,并且有返回结果。

(2)dubbo异步调用配置,设置 async="true",异步调用可以提高效率。

(3)dubbo认是有返回结果,不需要返回,可以设置return="false",不需要返回值,可以减少等待结果时间。

 

1. 源码分析(dubbo版本:2.6.0)

dubbo自身底层调用是使用netty异步实现的,认同步调用返回结果,是通过获取ResponseFuture,然后使用reentrantlock的await使当前线程等待结果,设置返回的。下面是部分核心代码部分:

com.alibaba.dubbo.rpc.protocol.dubbo.dubboInvoker.doInvoke(Invocation)

 

    @Override
    protected Result doInvoke(final Invocation invocation) throws Throwable {
        RpcInvocation inv = (RpcInvocation) invocation;
        final String methodName = RpcUtils.getmethodName(invocation);
        inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
        inv.setAttachment(Constants.VERSION_KEY, version);

        ExchangeClient currentClient;
        if (clients.length == 1) {
            currentClient = clients[0];
        } else {
            currentClient = clients[index.getAndIncrement() % clients.length];
        }
        try {
            // 获取是否异步配置async
            boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
            // 获取是否需要返回结果配置return
            boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
            // 获取超时配置timeout
            int timeout = getUrl().getmethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
            if (isOneway) {
                // 不管是否异步,只要不需要返回结果,直接异步调用,设置结果为null
                boolean isSent = getUrl().getmethodParameter(methodName, Constants.SENT_KEY, false);
                currentClient.send(inv, isSent);
                RpcContext.getContext().setFuture(null);
                return new RpcResult();
            } else if (isAsync) {
                // 如果异步,并且需要返回结果,调用后设置结果future
                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);
        } catch (RemotingException e) {
            throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getmethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

2. 实例

这里使用dubbo源码自带的例子

(1)在dubbo-demo-api中定义异步服务

package com.alibaba.dubbo.demo;

public interface AsyncDemoService {
    String sayHello(String name);
}

(2)在dubbo-demo-provider中实现

package com.alibaba.dubbo.demo.provider;

import java.text.SimpleDateFormat;
import java.util.Date;

import com.alibaba.dubbo.demo.AsyncDemoService;
import com.alibaba.dubbo.rpc.RpcContext;

public class AsyncDemoServiceImpl implements AsyncDemoService{

    @Override
    public String sayHello(String name) {
        System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
        return "Hello " + name + ", response form provider: " + RpcContext.getContext().getLocalAddress();
    }

}

(3)在dubbo-demo-provider.xml中配置服务信息

    <bean id="asyncDemoService" class="com.alibaba.dubbo.demo.provider.AsyncDemoServiceImpl"/>


    <dubbo:service interface="com.alibaba.dubbo.demo.AsyncDemoService" ref="asyncDemoService"/>

(4)在dubbo-demo-consumer.xml中配置调用服务信息,设置为异步调用async="true" 

    <dubbo:reference id="asyncDemoService" check="false" interface="com.alibaba.dubbo.demo.AsyncDemoService">
        <dubbo:method name="sayHello" async="true"/>
    </dubbo:reference>

(5)在dubbo-demo-consumer的Consumer类中增加调用

        AsyncDemoService asyncDemoService = (AsyncDemoService) context.getBean("asyncDemoService");
        asyncDemoService.sayHello("world");
        // 通过 1.源码分析 中可以知道异步返回结果放到了RpcContext.getContext()中
        Future<String> future =  RpcContext.getContext().getFuture();
        while(!future.isDone()){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printstacktrace();
            }
        }
        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printstacktrace();
        } catch (ExecutionException e) {
            e.printstacktrace();
        }

(6)不需要返回结果,配置dubbo-demo-consumer.xml 中return="false",同步异步的调用方式一样的,很简单,只需要调用一下就可以继续其他操作,下面是异步的例子。

        AsyncDemoService asyncDemoService = (AsyncDemoService) context.getBean("asyncDemoService");
        asyncDemoService.sayHello("world");

 

相关文章

在网络请求时,总会有各种异常情况出现,我们需要提前处理这...
作者:宇曾背景软件技术的发展历史,从单体的应用,逐渐演进...
hello,大家好呀,我是小楼。最近一个技术群有同学at我,问我...
 一个软件开发人员,工作到了一定的年限(一般是3、4年左右...
当一个服务调用另一个远程服务出现错误时的外观Dubbo提供了多...
最近在看阿里开源RPC框架Dubbo的源码,顺带梳理了一下其中用...