非同步呼叫

在 Dubbo 中發起非同步呼叫

功能說明

背景

從 2.7.0 版本開始,Dubbo 的所有非同步程式設計介面都基於 CompletableFuture

基於 NIO 的非阻塞式平行呼叫實現,客戶端無需啟動多執行緒即可完成對多個遠端服務的平行呼叫,多執行緒的開銷相對較小。

/user-guide/images/future.jpg

參考使用案例

https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-async

使用場景

將使用者請求內容傳送到目標請求。當目標請求遇到高流量或需要長時間處理時,非同步呼叫功能將允許立即將回應返回給使用者,同時目標請求在背景繼續處理請求。當目標請求返回結果時,內容將會顯示給使用者。

使用方法

使用 CompletableFuture 簽名的介面

對於需要服務提供者預先定義 CompletableFuture 簽名的服務,介面定義規範如下

提供者端的非同步執行將阻塞的業務從 Dubbo 的內部執行緒池切換到業務自定義的執行緒,避免過度佔用 Dubbo 執行緒池,並有助於避免不同服務之間的相互影響。非同步執行等同於節省資源或提高 RPC 回應效能,因為如果業務執行需要被阻塞,總會有一個執行緒負責執行。

提供者端非同步執行和消費者端非同步呼叫彼此獨立,兩種設定可以任意正交組合

  • 消費者同步 - 提供者同步
  • 消費者非同步 - 提供者同步
  • 消費者同步 - 提供者非同步
  • 消費者非同步 - 提供者非同步

定義 CompletableFuture 簽名的介面

服務介面定義

public interface AsyncService {
    CompletableFuture<String> sayHello(String name);
}

服務實現

public class AsyncServiceImpl implements AsyncService {
    @Override
    public CompletableFuture<String> sayHello(String name) {
        return CompletableFuture. supplyAsync(() -> {
            System.out.println(name);
            try {
                Thread. sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "async response from provider.";
        });
    }
}

透過 return CompletableFuture.supplyAsync(),業務執行已從 Dubbo 執行緒切換到業務執行緒,避免阻塞 Dubbo 執行緒池。

使用 AsyncContext

Dubbo 提供了一個類似 Servlet 3.0 的非同步介面 AsyncContext,也可以在 Provider 端實現非同步執行,無需 CompletableFuture 簽章介面。

服務介面定義

public interface AsyncService {
    String sayHello(String name);
}

服務暴露,與普通服務完全相同

<bean id="asyncService" class="org.apache.dubbo.samples.governance.impl.AsyncServiceImpl"/>
<dubbo:service interface="org.apache.dubbo.samples.governance.api.AsyncService" ref="asyncService"/>

服務實現

public class AsyncServiceImpl implements AsyncService {
    public String sayHello(String name) {
        final AsyncContext asyncContext = RpcContext. startAsync();
        new Thread(() -> {
            // If you want to use the context, it must be executed in the first sentence
            asyncContext.signalContextSwitch();
            try {
                Thread. sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // write back the response
            asyncContext.write("Hello " + name + ", response from provider.");
        }).start();
        return null;
    }
}

注意介面的返回類型是 CompletableFuture<String>

XML 引用服務

<dubbo:reference id="asyncService" timeout="10000" interface="com.alibaba.dubbo.samples.async.api.AsyncService"/>

呼叫遠端服務

// The call directly returns CompletableFuture
CompletableFuture<String> future = asyncService.sayHello("async call request");
// add callback
future. whenComplete((v, t) -> {
    if (t != null) {
        t. printStackTrace();
    } else {
        System.out.println("Response: " + v);
    }
});
// earlier than the result output
System.out.println("Executed before response return.");

使用 RpcContext

在 consumer.xml 中配置

<dubbo:reference id="asyncService" interface="org.apache.dubbo.samples.governance.api.AsyncService">
      <dubbo:method name="sayHello" async="true" />
</dubbo:reference>

呼叫程式碼

// this call returns null immediately
asyncService.sayHello("world");
// Get the Future reference of the call, when the result is returned, it will be notified and set to this Future
CompletableFuture<String> helloFuture = RpcContext.getServiceContext().getCompletableFuture();
// add callback for Future
helloFuture. whenComplete((retValue, exception) -> {
    if (exception == null) {
        System.out.println(retValue);
    } else {
        exception. printStackTrace();
    }
});

或者,也可以透過呼叫以下方法來非同步執行

CompletableFuture<String> future = RpcContext.getServiceContext().asyncCall(
    () -> {
        asyncService.sayHello("oneway call request1");
    }
);

future. get();

**非同步方式永遠不會等待返回**,您也可以設定是否等待訊息發送

  • sent="true" 等待訊息發送,如果訊息發送失敗,則會拋出異常。
  • sent="false" 不等待訊息發送,將訊息放入 IO 佇列,立即返回。
<dubbo:method name="findFoo" async="true" sent="true" />

如果您只想非同步並且完全忽略返回值,可以設定 return="false" 以減少建立和管理 Future 物件的成本

<dubbo:method name="findFoo" async="true" return="false" />

最後修改日期:2023 年 1 月 2 日:增強英文文件 (#1798) (95a9f4f6c1c)