صف‌بندی توابع با Cloud Tasks

کارکردهای صف تکلیف از Google Cloud Tasks استفاده می‌کنند تا به برنامه شما کمک کنند تکالیف زمان‌بر، منابع‌بر، یا با پهنای باند محدود را به‌صورت ناهم‌زمان و خارج از جریان اصلی برنامه اجرا کند.

برای مثال، تصور کنید که می‌خواهید از مجموعه بزرگی از فایل‌های تصویری که درحال‌حاضر در یک «میانای برنامه‌سازی کاربردی» با محدودیت نرخ میزبانی می‌شوند نسخه پشتیبان تهیه کنید. برای اینکه مصرف‌کننده مسئول آن «میانای برنامه‌سازی کاربردی» باشید، باید حدود نرخ آن‌ها را رعایت کنید. به‌علاوه، این نوع کار طولانی‌مدت ممکن است به‌دلیل اتمام زمان و محدودیت‌های حافظه آسیب‌پذیر باشد.

برای کاهش این پیچیدگی، می‌توانید تابع صف تکلیف بنویسید که گزینه‌های تکلیف پایه مثل scheduleTime و dispatchDeadline را تنظیم می‌کند و سپس تابع را به صفی در Cloud Tasks واگذار می‌کند. محیط Cloud Tasks به‌طور خاص برای اطمینان از کنترل مؤثر ازدحام و خط‌مشی‌های تلاش مجدد برای این نوع عملیات طراحی شده است.

«کیت توسعه نرم‌افزار Firebase» برای Cloud Functions for Firebase نسخه ۳.۲۰.۱ و بالاتر با Firebase Admin SDK نسخه ۱۰.۲.۰ و بالاتر برای پشتیبانی از عملکردهای صف وظیفه تعامل‌پذیری دارد.

استفاده از توابع صف وظایف با Firebase می‌تواند منجر به هزینه‌های پردازش Cloud Tasks شود. برای اطلاعات بیشتر، قیمت‌گذاری Cloud Tasks را ببینید.

ایجاد توابع صف تکلیف

برای استفاده از توابع صف کار، این گردش کار را دنبال کنید:

  1. بااستفاده از کیت توسعه نرم‌افزار Firebase برای Cloud Functions، تابع صف تکلیف بنویسید.
  2. با راه‌اندازی تابع با درخواست HTTP، آن را آزمایش کنید.
  3. تابع خود را با Firebase CLI مستقر کنید. وقتی برای اولین‌بار تابع صف کار را پیاده‌سازی می‌کنید، «واسط خط فرمان» صف کاری را در Cloud Tasks با گزینه‌هایی (محدودیت نرخ و تلاش مجدد) که در کد منبع شما مشخص شده است ایجاد می‌کند.
  4. تکالیف را به صف تکلیف تازه ایجادشده اضافه کنید و پارامترها را برای تنظیم برنامه زمانی اجرا درصورت نیاز ارسال کنید. می‌توانید با نوشتن کد بااستفاده از Admin SDK و استقرار آن در Cloud Functions for Firebase به این هدف برسید.

نوشتن توابع صف تکلیف

نمونه‌های کد در این بخش براساس برنامه‌ای است که سرویسی راه‌اندازی می‌کند که از همه تصاویر عکس نجومی روز ناسا پشتیبان‌گیری می‌کند. برای شروع، واحدهای لازم را وارد کنید:

Node.js

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

// Dependencies for image backup.
const {URL, URLSearchParams} = require("node:url");
const path = require("path");
const {initializeApp} = require("firebase-admin/app");
const {getStorage} = require("firebase-admin/storage");

پایتون

# 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

برای توابع صف تکلیف از onTaskDispatched یا on_task_dispatched استفاده کنید. هنگام نوشتن تابع صف تکلیف می‌توانید پیکربندی محدودیت نرخ و تلاش مجدد برای هر صف را تنظیم کنید.

پیکربندی کردن توابع صف تکلیف

توابع صف تکلیف با مجموعه قدرتمندی از تنظیمات پیکربندی ارائه می‌شوند که به شما امکان می‌دهد حدود نرخ و رفتار تلاش مجدد صف تکلیف را به‌دقت کنترل کنید:

Node.js

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

پایتون

@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: هر تکلیف در صف تکلیف به‌طور خودکار تا ۵ بار دوباره امتحان می‌شود. این کار به کاهش خطاهای گذرا مانند خطاهای شبکه یا اختلال موقت در سرویس وابسته و خارجی کمک می‌کند.

  • retryConfig.minBackoffSeconds=60: هر تکلیف حداقل با فاصله ۶۰ ثانیه از هر تلاش مجدد امتحان می‌شود. این کار باعث ایجاد یک بافر بزرگ بین هر تلاش می‌شود بنابراین ما برای استفاده از ۵ تلاش مجدد عجله نمی‌کنیم.

  • ‫rateLimits.maxConcurrentDispatch=6: در هر زمان معین حداکثر ۶ تکلیف اعزام می‌شود. این کار به تضمین جاری شدن پیوسته درخواست‌ها به تابع زیرین کمک می‌کند و تعداد نمونه‌های فعال و شروع‌های سرد را کاهش می‌دهد.

آزمایش کردن کارکردهای صف تکلیف

در اکثر موارد، شبیه‌ساز Cloud Functions بهترین راه برای آزمایش کردن کارکردهای صف وظیفه است. برای آشنایی با نحوه ابزاربندی کردن برنامه برای شبیه‌سازی عملکردهای صف تکلیف، به اسناد «مجموعه شبیه‌ساز» مراجعه کنید.

علاوه‌براین، توابع صف تکلیف به‌عنوان توابع ساده HTTP در Firebase Local Emulator Suite نمایان می‌شوند. می‌توانید عملکرد تکلیف شبیه‌سازی‌شده را با ارسال درخواست HTTP POST با بار داده 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

پیاده‌سازی کارکردهای صف تکلیف

استقرار تابع صف کار بااستفاده از Firebase CLI:

$ firebase deploy --only functions:backupapod

وقتی برای اولین‌بار تابع صف کار را پیاده‌سازی می‌کنید، «واسط خط فرمان» صف کاری را در Cloud Tasks با گزینه‌های (محدودیت نرخ و تلاش مجدد) مشخص‌شده در کد منبع شما ایجاد می‌کند.

اگر هنگام استقرار توابع با خطاهای اجازه مواجه شدید، مطمئن شوید که نقش‌های IAM مناسب به کاربری که فرمان‌های استقرار را اجرا می‌کند اختصاص داده شده است.

افزودن توابع صف تکلیف به صف

توابع صف وظیفه را می‌توان در Cloud Tasks از محیط سرور مطمئنی مثل Cloud Functions for Firebase بااستفاده از Firebase Admin SDK برای Node.js یا کتابخانه‌های Google Cloud برای Python در صف قرار داد. اگر با Admin SDKs آشنایی ندارید، برای شروع به کار، افزودن Firebase به سرور را ببینید.

جریان معمول تکلیف جدیدی ایجاد می‌کند، آن را در Cloud Tasks در صف قرار می‌دهد، و پیکربندی تکلیف را تنظیم می‌کند:

Node.js

exports.enqueuebackuptasks = onRequest(
    async (_request, response) => {
      const queue = getFunctions().taskQueue("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
            }),
        );
      }
      await Promise.all(enqueues);
      response.sendStatus(200);
    });

پایتون

@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")
  • کد نمونه تلاش می‌کند اجرای وظایف را با اختصاص دادن تأخیری N دقیقه‌ای برای وظیفه Nام پخش کند. این به معنای راه‌اندازی ~ ۱ کار/دقیقه است. توجه داشته باشید که اگر می‌خواهید Cloud Tasks تکلیفی را در زمان خاصی راه‌اندازی کنید، می‌توانید از scheduleTime (Node.js) یا schedule_time (Python) نیز استفاده کنید.

  • کد نمونه حداکثر زمان انتظار Cloud Tasks برای تکمیل تکلیف را تنظیم می‌کند. ‫Cloud Tasks تکلیف را براساس پیکربندی تلاش مجدد صف یا تا رسیدن به این موعد مقرر دوباره امتحان خواهد کرد. در نمونه، صف برای تلاش مجدد برای تکلیف تا ۵ بار پیکربندی شده است، اما اگر کل فرایند (شامل تلاش‌های مجدد) بیش‌از ۵ دقیقه طول بکشد، تکلیف به‌طور خودکار لغو می‌شود.

عیب‌یابی

با نحوه تشخیص مشکلات صف‌های کار بااستفاده از گزارش‌گیری و پیکربندی اجازه‌های IAM موردنیاز آشنا شوید.

روشن کردن گزارش‌گیری Cloud Tasks

گزارش‌های Cloud Tasks حاوی اطلاعات تشخیصی مفیدی مانند وضعیت درخواست مرتبط با یک تکلیف است. به‌طور پیش‌فرض، گزارش‌های Cloud Tasks به‌دلیل حجم زیاد گزارش‌هایی که می‌تواند به‌طور بالقوه در پروژه شما تولید کند خاموش است. توصیه می‌کنیم وقتی فعالانه درحال توسعه و اشکال‌زدایی کردن توابع صف وظایف خود هستید، گزارش‌های اشکال‌زدایی را روشن کنید. ببینید روشن کردن گزارش‌گیری.

اجازه‌های IAM

ممکن است هنگام صف‌بندی کردن تکلیف‌ها یا زمانی که Cloud Tasks تلاش می‌کند توابع صف تکلیف شما را فراخوانی کند، با PERMISSION DENIED خطا مواجه شوید. مطمئن شوید پروژه شما پیوندهای IAM زیر را دارد:

  • هویتی که برای صف‌بندی کردن وظایف در Cloud Tasks استفاده می‌شود باید اجازه cloudtasks.tasks.create «مدیریت دسترسی و هویت» را داشته باشد.

    در نمونه، این App Engine حساب سرویس پیش‌فرض است

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 مراجعه کنید.

  • هویت استفاده‌شده برای راه‌اندازی تابع صف کارها به اجازه cloudfunctions.functions.invoke نیاز دارد.

    در نمونه، این App Engine حساب سرویس پیش‌فرض است

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