dubbo入門之異步調用

dubbo默認使用同步的方式調用。但在有些特殊的場景下,咱們可能但願異步調用dubbo接口,從而避免沒必要要的等待時間,這時候咱們就須要用到異步。那麼dubbo的異步是如何實現的呢?下面就來看看這個問題
異步方法配置:app

<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源碼以下:async

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來實現的。ide

異步可能遇到的坑

上面這種配置有一個坑,可能你會像下面這樣使用異步:線程

@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是空的,正確的異步配置應該是:
直接去掉消費者端的code

<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>

如此,即可以實現異步了get

相關文章
相關標籤/搜索