重試異步函數

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

重試的語義

Cloud Functions 保證事件源發出的每個事件至少執行一次事件驅動函數。但是,默認情況下,如果函數調用因錯誤而終止,則不會再次調用該函數,並且事件將被刪除。當您對事件驅動函數啟用重試時,Cloud Functions 將重試失敗的函數調用,直到成功完成或重試窗口到期。對於第二代功能,重試窗口將在 24 小時後過期。對於第一代函數,它會在 7 天后過期。新創建的事件驅動函數將使用指數退避策略重試,最小退避時間為 10 秒,最大退避時間為 600 秒(即 10 分鐘)。此策略將在您首次部署新功能時應用到它們。它不會追溯應用於本發行說明中描述的更改生效之前首次部署的現有功能,即使您重新部署這些功能也是如此。

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

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

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

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

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

在上述任何一種情況下,該函數都會默認停止執行,並且該事件將被丟棄。如果您想在發生錯誤時重試該功能,可以通過設置“失敗時重試”屬性來更改默認重試策略。這會導致事件重複重試最多幾天,直到函數成功完成。

啟用和禁用重試

使用 GCP 控制台

您可以在 GCP Console 中啟用或禁用重試,如下所示:

  1. 進入雲平台控制台的雲函數概覽頁面

  2. 單擊創建函數。或者,單擊現有函數轉到其詳細信息頁面,然後單擊編輯

  3. 填寫您的函數的必填字段。

  4. 確保“觸發器”字段設置為基於事件的觸發器類型,例如 Cloud Pub/Sub 或 Cloud Storage。

  5. 單擊“更多”展開高級設置。

  6. 選中或取消選中標記為“失敗時重試”的框。

在函數代碼中

借助 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 文檔