إدراج الدوال في قائمة انتظار مع "مهام Cloud"

تستفيد دوال قائمة انتظار المهام من Google Cloud Tasks لمساعدة تطبيقك في تنفيذ المهام التي تستغرق وقتًا طويلاً أو تستهلك الكثير من الموارد أو تستخدم نطاقًا تردديًا محدودًا بشكل غير متزامن وخارج مسار التطبيق الرئيسي.

على سبيل المثال، لنفترض أنّك تريد إنشاء نُسخ احتياطية من مجموعة كبيرة من ملفات الصور المستضافة حاليًا على واجهة برمجة تطبيقات (API) مع حدّ أقصى لمعدّل الطلبات. لكي تكون مستهلكًا مسؤولاً لواجهة برمجة التطبيقات هذه، عليك الالتزام بالحدود القصوى لمعدّل الطلبات. بالإضافة إلى ذلك، قد يكون هذا النوع من الوظائف الطويلة عُرضة للفشل بسبب مهلات الانتظار وحدود الذاكرة.

للتخفيف من هذه المشكلة المعقّدة، يمكنك كتابة دالة قائمة انتظار مهام تضبط خيارات المهام الأساسية مثل scheduleTime وdispatchDeadline، ثم تنقل الدالة إلى قائمة انتظار في Cloud Tasks. تم تصميم بيئة Cloud Tasks خصيصًا لضمان التحكّم الفعّال في الازدحام و سياسات إعادة المحاولة لهذه الأنواع من العمليات.

تعمل حزمة Firebase SDK لـ Cloud Functions for Firebase والإصدار 3.20.1 والإصدارات الأحدث مع Firebase Admin SDK والإصدار 10.2.0 والإصدارات الأحدث لدعم دوال قائمة انتظار المهام.

قد يؤدي استخدام دوال قائمة انتظار المهام مع Firebase إلى فرض رسوم على Cloud Tasks المعالجة. يمكنك الاطّلاع على Cloud Tasks الأسعار لمزيد من المعلومات.

إنشاء دوال قائمة انتظار المهام

لاستخدام دوال قائمة انتظار المهام، اتّبِع سير العمل التالي:

  1. اكتب دالة قائمة انتظار مهام باستخدام حزمة Firebase SDK لـ 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 {getFunctions} = require("firebase-admin/functions");
const {logger} = require("firebase-functions");

// Dependencies for image backup.
const path = require("path");
const {initializeApp} = require("firebase-admin/app");
const {getStorage} = require("firebase-admin/storage");
const {GoogleAuth} = require("google-auth-library");

Python

# 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) => {

Python

@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: تتم إعادة محاولة تنفيذ كل مهمة في قائمة انتظار المهام تلقائيًا 5 مرات على الأكثر. يساعد ذلك في التخفيف من الأخطاء المؤقتة، مثل أخطاء الشبكة أو الانقطاع المؤقت للخدمة الخارجية التابعة.

  • retryConfig.minBackoffSeconds=60: تتم إعادة محاولة تنفيذ كل مهمة بعد 60 ثانية على الأقل من كل محاولة. يوفر ذلك فترة كبيرة بين كل محاولة حتى لا نُسرع في استنفاد محاولات إعادة المحاولة الخمس بسرعة كبيرة.

  • rateLimits.maxConcurrentDispatch=6: يتم إرسال 6 مهام على الأكثر في وقت معيّن. يساعد ذلك في ضمان تدفق ثابت للطلبات إلى الدالة الأساسية ويساعد في تقليل عدد النُسخ النشطة وعمليات التشغيل على البارد.

اختبار دوال قائمة انتظار المهام

في معظم الحالات، يكون محاكي Cloud Functions أفضل طريقة لاختبار دوال قائمة انتظار المهام. يمكنك الاطّلاع على مستندات Emulator Suite للتعرّف على كيفية إعداد تطبيقك لمحاكاة دوال قائمة انتظار المهام.

بالإضافة إلى ذلك، يتم عرض دوال قائمة انتظار المهام كدوال 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 مع الخيارات (الحدّ الأقصى لمعدّل الطلبات وإعادة المحاولة) المحدّدة في رمز المصدر.

إذا واجهتك أخطاء في الأذونات عند نشر الدوال، تأكَّد من أنّ الـ أدوار المناسبة لإدارة الهوية وإمكانية الوصول مُسنَدة إلى المستخدم الذي يُشغّل أوامر النشر.

وضع دوال قائمة انتظار المهام في قائمة الانتظار

يمكن وضع دوال قائمة انتظار المهام في قائمة الانتظار في 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 targetUri = await getFunctionUrl("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
              uri: targetUri,
            }),
        );
      }
      await Promise.all(enqueues);
      response.sendStatus(200);
    });

Python

@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. ويؤدي ذلك إلى تشغيل مهمة واحدة تقريبًا في الدقيقة. يُرجى العِلم أنّه يمكنك أيضًا استخدام scheduleTime (في Node.js) أو schedule_time (في Python) إذا أردت أن تشغّل Cloud Tasks مهمة في وقت معيّن.

  • يضبط نموذج الرمز البرمجي الحد الأقصى للوقت الذي ستنتظره Cloud Tasks لإكمال مهمة. ستعيد Cloud Tasks محاولة تنفيذ المهمة وفقًا لإعدادات إعادة المحاولة في قائمة الانتظار أو إلى أن يتم بلوغ هذا الموعد النهائي. في النموذج، تم ضبط قائمة الانتظار على إعادة محاولة تنفيذ المهمة 5 مرات على الأكثر، ولكن يتم إلغاء المهمة تلقائيًا إذا استغرقت العملية بأكملها (بما في ذلك محاولات إعادة المحاولة) أكثر من 5 دقائق.

تحديد المشاكل وحلّها

تفعيل تسجيل Cloud Tasks

تحتوي السجلّات من Cloud Tasks على معلومات تشخيصية مفيدة، مثل الـ حالة الطلب المرتبط بمهمة. تكون السجلّات من Cloud Tasks غير مفعّلة تلقائيًا بسبب الحجم الكبير من السجلّات التي يمكن أن تنشئها في مشروعك. ننصحك بتفعيل سجلّات تصحيح الأخطاء أثناء تطوير دوال قائمة انتظار المهام وتصحيح أخطائها بنشاط. يمكنك الاطّلاع على مقالة تفعيل التسجيل.

أذونات "إدارة الهوية وإمكانية الوصول"

قد تظهر لك أخطاء PERMISSION DENIED عند وضع المهام في قائمة الانتظار أو عندما Cloud Tasks تحاول استدعاء دوال قائمة انتظار المهام. تأكَّد من أنّ مشروعك يتضمّن عمليات الربط التالية في إدارة الهوية وإمكانية الوصول:

  • تحتاج الهوية المستخدَمة لوضع المهام في قائمة الانتظار إلى إذن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 حساب الخدمة التلقائي.

يمكنك الاطّلاع على مستندات Google Cloud IAM للحصول على تعليمات حول كيفية إضافة حساب الخدمة التلقائي App Engine كمستخدم لحساب الخدمة التلقائي App Engine.

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