Accoda le funzioni con Cloud Tasks

Le funzioni della coda di attività sfruttano Google Cloud Tasks per aiutare la tua app a eseguire in modo asincrono, al di fuori del flusso principale dell'applicazione, attività che richiedono molto tempo, molte risorse o una larghezza di banda limitata.

Ad esempio, supponiamo che tu voglia creare backup di un insieme di file di immagini di grandi dimensioni attualmente ospitati su un'API con un limite di frequenza. Per utilizzare l'API in modo responsabile, devi rispettare i limiti di frequenza. Inoltre, questo tipo di job a lunga esecuzione potrebbe essere vulnerabile a errori dovuti a timeout e limiti di memoria.

Per ridurre questa complessità, puoi scrivere una funzione della coda di attività che imposta le opzioni di base delle attività, come scheduleTime e dispatchDeadline, e poi passa la funzione a una coda in Cloud Tasks. L'Cloud Tasks ambiente è progettato appositamente per garantire un controllo efficace della congestione e norme di nuovi tentativi per questo tipo di operazioni.

L'SDK Firebase per Cloud Functions for Firebase v3.20.1 e versioni successive interagisce con Firebase Admin SDK v10.2.0 e versioni successive per supportare le funzioni della coda di attività.

L'utilizzo delle funzioni della coda di attività con Firebase può comportare addebiti per Cloud Tasks l'elaborazione. Per ulteriori informazioni, consulta i Cloud Tasks prezzi.

Creare funzioni della coda di attività

Per utilizzare le funzioni della coda di attività, segui questo flusso di lavoro:

  1. Scrivi una funzione della coda di attività utilizzando l'SDK Firebase per Cloud Functions.
  2. Testa la funzione attivandola con una richiesta HTTP.
  3. Esegui il deployment della funzione con la Firebase CLI. Quando esegui il deployment della funzione della coda di attività per la prima volta, l'interfaccia a riga di comando crea una coda di attività in Cloud Tasks con le opzioni (limitazione di frequenza e nuovi tentativi) specificate nel codice sorgente.
  4. Aggiungi attività alla coda di attività appena creata, passando i parametri per configurare una pianificazione di esecuzione, se necessario. Puoi farlo scrivendo il codice utilizzando Admin SDK ed eseguendone il deployment in Cloud Functions for Firebase.

Scrivere funzioni della coda di attività

Gli esempi di codice in questa sezione si basano su un'app che configura un servizio che esegue il backup di tutte le immagini di Astronomy Picture of the Day della NASA. Per iniziare, importa i moduli richiesti:

Node.js

// Dependencies for task queue functions.
const {onTaskDispatched} = require("firebase-functions/tasks");
const {onRequest, HttpsError} = require("firebase-functions/https");
const {getFunctions} = require("firebase-admin/functions");
const {logger} = require("firebase-functions");

// Dependencies for image backup.
const path = require("path");
const {initializeApp} = require("firebase-admin/app");
const {getStorage} = require("firebase-admin/storage");
const {GoogleAuth} = require("google-auth-library");

Python

# Dependencies for task queue functions.
from google.cloud import tasks_v2
import requests
from firebase_functions.options import RetryConfig, RateLimits, SupportedRegion

# Dependencies for image backup.
from datetime import datetime, timedelta
import json
import pathlib
from urllib.parse import urlparse
from firebase_admin import initialize_app, storage, functions
from firebase_functions import https_fn, tasks_fn, params
import google.auth
from google.auth.transport.requests import AuthorizedSession

Utilizza onTaskDispatched o on_task_dispatched per le funzioni della coda di attività. Quando scrivi una funzione della coda di attività, puoi impostare la configurazione dei nuovi tentativi e della limitazione della frequenza per ogni coda.

Configurare le funzioni della coda di attività

Le funzioni della coda di attività sono dotate di un potente insieme di impostazioni di configurazione per controllare con precisione i limiti di frequenza e il comportamento dei nuovi tentativi di una coda di attività:

Node.js

exports.backupapod = onTaskDispatched(
    {
      retryConfig: {
        maxAttempts: 5,
        minBackoffSeconds: 60,
      },
      rateLimits: {
        maxConcurrentDispatches: 6,
      },
    }, async (req) => {

Python

@tasks_fn.on_task_dispatched(
    retry_config=RetryConfig(max_attempts=5, min_backoff_seconds=60),
    rate_limits=RateLimits(max_concurrent_dispatches=10),
)
def backupapod(req: tasks_fn.CallableRequest) -> str:
    """Grabs Astronomy Photo of the Day (APOD) using NASA's API."""
  • retryConfig.maxAttempts=5: ogni attività nella coda di attività viene ritentata automaticamente fino a 5 volte. In questo modo è possibile mitigare gli errori temporanei, come gli errori di rete o l'interruzione temporanea del servizio di un servizio esterno dipendente.

  • retryConfig.minBackoffSeconds=60: ogni attività viene ritentata almeno 60 secondi dopo ogni tentativo. In questo modo viene fornito un buffer di grandi dimensioni tra ogni tentativo, in modo da non esaurire troppo rapidamente i 5 tentativi.

  • rateLimits.maxConcurrentDispatch=6: al massimo 6 attività vengono inviate contemporaneamente. In questo modo si garantisce un flusso costante di richieste alla funzione sottostante e si riduce il numero di istanze attive e di avvii a freddo.

Testare le funzioni della coda di attività

Nella maggior parte dei casi, l'Cloud Functions emulatore è il modo migliore per testare le funzioni della coda di attività. Consulta la documentazione di Emulator Suite per scoprire come instrumentare la tua app per l'emulazione delle funzioni della coda di attività.

Inoltre, le funzioni della coda di attività vengono esposte come semplici funzioni HTTP in Firebase Local Emulator Suite. Puoi testare una funzione di attività emulata inviando una richiesta HTTP POST con un payload di dati JSON:

 # 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

Eseguire il deployment delle funzioni della coda di attività

Esegui il deployment della funzione della coda di attività utilizzando la Firebase CLI:

$ firebase deploy --only functions:backupapod

Quando esegui il deployment di una funzione della coda di attività per la prima volta, l'interfaccia a riga di comando crea una coda di attività in Cloud Tasks con le opzioni (limitazione della frequenza e nuovi tentativi) specificate nel codice sorgente.

Se riscontri errori di autorizzazione durante il deployment delle funzioni, assicurati che all'utente che esegue i comandi di deployment siano assegnati i ruoli IAM appropriati.

Inserire in coda le funzioni della coda di attività

Le funzioni della coda di attività possono essere inserite in coda in Cloud Tasks da un ambiente server attendibile come Cloud Functions for Firebase utilizzando l'SDK Admin Firebase per Firebase Admin SDKNode.js o le librerie Google Cloud per Python. Se non hai mai utilizzato gli Admin SDKs, consulta Aggiungere Firebase a un server per iniziare.

Un flusso tipico crea una nuova attività, la inserisce in coda in Cloud Tasks, e imposta la configurazione dell'attività:

Node.js

exports.enqueuebackuptasks = onRequest(
    async (_request, response) => {
      const queue = getFunctions().taskQueue("backupapod");
      const targetUri = await getFunctionUrl("backupapod");

      const enqueues = [];
      for (let i = 0; i <= BACKUP_COUNT; i += 1) {
        const iteration = Math.floor(i / HOURLY_BATCH_SIZE);
        // Delay each batch by N * hour
        const scheduleDelaySeconds = iteration * (60 * 60);

        const backupDate = new Date(BACKUP_START_DATE);
        backupDate.setDate(BACKUP_START_DATE.getDate() + i);
        // Extract just the date portion (YYYY-MM-DD) as string.
        const date = backupDate.toISOString().substring(0, 10);
        enqueues.push(
            queue.enqueue({date}, {
              scheduleDelaySeconds,
              dispatchDeadlineSeconds: 60 * 5, // 5 minutes
              uri: targetUri,
            }),
        );
      }
      await Promise.all(enqueues);
      response.sendStatus(200);
    });

Python

@https_fn.on_request()
def enqueuebackuptasks(_: https_fn.Request) -> https_fn.Response:
    """Adds backup tasks to a Cloud Tasks queue."""
    task_queue = functions.task_queue("backupapod")
    target_uri = get_function_url("backupapod")

    for i in range(BACKUP_COUNT):
        batch = i // HOURLY_BATCH_SIZE

        # Delay each batch by N hours
        schedule_delay = timedelta(hours=batch)
        schedule_time = datetime.now() + schedule_delay

        dispatch_deadline_seconds = 60 * 5  # 5 minutes

        backup_date = BACKUP_START_DATE + timedelta(days=i)
        body = {"data": {"date": backup_date.isoformat()[:10]}}
        task_options = functions.TaskOptions(
            schedule_time=schedule_time,
            dispatch_deadline_seconds=dispatch_deadline_seconds,
            uri=target_uri,
        )
        task_queue.enqueue(body, task_options)
    return https_fn.Response(status=200, response=f"Enqueued {BACKUP_COUNT} tasks")
  • Il codice di esempio tenta di distribuire l'esecuzione delle attività associando un ritardo di N minuti per l'attività N. Ciò si traduce nell'attivazione di circa 1 attività/minuto. Tieni presente che puoi anche utilizzare scheduleTime (Node.js) o schedule_time (Python) se vuoi che Cloud Tasks attivi un'attività in un momento specifico.

  • Il codice campione imposta la quantità massima di tempo Cloud Tasks attenderà il completamento di un'attività. Cloud Tasks ritenterà l'attività seguendo la configurazione dei nuovi tentativi della coda o fino al raggiungimento di questa scadenza. Nell'esempio, la coda è configurata per ritentare l'attività fino a 5 volte, ma l'attività viene annullata automaticamente se l'intero processo (inclusi i tentativi) richiede più di 5 minuti.

Risoluzione dei problemi

Attivare la registrazione di Cloud Tasks

I log da Cloud Tasks contengono informazioni diagnostiche utili, come lo stato della richiesta associata a un'attività. Per impostazione predefinita, i log di Cloud Tasks sono disattivati a causa del volume elevato di log che possono essere generati potenzialmente nel tuo progetto. Ti consigliamo di attivare i log di debug durante lo sviluppo e il debug attivi delle funzioni della coda di attività. Consulta Attivare la registrazione.

Autorizzazioni IAM

Potresti visualizzare errori PERMISSION DENIED quando inserisci in coda le attività o quando Cloud Tasks tenta di richiamare le funzioni della coda di attività. Assicurati che il tuo progetto abbia i seguenti binding IAM:

  • L'identità utilizzata per inserire in coda le attività in Cloud Tasks deve avere cloudtasks.tasks.create l'autorizzazione IAM.

    Nell'esempio, questo è l'account di servizio App Engine predefinito

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member=serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com \
  --role=roles/cloudtasks.enqueuer
  • L'identità utilizzata per inserire in coda le attività in Cloud Tasks deve avere l'autorizzazione per utilizzare il service account associato a un'attività in Cloud Tasks.

    Nell'esempio, questo è l'account di servizio predefinito App Engine.

Per istruzioni su come aggiungere l'App Engine default service account come utente dell'App Engine default service account , consulta la documentazione di Google Cloud IAM.

  • L'identità utilizzata per attivare la funzione della coda di attività deve avere l'autorizzazione cloudfunctions.functions.invoke.

    Nell'esempio, questo è l'account di servizio App Engine predefinito

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