ביצוע משימות אסינכרוניות

אתם יכולים להשתמש ב-Cloud Tasks כדי להוסיף משימה לתור בצורה מאובטחת, כך שהיא תעובד באופן אסינכרוני על ידי שירות Cloud Run. תרחישים נפוצים לדוגמה:

  • שמירה על בקשות במהלך אירועי ייצור בלתי צפויים
  • החלקת עליות פתאומיות בתנועה על ידי עיכוב עבודה שלא מוצגת למשתמשים
  • שיפור מהירות התגובה של המשתמשים על ידי העברת פעולות איטיות ברקע לטיפול בשירות אחר, כמו עדכונים של מסד נתונים או עיבוד אצווה
  • הגבלת קצב הקריאות לשירותי גיבוי כמו מסדי נתונים וממשקי API של צד שלישי

בדף הזה מוסבר איך להוסיף לתור משימות שמועברות בצורה מאובטחת באמצעות פרוטוקול HTTPS לשירות פרטי של Cloud Run. המאמר מתאר את ההתנהגות הנדרשת של שירות Cloud Run פרטי, את ההרשאות הנדרשות לחשבון השירות, את יצירת תור המשימות ואת יצירת המשימות.

לפני שמתחילים

מפעילים את Cloud Tasks API בפרויקט שבו אתם משתמשים.

פריסת שירות Cloud Run לטיפול במשימות

כדי לפרוס שירות שמקבל משימות שנשלחות לתור המשימות, פורסים את השירות באותו אופן כמו כל שירות אחר של Cloud Run. שירות Cloud Run צריך להחזיר קוד HTTP‏ 200 כדי לאשר שהמשימה הושלמה בהצלחה.

משימות יידחפו לשירות הזה ב-Cloud Run כבקשות HTTPS על ידי Cloud Tasks.

התגובה ל-Cloud Tasks צריכה להתרחש במסגרת הזמן הקצוב לתפוגה שהוגדר. לעומסי עבודה שצריכים לפעול מעבר לזמן הקצוב לתפוגה המקסימלי של Cloud Tasks, כדאי להשתמש במשימות של Cloud Run.

יצירת תור משימות

שורת הפקודה

כדי ליצור תור משימות, משתמשים בפקודה

gcloud tasks queues create QUEUE-ID

מחליפים את QUEUE-ID בשם שרוצים לתת לתור המשימות. השם חייב להיות ייחודי בפרויקט. אם מוצגת בקשה ליצור אפליקציית App Engine בפרויקט, מזינים y כדי ליצור אותה. ‫Cloud Tasks משתמש במיקום הזה לתור, לכן חשוב לבחור את אותו מיקום שבו אתם משתמשים בשירות Cloud Run.

הגדרת ברירת המחדל של תור המשימות אמורה לפעול ברוב המקרים. עם זאת, אפשר להגדיר מגבלות קצב שונות ופרמטרים שונים לניסיון חוזר אם רוצים.

Terraform

כדי ללמוד איך להחיל הגדרות ב-Terraform או להסיר אותן, ראו פקודות בסיסיות ב-Terraform.

כדי ליצור תור משימות, מוסיפים את הקוד הבא לקובץ .tf:

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

מזינים terraform apply כדי להחיל את השינויים.

יצירת חשבון שירות לשיוך למשימות

צריך ליצור חשבון שירות שישויך למשימות שנוספו לתור. לחשבון השירות הזה צריך להיות תפקיד ה-IAM של Cloud Run Invoker כדי לאפשר לתור המשימות לשלוח משימות לשירות Cloud Run. .

המסוף

  1. נכנסים לדף Service Accounts במסוף Google Cloud .

    לדף Service accounts

  2. בוחרים פרויקט.

  3. נותנים לחשבון השירות שם שיוצג במסוף Google Cloud .

    המסוף יוצר מזהה לחשבון השירות על סמך השם הזה. Google Cloud עורכים את המזהה לפי הצורך. אי אפשר יהיה לשנות את המזהה בשלב מאוחר יותר.

  4. אופציונלי: מזינים תיאור לחשבון השירות.

  5. לוחצים על Create and continue.

  6. אופציונלי: לוחצים על השדה בחירת תפקיד.

  7. בוחרים באפשרות Cloud Run > Cloud Run Invoker.

  8. לוחצים על סיום.

שורת הפקודה

  1. יוצרים את חשבון השירות:

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

    החלפה

    • SERVICE_ACCOUNT_NAME עם שם באותיות קטנות שהוא ייחודי בתוך הפרויקט Google Cloud , לדוגמה my-invoker-service-account-name.
    • DISPLAYED_SERVICE_ACCOUNT_NAME בשם שרוצים להציג לחשבון השירות הזה, לדוגמה, במסוף, למשל, My Invoker Service Account.
  2. ב-Cloud Run, נותנים לחשבון השירות הרשאה להפעיל את השירות:

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

    החלפה

    • SERVICE בשם השירות שרוצים להפעיל באמצעות Cloud Tasks.
    • SERVICE_ACCOUNT_NAME בשם של חשבון השירות.
    • PROJECT_ID במזהה הפרויקט ב- Google Cloud .
  3. נותנים לחשבון השירות גישה לפרויקט כדי שתהיה לו הרשאה לבצע פעולות ספציפיות במשאבים בפרויקט:

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

    החלפה

    • RESOURCE_ID: מזהה הפרויקט ב- Google Cloud .

    • PRINCIPAL: המזהה של החשבון הראשי או של המשתמש בקבוצה. בדרך כלל בצורה הבאה: PRINCIPAL_TYPE:ID. לדוגמה: user:my-user@example.com. בדף העזר בנושא קישורי מדיניות תוכלו למצוא רשימה מלאה של הערכים שיכולים להיות ל-PRINCIPAL.

Terraform

כדי ללמוד איך להחיל הגדרות ב-Terraform או להסיר אותן, ראו פקודות בסיסיות ב-Terraform.

מוסיפים לקובץ .tf את הנתונים הבאים:

יוצרים את חשבון השירות:

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

ב-Cloud Run, נותנים לחשבון השירות הרשאה להפעיל את השירות:

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

מזינים terraform apply כדי להחיל את השינויים.

יצירת משימות HTTP עם טוקנים לאימות

כשיוצרים משימה לשליחה לתור המשימות, מציינים את הפרויקט, המיקום, שם התור, כתובת האימייל של חשבון השירות שנוצר קודם לשיוך למשימות, כתובת ה-URL של שירות Cloud Run הפרטי שיפעיל את המשימה וכל נתון אחר שצריך לשלוח. אתם יכולים להגדיר את הערכים האלה בהגדרות הקוד, אבל ערכים כמו מזהה הפרויקט, המיקום וכתובת האימייל של חשבון השירות יכולים להיות מאוחזרים באופן דינמי משרת המטא-נתונים של Cloud Run.

פרטים על גוף בקשת המשימה זמינים במאמרי העזרה של Cloud Tasks API. שימו לב שבקשות שמכילות נתוני מטען ייעודי חייבות להשתמש בשיטת HTTP PUT או POST.

לקוד שמוסיף את המשימות לתור צריכות להיות הרשאות ה-IAM הנדרשות כדי לעשות זאת, כמו התפקיד Cloud Tasks Enqueuer. אם משתמשים בחשבון השירות שמוגדר כברירת מחדל ב-Cloud Run, לקוד יהיו הרשאות ה-IAM הנדרשות.

בדוגמאות הבאות נוצרות בקשות למשימות שכוללות גם יצירה של אסימון כותרת. בדוגמאות נעשה שימוש באסימוני OIDC. כדי להשתמש בטוקן OAuth, צריך להחליף את הפרמטר OIDC בפרמטר OAuth המתאים לשפה כשיוצרים את הבקשה.

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

שימו לב לקובץ requirements.txt:

google-cloud-tasks==2.18.0

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

שימו לב לקובץ 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();

שימו לב לקובץ 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"
  }
}

המאמרים הבאים