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>

如此,便可以實現異步了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM