Ejecuta tareas asíncronas

Puedes usar Cloud Tasks para poner en cola de forma segura una tarea que un servicio de Cloud Run procesará de forma asíncrona. Los casos prácticos más comunes incluyen los siguientes:

  • Preservar las solicitudes mediante incidentes de producción imprevistos
  • Suavizar los incrementos de tráfico mediante el retraso del trabajo que no está orientado al usuario
  • Acelerar el tiempo de respuesta del usuario mediante la delegación de operaciones lentas en segundo plano para que las controle otro servicio, como actualizaciones de bases de datos o procesamiento por lotes
  • Limitar la tarifa de llamadas a servicios de creación de copias de seguridad, como bases de datos y API de terceros

En esta página, se muestra cómo poner en cola tareas que se envían de forma segura mediante el protocolo HTTPS a un servicio privado de Cloud Run. Se describe el comportamiento requerido para el servicio privado de Cloud Run, los permisos necesarios de la cuenta de servicio, la creación de listas de tareas en cola y la creación de tareas.

Antes de comenzar

Habilita la API de Cloud Tasks en el proyecto que estás usando.

Implementa un servicio de Cloud Run para controlar tareas

Para implementar un servicio que acepte tareas enviadas a la lista de tareas en cola, implementa el servicio de la misma manera que cualquier otro servicio de Cloud Run. El servicio de Cloud Run debe mostrar un código 200 HTTP para confirmar el éxito después de que se completó el procesamiento de la tarea.

Cloud Tasks enviará las tareas a este servicio de Cloud Run como solicitudes HTTPS.

La respuesta a Cloud Tasks debe ocurrir dentro del tiempo de espera configurado. Para las cargas de trabajo que necesitan ejecutarse más tiempo que el tiempo de espera máximo de Cloud Tasks, considera usar trabajos de Cloud Run.

Lleve a cabo la implementación con Terraform

Si deseas obtener más información para aplicar o quitar una configuración de Terraform, consulta los comandos básicos de Terraform.

Para crear un servicio, agrega lo siguiente al archivo .tf:

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

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

Crea una lista de tareas en cola

Línea de comandos

Para crear una lista de tareas en cola, usa el siguiente comando:

gcloud tasks queues create QUEUE-ID

Reemplaza QUEUE-ID por el nombre que deseas asignar a la lista de tareas en cola: debe ser único en el proyecto. Si se te solicita que crees una app de App Engine en el proyecto, responde y para crearla. Cloud Tasks usa esto para la cola: asegúrate de elegir la misma ubicación que usas para el servicio de Cloud Run.

La configuración predeterminada de la lista de tareas en cola debería funcionar en la mayoría de los casos. Sin embargo, si lo deseas, puedes establecer diferentes límites de frecuencia y parámetros de reintento.

Terraform

Si deseas obtener más información para aplicar o quitar una configuración de Terraform, consulta los comandos básicos de Terraform.

Para crear una lista de tareas en cola, agrega lo siguiente al archivo .tf:

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

Ingresa terraform apply para aplicar los cambios.

Crea una cuenta de servicio para asociar con las tareas

Debes crear una cuenta de servicio que se asociará con las tareas en cola. Esta cuenta de servicio debe tener la función de IAM de Invocador de Cloud Run para permitir que la lista de tareas en cola envíe las tareas al servicio de Cloud Run. .

Console

  1. En la consola de Google Cloud, ve a la página Cuentas de servicio.

    Ir a Cuentas de servicio

  2. Selecciona un proyecto

  3. Ingresa un nombre de cuenta de servicio para mostrar en la consola de Google Cloud.

    La consola de Google Cloud genera un ID de cuenta de servicio a partir de este nombre. Si es necesario, edita el ID. No podrás cambiar el ID más adelante.

  4. Opcional: Ingresa una descripción de la cuenta de servicio.

  5. Haz clic en Crear y continuar.

  6. Opcional: Haz clic en el campo Seleccionar una función.

  7. Selecciona Cloud Run > Invocador de Cloud Run.

  8. Haz clic en Listo.

Línea de comandos

  1. Crea la cuenta de servicio:

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

    Reemplaza los siguientes elementos:

    • SERVICE_ACCOUNT_NAME por un nombre único en minúscula dentro del proyecto de Google Cloud, por ejemplo, my-invoker-service-account-name
    • DISPLAYED_SERVICE_ACCOUNT_NAME por el nombre que deseas mostrar para esta cuenta de servicio, por ejemplo, en la consola, My Invoker Service Account
  2. En Cloud Run, debes otorgar permiso a la cuenta de servicio para invocar el servicio:

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

    Reemplaza los siguientes elementos:

    • SERVICE por el nombre del servicio que deseas que Cloud Tasks invoque
    • SERVICE_ACCOUNT_NAME por el nombre de la cuenta de servicio
    • PROJECT_ID por el ID del proyecto de Google Cloud.
  3. Otórgale a tu cuenta de servicio acceso al proyecto para que tenga el permiso de completar acciones específicas en los recursos de tu proyecto.

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

    Reemplaza los siguientes elementos:

    • RESOURCE_ID es el ID del proyecto de Google Cloud.

    • PRINCIPAL: Un identificador para la principal o el miembro, que suele tener el siguiente formato: PRINCIPAL_TYPE:ID. Por ejemplo, user:my-user@example.com Si deseas obtener una lista completa de los valores que PRINCIPAL puede tener, consulta la referencia sobre la vinculación de políticas.

Terraform

Si deseas obtener más información para aplicar o quitar una configuración de Terraform, consulta los comandos básicos de Terraform.

Agrega lo siguiente a tu archivo .tf:

Crea la cuenta de servicio:

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

En Cloud Run, debes otorgar permiso a la cuenta de servicio para invocar el servicio:

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}"]
}

Ingresa terraform apply para aplicar los cambios.

Crea tareas HTTP con tokens de autenticación

Cuando creas una tarea para enviar a la lista de tareas en cola, debes especificar el proyecto, la ubicación, el nombre de la cola, el correo electrónico de la cuenta de servicio que creaste antes a fin de asociarla con las tareas, la URL del servicio privado de Cloud Run que ejecutará la tarea y cualquier otro dato que debas enviar. Puedes elegir codificar estos valores, aunque los valores como el ID del proyecto, la ubicación y el correo electrónico de la cuenta de servicio se pueden recuperar de forma dinámica desde el servidor de metadatos de Cloud Run.

Consulta la documentación de la API de Cloud Tasks para obtener detalles sobre el cuerpo de la solicitud de la tarea. Ten en cuenta que las solicitudes que contienen cargas útiles de datos deben usar el método HTTP PUT o POST.

El código que pone las tareas en cola debe tener los permisos de IAM necesarios para hacerlo, como la función Agregador de elementos en cola de Cloud Tasks. El código tendrá los permisos de IAM necesarios si usas la cuenta de servicio predeterminada en Cloud Run.

Los siguientes ejemplos crean solicitudes de tareas que también incluyen la creación de un token de encabezado. Se usan los tokens OIDC en los ejemplos. A fin de usar un token de OAuth, reemplaza el parámetro OIDC por el parámetro de OAuth apropiado para el lenguaje en la construcción de la solicitud.

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,
        )
    )

Ten en cuenta el archivo 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());
    }
  }
}

Ten en cuenta el archivo 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>

Go

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();

Ten en cuenta el archivo 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": "^4.0.0",
    "express": "^4.16.3"
  },
  "devDependencies": {
    "c8": "^8.0.0",
    "chai": "^4.2.0",
    "mocha": "^10.0.0",
    "uuid": "^9.0.0"
  }
}

¿Qué sigue?