dubbo入门之异步调用

dubbo默认使用同步的方式调用。但在有些特殊的场景下,我们可能希望异步调用dubbo接口,从而避免不必要的等待时间,这时候我们就需要用到异步。那么dubbo的异步是如何实现的呢?下面就来看看这个问题
异步方法配置:

<dubbo:reference cache="lru" id="demoService" interface="com.ping.chen.dubbo.service.DemoService" timeout="5000">
    <dubbo:method name="sayHello" async="true"/>
</dubbo:reference>

底层的异步处理实现在DubboInvoker的doInvoke方法中,源码如下:

protected Result doInvoke(final Invocation invocation) throws Throwable {
    RpcInvocation inv = (RpcInvocation) invocation;
    忽略Attachment设置。。。
    try {
        // 是否异步执行
        boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
        boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);//是否单向执行
        int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);//接口超时时间,默认1秒
        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) {
        //异常处理。。。
    } 
}

可以看到,如果异步执行,会直接返回一个空的RpcResult,然后用户如果需要异步执行结果,可以从RpcContext中的Future中去获取,直接用RpcContext.getContext().getFuture().get();就可以获取到执行结果。那么RpcContext是如何保证当前线程可以拿到执行结果呢?答案是ThreadLocal。我们来看看RpcContext源码如下:

public class RpcContext {

	private static final ThreadLocal<RpcContext> LOCAL = new ThreadLocal<RpcContext>() {
		@Override
		protected RpcContext initialValue() {
			return new RpcContext();
		}
	};
	public static RpcContext getContext() {
		return LOCAL.get();
	}
            。。。。。。
}

可以看到RpcContext 内部是使用ThreadLocal来实现的。

异步可能遇到的坑

上面这种配置有一个坑,可能你会像下面这样使用异步:

@RestController
public class DemoController {
	@Reference(timeout = 5000)
	DemoProvider demoProvider;

	@RequestMapping("/demo")
	public void demo() throws ExecutionException, InterruptedException {
		demoProvider.sayHello("world");
		System.out.println(">>>>>>>>>>" + RpcContext.getContext().getFuture().get());
	}
}

然后请求demo接口的时候,很不幸,你将会收到一个NullPointException,这因为我们只在消费者端配置了异步执行,但服务端执行的时候是同步的,所以我们从RpcContext中获取到的Future是空的,正确的异步配置应该是:
直接去掉消费者端的

<dubbo:method name="sayHello" async="true"/>

配置,然后在服务提供者端做如下配置:

<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.ping.chen.dubbo.provider.DemoProvider" ref="demoProvider" >
    <dubbo:method name="sayHello" async="true"/>
</dubbo:service>

如此,便可以实现异步了

posted @ 2019-03-18 20:28  SheaChen  阅读(2585)  评论(0编辑  收藏  举报