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

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

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

برای کاهش این پیچیدگی، می‌توانید تابع صف تکلیفی بنویسید که گزینه‌های تکلیف پایه مانند 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 مستقر کنید. هنگام استقرار تابع صف تکلیف برای اولین‌بار، 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

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

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

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

توابع صف وظیفه را می‌توان در Cloud Tasks از یک محیط سرور مطمئن مانند Cloud Functions for Firebase بااستفاده از Firebase Admin SDK برای Node.js یا کتابخانه‌های Google Cloud برای Python در صف قرار داد. اگر با Admin SDK آشنایی ندارید، برای شروع به افزودن 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 به اجازه IAM 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