重試異步函數

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

重試的語義

Cloud Functions 保證為事件源發出的每個事件至少執行一次事件驅動函數。但是,默認情況下,如果函數調用因錯誤而終止,則不會再次調用該函數,並且該事件將被丟棄。當您對事件驅動函數啟用重試時,Cloud Functions 將重試失敗的函數調用,直到它成功完成或重試窗口到期(默認為 7 天后)。

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

為什麼事件驅動的功能無法完成

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

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

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

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

啟用和禁用重試

使用 GCP 控制台

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

  1. 轉到 Cloud Platform Console 中的Cloud Functions 概覽頁面

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

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

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

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

  6. 選中或取消選中標有Retry on failure的框。

在功能代碼中

借助 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與 Promises 一起使用

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

這是您應該執行的操作的示例:

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

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

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

  • 許多外部 API(例如 Stripe)允許您提供冪等鍵作為參數。如果您使用這樣的 API,您應該使用事件 ID 作為冪等鍵。
  • 冪等性適用於至少一次交付,因為它可以安全地重試。因此,編寫可靠代碼的一般最佳實踐是將冪等性與重試結合起來。
  • 確保您的代碼在內部是冪等的。例如:
    • 確保突變可以多次發生而不改變結果。
    • 在改變狀態之前查詢事務中的數據庫狀態。
    • 確保所有副作用本身都是冪等的。
  • 在函數外部實施事務檢查,獨立於代碼。例如,在某處保持狀態,記錄給定事件 ID 已被處理。
  • 處理帶外重複的函數調用。例如,有一個單獨的清理過程,在重複的函數調用之後進行清理。