處理長時間執行的作業

有時,API 可能會公開需要大量時間才能完成的方法。您不必在工作執行時封鎖,可以傳回 Promise,讓使用者檢查狀態。

Google Cloud Rust 用戶端程式庫提供輔助工具,可處理這些長時間執行的作業 (LRO)。本指南說明如何啟動 LRO 並等待完成。

必要條件

本指南使用 Cloud Storage 服務,讓程式碼片段更具體。這些概念適用於使用 LRO 的任何其他服務。

按照本指南操作之前,請先完成下列事項:

如需 Rust 程式庫的完整設定說明,請參閱「設定開發環境」。

依附元件

Cargo.toml 檔案中宣告 Google Cloud 依附元件:

cargo add google-cloud-storage google-cloud-lro google-cloud-longrunning

此外,你還需要使用幾項 tokio 功能:

cargo add tokio --features full,macros

啟動長時間執行的作業

本範例使用「重新命名資料夾」。如果資料夾很大,這項作業可能需要很長時間,但如果資料夾較小,速度相對較快。

如要啟動長時間執行的作業,您必須初始化用戶端並發出 RPC。

首先,請新增 use 宣告,避免套件名稱過長:

use anyhow::anyhow;
use google_cloud_longrunning as longrunning;
use google_cloud_storage::client::StorageControl;

接著,建立用戶端:

let client = StorageControl::builder().build().await?;

在 Rust 用戶端程式庫中,每個要求都以傳回要求建構工具的方法表示。在用戶端呼叫方法,建立要求建構工具:

let operation = client
    .rename_folder()
    .set_name(format!("projects/_/buckets/{bucket}/folders/{folder}"))
    .set_destination_folder_id(dest)

範例函式會將 bucket 和資料夾名稱做為引數:

pub async fn manual(bucket: &str, folder: &str, dest: &str) -> anyhow::Result<()> {

提出要求並等待系統傳回 Operation。這個 Operation 會做為長期要求的結果承諾:

    let operation =
        // ...
        .send()
        .await?;

這項要求會在背景啟動作業。等待作業完成,判斷是否成功。

自動輪詢長時間執行的作業

如要設定自動輪詢,請使用 Poller (而非 .send().wait) 啟動長時間執行的作業,如下所示:

.poller()
.until_done()
.await?;

首先,使用 use 宣告,在範圍內導入 Poller 特徵:

use google_cloud_lro::Poller;

接著,請如常初始化用戶端並準備要求:

let response = client
    .rename_folder()
    .set_name(format!("projects/_/buckets/{bucket}/folders/{folder}"))
    .set_destination_folder_id(dest)

輪詢作業,直到作業完成並列印結果為止:

    .poller()
    .until_done()
    .await?;

println!("LRO completed, response={response:?}");

輪詢長時間執行的作業,並取得中間結果

.until_done() 方法很方便,但會省略長期執行的作業的部分進度報告。如果應用程式需要這項資訊,請直接使用輪詢器:

    let mut poller = client
        .rename_folder()
        /* more stuff */
        .poller();

然後在迴圈中使用輪詢器:

while let Some(p) = poller.poll().await {
    match p {
        PollingResult::Completed(r) => {
            println!("LRO completed, response={r:?}");
        }
        PollingResult::InProgress(m) => {
            println!("LRO in progress, metadata={m:?}");
        }
        PollingResult::PollingError(e) => {
            println!("Transient error polling the LRO: {e}");
        }
    }
    tokio::time::sleep(std::time::Duration::from_millis(500)).await;
}

這個迴圈會明確等待,然後再輪詢。輪詢週期取決於特定作業及其酬載。請參閱服務說明文件或實驗資料,判斷合適的值。

手動輪詢長時間執行的作業

我們建議採用自動輪詢方法,但也可以手動輪詢長時間執行的作業。詳情請參閱「Operation」訊息參考說明文件。

使用用戶端啟動長時間執行的作業:

    let mut operation = client
        .rename_folder()
        /* more stuff */
        .send()
        .await?;

啟動輪詢迴圈,並使用 done 欄位檢查作業是否完成:

let response: anyhow::Result<Folder> = loop {
    if operation.done {

作業完成時,通常會包含結果。結果欄位為選填,因為服務可能會傳回 done (true),但沒有結果。舉例來說,成功刪除作業不會傳回任何值。在本範例中,Cloud Storage 服務一律會傳回值:

match &operation.result {
    None => {
        break Err(anyhow!("missing result for finished operation"));
    }

已啟動的作業可能無法順利完成。結果可能是錯誤或有效回應。請先檢查是否有錯誤:

Some(r) => {
    break match r {
        longrunning::model::operation::Result::Error(s) => {
            Err(anyhow!("operation completed with error {s:?}"))
        }

錯誤類型為 Status 訊息類型。這項操作「不會」實作標準 Error 特徵。使用 Error::service 手動轉換為有效錯誤。

如果結果成功,請擷取回應類型。在 LRO 方法說明文件或服務 API 說明文件中,找出這類項目:

longrunning::model::operation::Result::Response(any) => {
    let response = any.to_msg::<Folder>()?;
    Ok(response)
}

請注意,如果類型與服務傳送的類型不符,可能無法擷取值。

Google Cloud 類型日後可能會新增欄位和分支。Rust 的用戶端程式庫會將所有 struct 和 enum 標示為 #[non_exhaustive]。 Google Cloud處理這個情況:

_ => Err(anyhow!("unexpected result branch {r:?}")),

如果作業尚未完成,則可能包含中繼資料。部分服務會提供要求相關的初步資訊,其他服務則會提供部分進度報告。您可以擷取及回報下列中繼資料:

if let Some(any) = &operation.metadata {
    let metadata = any.to_msg::<RenameFolderMetadata>()?;
    println!("LRO in progress, metadata={metadata:?}");
}

請稍後再輪詢。請考慮使用部分指數輪詢調整輪詢間隔。這個範例每 500 毫秒輪詢一次:

tokio::time::sleep(std::time::Duration::from_millis(500)).await;

查詢作業狀態:

if let Ok(attempt) = client
    .get_operation()
    .set_name(&operation.name)
    .send()
    .await
{
    operation = attempt;
}

為求簡單明瞭,本範例會忽略所有錯誤。在應用程式中,您可以將部分錯誤視為無法復原,並限制輪詢嘗試次數。

後續步驟

  • GitHub 上查看範例的原始碼。