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();
try {
String hello = future.get(1, TimeUnit.SECONDS);
System.out.println(hello);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
(6)不需要返回结果,配置dubbo-demo-consumer.xml 中return="false",同步异步的调用方式一样的,很简单,只需要调用一下就可以继续其他操作,下面是异步的例子。
AsyncDemoService asyncDemoService = (AsyncDemoService) context.getBean("asyncDemoService");
asyncDemoService.sayHello("world");