使用 Cloud Tasks 將函數排入隊列

任務隊列函數利用 Google Cloud Tasks幫助您的應用在主應用流程之外異步運行耗時、資源密集或帶寬受限的任務。

例如,假設您想要創建大量圖像文件的備份,這些圖像文件當前託管在具有速率限制的 API 上。為了成為該 API 的負責任消費者,您需要尊重他們的速率限制。此外,由於超時和內存限制,這種長時間運行的作業可能容易失敗。

為了減輕這種複雜性,您可以編寫一個任務隊列函數來設置基本任務選項,如scheduleTimedispatchDeadline ,然後將該函數交給 Cloud Tasks 中的隊列。 Cloud Tasks 環境專門設計用於確保對此類操作進行有效的擁塞控制和重試策略。

Firebase Cloud Functions for Firebase v3.20.1 及更高版本的 Firebase SDK 與 Firebase Admin SDK v10.2.0 及更高版本互操作以支持任務隊列功能。

在 Firebase 中使用任務隊列功能可能會產生 Cloud Tasks 處理費用。有關更多信息,請參閱Cloud Tasks 定價

創建任務隊列函數

要使用任務隊列功能,請遵循以下工作流程:

  1. 使用 Firebase SDK for Cloud Functions 編寫任務隊列函數。
  2. 使用 Firebase 本地模擬器套件測試您的功能。
  3. 使用 Firebase CLI 部署您的函數。首次部署任務隊列功能時,CLI 將在 Cloud Tasks 中創建一個任務隊列,並在您的源代碼中指定選項(速率限制和重試)。
  4. 將任務添加到新創建的任務隊列中,並在需要時傳遞參數以設置執行計劃。您可以通過使用 Admin SDK 編寫代碼並將其部署到 Cloud Functions for Firebase 來實現此目的。

編寫任務隊列函數

使用onTaskDispatched開始編寫任務隊列函數。編寫任務隊列函數的一個重要部分是設置每個隊列的重試和限速配置。此頁面中的代碼示例基於一個應用程序,該應用程序設置了一項服務,該服務備份來自 NASA 的每日天文圖片的所有圖像:

任務隊列配置

任務隊列函數帶有一組強大的配置設置,可以精確控制任務隊列的速率限制和重試行為:

exports.backupapod = onTaskDispatched(
    {
      retryConfig: {
        maxAttempts: 5,
        minBackoffSeconds: 60,
      },
      rateLimits: {
        maxConcurrentDispatches: 6,
      },
    }, async (req) => {
  • retryConfig.maxAttempts=5 :任務隊列中的每個任務自動重試最多 5 次。這有助於緩解暫時性錯誤,例如網絡錯誤或依賴的外部服務的臨時服務中斷。
  • retryConfig.minBackoffSeconds=60 :每個任務在每次嘗試後至少重試 60 秒。這在每次嘗試之間提供了很大的緩衝,因此我們不會急於過快地耗盡 5 次重試嘗試。
  • rateLimits.maxConcurrentDispatch=6 :在給定時間最多分派 6 個任務。這有助於確保對底層函數的請求源源不斷,並有助於減少活動實例和冷啟動的數量。

使用 Firebase Local Emulator Suite 測試任務隊列功能

Firebase Local Emulator Suite 中的任務隊列函數公開為簡單的 HTTP 函數。您可以通過發送帶有 json 數據負載的 HTTP POST 請求來測試模擬任務功能:

 # start the Local Emulator Suite
 firebase emulators:start

 # trigger the emulated task queue function
 curl \
  -X POST                                            # An HTTP POST request...
  -H "content-type: application/json" \              # ... with a JSON body
  http://localhost:$PORT/$PROJECT_ID/$REGION/$NAME \ # ... to function url
  -d '{"data": { ... some data .... }}'              # ... with JSON encoded data

部署任務隊列功能

使用 Firebase CLI 部署任務隊列功能:

$ firebase deploy --only functions:backupapod

首次部署任務隊列功能時,CLI 會在 Cloud Tasks 中創建一個任務隊列,並在您的源代碼中指定選項(速率限制和重試)。

如果您在部署函數時遇到權限錯誤,請確保將適當的IAM 角色分配給運行部署命令的用戶。

入隊函數

可以使用適用於 Node.js 的 Firebase Admin SDK 將任務隊列函數從受信任的服務器環境(如 Cloud Functions for Firebase)排入 Cloud Tasks 隊列。如果您不熟悉 Admin SDK,請參閱將 Firebase 添加到服務器以開始使用。

在典型流程中,Admin SDK 創建一個新任務,將其排入 {cloudtasks_name}} 隊列,並為任務設置配置:

exports.enqueueBackuptasks = onRequest(
async (_request, response) => {
  const queue = getFunctions().taskQueue("backupApod");
  const enqueues = [];
  for (let i = 0; i <= 10; i += 1) {
    # Enqueue each task with i*60 seconds day. Our task queue function
    # should process ~1 task/min.
    const scheduleDelaySeconds = i * 60 
    enqueues.push(
        queue.enqueue(
          { id: `task-${i}` },
          {
            scheduleDelaySeconds,
            dispatchDeadlineSeconds: 60 * 5 // 5 minutes
          },
        ),
    );
  }
  await Promise.all(enqueues);
  response.sendStatus(200);

});
  • scheduleDelaySeconds :示例代碼嘗試通過為第 N 個任務關聯第 N 分鐘的延遲來分散任務的執行。這轉化為觸發約 1 個任務/分鐘。請注意,如果您希望 Cloud Tasks 在特定時間觸發任務,也可以使用scheduleTime
  • dispatchDeadlineSeconds :Cloud Tasks 等待任務完成的最長時間。 Cloud Tasks 將在隊列的重試配置之後重試該任務,或者直到達到此期限。在示例中,隊列被配置為重試任務最多 5 次,但如果整個過程(包括重試嘗試)時間超過 5 分鐘,任務將自動取消。

檢索並包含目標 URI

由於 Cloud Functions for Firebase v2 不支持確定性 HTTP URL,因此您必須手動檢索目標 URI 並將其包含在每個入隊任務中。您還可以通過編程方式檢索函數的 URL,如下所示:

/**
 * Get the URL of a given v2 cloud function.
 *
 * @param {string} name the function's name
 * @param {string} location the function's location
 * @return {Promise<string>} The URL of the function
 */
async function getFunctionUrl(name, location="us-central1") {
  if (!auth) {
    auth = new GoogleAuth({
      scopes: "https://www.googleapis.com/auth/cloud-platform",
    });
  }
  const projectId = await auth.getProjectId();
  const url = "https://cloudfunctions.googleapis.com/v2beta/" +
    `projects/${projectId}/locations/${location}/functions/${name}`;

  const client = await auth.getClient();
  const res = await client.request({url});
  const uri = res.data?.serviceConfig?.uri;
  if (!uri) {
    throw new Error(`Unable to retreive uri for function at ${url}`);
  }
  return uri;
}

故障排除

打開 Cloud Tasks 日誌記錄

Cloud Tasks 的日誌包含有用的診斷信息,例如與任務關聯的請求的狀態。默認情況下,來自 Cloud Tasks 的日誌已關閉,因為它可能會在您的項目中生成大量日誌。我們建議您在積極開發和調試任務隊列功能時打開調試日誌。請參閱打開日誌記錄

IAM 權限

在將任務排入隊列或 Cloud Tasks 嘗試調用您的任務隊列函數時,您可能會看到PERMISSION DENIED錯誤。確保您的項目具有以下 IAM 綁定:

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member=serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com \
  --role=roles/cloudtasks.enqueuer
  • 用於將任務排入 Cloud Tasks 的身份需要使用與 Cloud Tasks 中的任務關聯的服務帳號的權限。

    在示例中,這是App Engine 默認服務帳戶

有關如何將 App Engine 默認服務帳戶添加為 App Engine 默認服務帳戶用戶的說明,請參閱Google Cloud IAM 文檔

gcloud functions add-iam-policy-binding $FUNCTION_NAME \
  --region=us-central1 \
  --member=serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com \
  --role=roles/cloudfunctions.invoker