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