Esecuzione di attività asincrone

Puoi utilizzare Cloud Tasks per mettere in coda in modo sicuro un'attività da elaborare in modo asincrono da un servizio Cloud Run. I casi d'uso tipici includono:

  • Preservare le richieste in caso di incidenti di produzione imprevisti
  • Appianare i picchi di traffico ritardando il lavoro non rivolto agli utenti
  • Accelerare il tempo di risposta dell'utente delegando le operazioni in background lente a un altro servizio, ad esempio aggiornamenti del database o elaborazione collettiva
  • Limitare la frequenza di chiamata ai servizi di supporto come database e API di terze parti

Questa pagina mostra come mettere in coda le attività che vengono inviate in modo sicuro tramite il protocollo HTTPS a un servizio Cloud Run privato. Descrive il comportamento richiesto per il servizio Cloud Run privato, le autorizzazioni dell'account di servizio richieste, la creazione della coda di attività e la creazione delle attività.

Prima di iniziare

Abilita l'API Cloud Tasks nel progetto che stai utilizzando.

Eseguire il deployment di un servizio Cloud Run per gestire le attività

Per eseguire il deployment di un servizio che accetta le attività inviate alla coda di attività, esegui il deployment del servizio nello stesso modo in cui faresti con qualsiasi altro servizio Cloud Run. Il servizio Cloud Run deve restituire un codice HTTP 200 per confermare il completamento dell'elaborazione dell'attività.

Le attività verranno inviate a questo servizio Cloud Run come richieste HTTPS da Cloud Tasks.

La risposta a Cloud Tasks deve avvenire entro il timeout configurato. Per i workload che devono essere eseguiti per più tempo del timeout massimo di Cloud Tasks, ti consigliamo di utilizzare i job Cloud Run.

Esegui il deployment con Terraform

Per scoprire come applicare o rimuovere una configurazione Terraform, consulta Comandi Terraform di base.

Per creare un servizio, aggiungi quanto segue al file .tf:

resource "google_cloud_run_v2_service" "default" {
  name     = "cloud-run-task-service"
  location = "us-central1"

  deletion_protection = false # set to "true" in production

  template {
    containers {
      image = "us-docker.pkg.dev/cloudrun/container/hello"
    }
  }
}

Creazione di una coda di attività

Riga di comando

Per creare una coda di attività, utilizza il comando

gcloud tasks queues create QUEUE-ID

sostituendo QUEUE-ID con il nome che vuoi assegnare alla coda di attività: deve essere univoco nel progetto. Se ti viene chiesto di creare un'app App Engine nel tuo progetto, rispondi y per crearla. Cloud Tasks lo utilizza per la coda: assicurati di scegliere la stessa località utilizzata per il servizio Cloud Run.

La configurazione predefinita della coda di attività dovrebbe funzionare nella maggior parte dei casi. Tuttavia, se vuoi, puoi impostare limiti di frequenza e parametri di ripetizione diversi.

Terraform

Per scoprire come applicare o rimuovere una configurazione Terraform, consulta Comandi Terraform di base.

Per creare una coda di attività, aggiungi quanto segue al file .tf:

resource "google_cloud_tasks_queue" "default" {
  name     = "cloud-tasks-queue-name"
  location = "us-central1"
}

Applica le modifiche inserendo terraform apply.

Creazione di un account di servizio da associare alle attività

Devi creare un account di servizio che verrà associato alle attività in coda. Questo account di servizio deve avere il ruolo IAM Invoker di Cloud Run per consentire alla coda di attività di inviare attività al servizio Cloud Run. .

Console

  1. Nella console Google Cloud, vai alla pagina Account di servizio.

    Vai a Service account

  2. Seleziona un progetto.

  3. Inserisci il nome di un account di servizio da visualizzare nella console Google Cloud.

    La console Google Cloud genera un ID account di servizio in base a questo nome. Modifica l'ID, se necessario. Non potrai modificare l'ID in un secondo momento.

  4. (Facoltativo) Inserisci una descrizione dell'account di servizio.

  5. Fai clic su Crea e continua.

  6. (Facoltativo) Fai clic sul campo Seleziona un ruolo.

  7. Seleziona Cloud Run > Cloud Run Invoker.

  8. Fai clic su Fine.

Riga di comando

  1. Crea l'account di servizio:

    gcloud iam service-accounts create SERVICE_ACCOUNT_NAME \
       --display-name "DISPLAYED_SERVICE_ACCOUNT_NAME"

    Sostituisci

    • SERVICE_ACCOUNT_NAME con un nome in minuscolo univoco all'interno del progetto Google Cloud, ad esempio my-invoker-service-account-name.
    • DISPLAYED_SERVICE_ACCOUNT_NAME con il nome che vuoi visualizzare per questo account di servizio, ad esempio nella console, ad esempio My Invoker Service Account.
  2. Per Cloud Run, concedi all'account di servizio l'autorizzazione per invocare il servizio:

    gcloud run services add-iam-policy-binding SERVICE \
       --member=serviceAccount:SERVICE_ACCOUNT_NAME@PROJECT_ID.iam.gserviceaccount.com \
       --role=roles/run.invoker

    Sostituisci

    • SERVICE con il nome del servizio che vuoi invocare da Cloud Tasks.
    • SERVICE_ACCOUNT_NAME con il nome dell'account di servizio.
    • PROJECT_ID con il tuo ID progetto Google Cloud.
  3. Concedi all'account di servizio l'accesso al progetto, affinché sia autorizzato a completare azioni specifiche sulle risorse nel progetto:

    gcloud projects add-iam-policy-binding RESOURCE_ID \
       --member=PRINCIPAL --role=roles/run.invoker

    Sostituisci

    • RESOURCE_ID: l'ID del tuo progetto Google Cloud.

    • PRINCIPAL: un identificatore per il principale o il membro, che in genere ha la seguente forma:PRINCIPAL_TYPE:ID. Ad esempio,user:my-user@example.com. Per un elenco completo dei valori che può assumere PRINCIPAL, consulta la documentazione di riferimento sull'associazione dei criteri.

Terraform

Per scoprire come applicare o rimuovere una configurazione Terraform, consulta Comandi Terraform di base.

Aggiungi quanto segue al tuo file .tf:

Crea l'account di servizio:

resource "google_service_account" "default" {
  account_id   = "cloud-run-task-invoker"
  display_name = "Cloud Run Task Invoker"
}

Per Cloud Run, concedi all'account di servizio l'autorizzazione a invocare il servizio:

resource "google_cloud_run_service_iam_binding" "default" {
  location = google_cloud_run_v2_service.default.location
  service  = google_cloud_run_v2_service.default.name
  role     = "roles/run.invoker"
  members  = ["serviceAccount:${google_service_account.default.email}"]
}

Applica le modifiche inserendo terraform apply.

Creazione di attività HTTP con token di autenticazione

Quando crei un'attività da inviare alla coda di attività, specifica il progetto, la posizione, il nome della coda, l'indirizzo email dell'account di servizio creato in precedenza da associare alle attività, l'URL del servizio Cloud Run privato che eseguirà l'attività e tutti gli altri dati da inviare. Puoi scegliere di codificare questi valori, anche se valori come l'ID progetto, la posizione e l'email del account di servizio possono essere recuperati dinamicamente dal server di metadati di Cloud Run.

Per informazioni dettagliate sul corpo della richiesta di attività, consulta la documentazione dell'API Cloud Tasks. Tieni presente che le richieste che contengono payload di dati devono utilizzare il metodo HTTP PUT o POST.

Il codice che mette in coda le attività deve disporre delle autorizzazioni IAM necessarie per farlo, ad esempio il ruolo Inserzionista in coda di Cloud Tasks. Il codice disporrà delle autorizzazioni IAM necessarie se utilizzi l'account di servizio predefinito su Cloud Run.

Gli esempi riportati di seguito creano richieste di attività che includono anche la creazione di un token intestazione. Negli esempi vengono utilizzati token OIDC. Per utilizzare un token OAuth, sostituisci il parametro OIDC con il parametro OAuth appropriato per la lingua durante la creazione della richiesta.

Python

from typing import Optional

from google.cloud import tasks_v2


def create_http_task_with_token(
    project: str,
    location: str,
    queue: str,
    url: str,
    payload: bytes,
    service_account_email: str,
    audience: Optional[str] = None,
) -> tasks_v2.Task:
    """Create an HTTP POST task with an OIDC token and an arbitrary payload.
    Args:
        project: The project ID where the queue is located.
        location: The location where the queue is located.
        queue: The ID of the queue to add the task to.
        url: The target URL of the task.
        payload: The payload to send.
        service_account_email: The service account to use for generating the OIDC token.
        audience: Audience to use when generating the OIDC token.
    Returns:
        The newly created task.
    """

    # Create a client.
    client = tasks_v2.CloudTasksClient()

    # Construct the request body.
    task = tasks_v2.Task(
        http_request=tasks_v2.HttpRequest(
            http_method=tasks_v2.HttpMethod.POST,
            url=url,
            oidc_token=tasks_v2.OidcToken(
                service_account_email=service_account_email,
                audience=audience,
            ),
            body=payload,
        ),
    )

    # Use the client to build and send the task.
    return client.create_task(
        tasks_v2.CreateTaskRequest(
            parent=client.queue_path(project, location, queue),
            task=task,
        )
    )

Prendi nota del file requirements.txt:

google-cloud-tasks==2.13.1

Java

import com.google.cloud.tasks.v2.CloudTasksClient;
import com.google.cloud.tasks.v2.HttpMethod;
import com.google.cloud.tasks.v2.HttpRequest;
import com.google.cloud.tasks.v2.OidcToken;
import com.google.cloud.tasks.v2.QueueName;
import com.google.cloud.tasks.v2.Task;
import com.google.protobuf.ByteString;
import java.io.IOException;
import java.nio.charset.Charset;

public class CreateHttpTaskWithToken {

  public static void main(String[] args) throws IOException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project-id";
    String locationId = "us-central1";
    String queueId = "my-queue";
    String serviceAccountEmail =
        "java-docs-samples-testing@java-docs-samples-testing.iam.gserviceaccount.com";
    createTask(projectId, locationId, queueId, serviceAccountEmail);
  }

  // Create a task with a HTTP target and authorization token using the Cloud Tasks client.
  public static void createTask(
      String projectId, String locationId, String queueId, String serviceAccountEmail)
      throws IOException {

    // Instantiates a client.
    try (CloudTasksClient client = CloudTasksClient.create()) {
      String url =
          "https://example.com/taskhandler"; // The full url path that the request will be sent to
      String payload = "Hello, World!"; // The task HTTP request body

      // Construct the fully qualified queue name.
      String queuePath = QueueName.of(projectId, locationId, queueId).toString();

      // Add your service account email to construct the OIDC token.
      // in order to add an authentication header to the request.
      OidcToken.Builder oidcTokenBuilder =
          OidcToken.newBuilder().setServiceAccountEmail(serviceAccountEmail);

      // Construct the task body.
      Task.Builder taskBuilder =
          Task.newBuilder()
              .setHttpRequest(
                  HttpRequest.newBuilder()
                      .setBody(ByteString.copyFrom(payload, Charset.defaultCharset()))
                      .setHttpMethod(HttpMethod.POST)
                      .setUrl(url)
                      .setOidcToken(oidcTokenBuilder)
                      .build());

      // Send create task request.
      Task task = client.createTask(queuePath, taskBuilder.build());
      System.out.println("Task created: " + task.getName());
    }
  }
}

Prendi nota del file pom.xml:

<?xml version='1.0' encoding='UTF-8'?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example.tasks</groupId>
  <artifactId>cloudtasks-snippets</artifactId>
  <packaging>jar</packaging>
  <name>Google Cloud Tasks Snippets</name>

  <!--
    The parent pom defines common style checks and testing strategies for our samples.
    Removing or replacing it should not affect the execution of the samples in anyway.
  -->
  <parent>
    <groupId>com.google.cloud.samples</groupId>
    <artifactId>shared-configuration</artifactId>
    <version>1.2.0</version>
  </parent>

  <properties>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.source>1.8</maven.compiler.source>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>


  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.google.cloud</groupId>
        <artifactId>libraries-bom</artifactId>
        <version>26.32.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-tasks</artifactId>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.google.truth</groupId>
      <artifactId>truth</artifactId>
      <version>1.4.0</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Vai

import (
	"context"
	"fmt"

	cloudtasks "cloud.google.com/go/cloudtasks/apiv2"
	taskspb "cloud.google.com/go/cloudtasks/apiv2/cloudtaskspb"
)

// createHTTPTaskWithToken constructs a task with a authorization token
// and HTTP target then adds it to a Queue.
func createHTTPTaskWithToken(projectID, locationID, queueID, url, email, message string) (*taskspb.Task, error) {
	// Create a new Cloud Tasks client instance.
	// See https://godoc.org/cloud.google.com/go/cloudtasks/apiv2
	ctx := context.Background()
	client, err := cloudtasks.NewClient(ctx)
	if err != nil {
		return nil, fmt.Errorf("NewClient: %w", err)
	}
	defer client.Close()

	// Build the Task queue path.
	queuePath := fmt.Sprintf("projects/%s/locations/%s/queues/%s", projectID, locationID, queueID)

	// Build the Task payload.
	// https://godoc.org/google.golang.org/genproto/googleapis/cloud/tasks/v2#CreateTaskRequest
	req := &taskspb.CreateTaskRequest{
		Parent: queuePath,
		Task: &taskspb.Task{
			// https://godoc.org/google.golang.org/genproto/googleapis/cloud/tasks/v2#HttpRequest
			MessageType: &taskspb.Task_HttpRequest{
				HttpRequest: &taskspb.HttpRequest{
					HttpMethod: taskspb.HttpMethod_POST,
					Url:        url,
					AuthorizationHeader: &taskspb.HttpRequest_OidcToken{
						OidcToken: &taskspb.OidcToken{
							ServiceAccountEmail: email,
						},
					},
				},
			},
		},
	}

	// Add a payload message if one is present.
	req.Task.GetHttpRequest().Body = []byte(message)

	createdTask, err := client.CreateTask(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("cloudtasks.CreateTask: %w", err)
	}

	return createdTask, nil
}

Node.js

// Imports the Google Cloud Tasks library.
const {CloudTasksClient} = require('@google-cloud/tasks');

// Instantiates a client.
const client = new CloudTasksClient();

async function createHttpTaskWithToken() {
  // TODO(developer): Uncomment these lines and replace with your values.
  // const project = 'my-project-id';
  // const queue = 'my-queue';
  // const location = 'us-central1';
  // const url = 'https://example.com/taskhandler';
  // const serviceAccountEmail = 'client@<project-id>.iam.gserviceaccount.com';
  // const payload = 'Hello, World!';

  // Construct the fully qualified queue name.
  const parent = client.queuePath(project, location, queue);

  const task = {
    httpRequest: {
      headers: {
        'Content-Type': 'text/plain', // Set content type to ensure compatibility your application's request parsing
      },
      httpMethod: 'POST',
      url,
      oidcToken: {
        serviceAccountEmail,
      },
    },
  };

  if (payload) {
    task.httpRequest.body = Buffer.from(payload).toString('base64');
  }

  console.log('Sending task:');
  console.log(task);
  // Send create task request.
  const request = {parent: parent, task: task};
  const [response] = await client.createTask(request);
  const name = response.name;
  console.log(`Created task ${name}`);
}
createHttpTaskWithToken();

Prendi nota del file package.json:

{
  "name": "appengine-cloudtasks",
  "description": "Google App Engine Cloud Tasks example.",
  "license": "Apache-2.0",
  "author": "Google Inc.",
  "private": true,
  "engines": {
    "node": ">=16.0.0"
  },
  "files": [
    "*.js"
  ],
  "scripts": {
    "test": "c8 mocha -p -j 2 --timeout 30000",
    "start": "node server.js"
  },
  "dependencies": {
    "@google-cloud/tasks": "^5.0.0",
    "express": "^4.16.3"
  },
  "devDependencies": {
    "c8": "^10.0.0",
    "chai": "^4.5.0",
    "mocha": "^10.0.0",
    "uuid": "^10.0.0"
  }
}

Passaggi successivi