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

Posted xkzhangsanx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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");

 

以上是关于dubbo同步调用异步调用和是否返回结果源码分析和实例的主要内容,如果未能解决你的问题,请参考以下文章

RPC理论以及Dubbo的使用介绍

Dubbo异步转同步

Dubbo实现源码分析

如何简单粗暴的搞定dubbo调用模块

手写dubbo-5rpc调用异步转同步

阿里巴巴Dubbo实现的源码分析