重試異步函數

本文檔介紹如何請求非同步(非 HTTPS)後台函數在失敗時重試。

重試的語義

Cloud Functions 確保事件來源發出的每個事件至少執行一次事件驅動函數。預設情況下,如果函數呼叫因錯誤而終止,則不會再次呼叫函數並且事件將被刪除。當您對事件驅動函數啟用重試時,Cloud Functions 會重試失敗的函數調用,直到成功完成或重試視窗到期。

對於第二代功能,此重試視窗將在 24 小時後過期。對於第一代函數,它會在 7 天後過期。 Cloud Functions 使用指數退避策略重試新建立的事件驅動函數,退避時間增加在 10 到 600 秒之間。此策略將在您首次部署新功能時套用到它們。它不會追溯應用於本發行說明中所述的變更生效之前首次部署的現有功能,即使您重新部署這些功能也是如此。

當某個函數未啟用重試(預設)時,該函數會始終報告其執行成功,且其日誌中可能會出現200 OK回應碼。即使函數遇到錯誤也會發生這種情況。為了清楚地表明您的函數何時遇到錯誤,請務必適當地報告錯誤

為什麼事件驅動函數無法完成

在極少數情況下,函數可能會由於內部錯誤而提前退出,並且預設情況下該函數可能會也可能不會自動重試。

更常見的是,事件驅動函數可能由於函數程式碼本身引發的錯誤而無法成功完成。可能發生這種情況的原因包括:

  • 該函數包含錯誤並且運行時拋出異常。
  • 此函數無法到達服務端點,或在嘗試到達服務端點時逾時。
  • 此函數故意拋出異常(例如,當參數驗證失敗時)。
  • Node.js 函數傳回被拒絕的 Promise,或將非null值傳遞給回呼。

在上述任何一種情況下,該函數都會預設停止執行,並且該事件將被丟棄。若要在發生錯誤時重試該功能,可以透過設定「失敗時重試」屬性來變更預設重試策略。這會導致事件被重複重試,直到函數成功完成或重試逾時到期。

啟用或停用重試

從 GCP Console 配置重試

如果您要建立一個新函數:

  1. 「建立函數」畫面中,選擇「新增觸發器」並選擇充當函數觸發器的事件類型。
  2. Eventarc 觸發器窗格中,選取失敗時重試複選框以啟用重試。

如果您要更新現有函數:

  1. 雲端函數概述頁面中,按一下要更新的函數的名稱以開啟其函數詳細資訊螢幕,然後從功能表列中選擇編輯以顯示HTTPSEventarc觸發器窗格。
  2. Eventarc 觸發器窗格中,按一下圖示以編輯觸發器的設定。
  3. Eventarc 觸發器窗格中,選取或清除失敗時重試複選框以啟用或停用重試。

從您的功能代碼配置重試

使用 Cloud Functions for Firebase,您可以在函數程式碼中啟用重試。對後台函數執行此操作,例如functions.foo.onBar(myHandler); ,使用runWith並配置失敗策略:

functions.runWith({failurePolicy: true}).foo.onBar(myHandler);

如圖所示設定true將函數配置為在失敗時重試。

最佳實踐

本節介紹使用重試的最佳實務。

使用重試來處理暫時性錯誤

由於您的函數會不斷重試直到成功執行,因此應在啟用重試之前透過測試從程式碼中消除諸如錯誤之類的永久性錯誤。重試最適合處理間歇性/暫時性故障,這些故障在重試後很可能得到解決,例如不穩定的服務端點或逾時。

設定結束條件以避免無限重試循環

最佳實踐是在使用重試時保護您的函數免受連續循環的影響。您可以透過在函數開始處理之前包含明確定義的結束條件來實現此目的。請注意,此技術僅在您的函數成功啟動並且能夠評估結束條件時才有效。

一個簡單而有效的方法是丟棄時間戳早於特定時間的事件。當故障持續存在或持續時間比預期更長時,這有助於避免過度執行。

例如,以下程式碼片段會丟棄所有超過 10 秒的事件:

const eventAgeMs = Date.now() - Date.parse(event.timestamp);
const eventMaxAgeMs = 10000;
if (eventAgeMs > eventMaxAgeMs) {
  console.log(`Dropping event ${event} with age[ms]: ${eventAgeMs}`);
  callback();
  return;
}

catch與 Promise 一起使用

如果您的函數啟用了重試,則任何未處理的錯誤都會觸發重試。確保您的程式碼捕獲任何不應導致重試的錯誤。

以下是您應該執行的操作的範例:

return doFooAsync().catch((err) => {
    if (isFatal(err)) {
        console.error(`Fatal error ${err}`);
    }
    return Promise.reject(err);
});

使可重試的事件驅動函數具有冪等性

可重試的事件驅動函數必須是冪等的。以下是使此類函數冪等的一些一般準則:

  • 許多外部 API(例如 Stripe)允許您提供冪等性金鑰作為參數。如果您使用此類 API,則應使用事件 ID 作為冪等鍵。
  • 冪等性對於至少一次傳遞效果很好,因為它可以安全地重試。因此,編寫可靠程式碼的一般最佳實踐是將冪等性與重試結合。
  • 確保您的程式碼在內部是冪等的。例如:
    • 確保突變可以多次發生而不改變結果。
    • 在改變狀態之前查詢事務中的資料庫狀態。
    • 確保所有副作用本身都是冪等的。
  • 在函數外部實作事務檢查,獨立於程式碼。例如,在某處保留狀態,記錄給定的事件 ID 已被處理。
  • 處理帶外重複的函數呼叫。例如,有一個單獨的清理進程,可以在重複的函數呼叫後進行清理。

配置重試策略

根據您的雲端功能的需求,您可能希望直接配置重試策略。這將允許您設定以下任意組合:

  • 將重試視窗從 7 天縮短至 10 分鐘。
  • 更改指數退避重試策略的最小和最大退避時間。
  • 將重試策略變更為立即重試。
  • 配置死信主題
  • 設定發送嘗試的最大和最小次數。

配置重試策略:

  1. 編寫一個 HTTP 函數。
  2. 使用 Pub/Sub API 建立 Pub/Sub 訂閱,並將函數的 URL 指定為目標。

有關直接配置Pub/Sub 的更多信息,請參閱有關處理失敗的 Pub/Sub 文件