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


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

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

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

حزمة تطوير البرامج (SDK) لمنصّة Firebase for Cloud Functions في الإصدار 3.20.1 والإصدارات الأحدث من Firebase مع الإصدار 10.2.0 أو الإصدارات الأحدث من حزمة تطوير البرامج (SDK) لمشرف Firebase لدعم وظائف قائمة انتظار المهام.

يمكن أن يؤدي استخدام وظائف قائمة انتظار المهام مع Firebase إلى تحصيل رسوم جارٍ معالجة "مهام Google" في السحابة الإلكترونية. عرض أسعار Cloud Tasks لمزيد من المعلومات.

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

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

  1. اكتب دالة قائمة انتظار المهام باستخدام حزمة تطوير البرامج (SDK) لمنصّة Firebase لوظائف السحابة الإلكترونية.
  2. اختبِر الدالة من خلال تشغيلها باستخدام طلب HTTP.
  3. انشُر الدالة باستخدام واجهة سطر الأوامر في Firebase. عند نشر مهمتك لأول مرة، سيقوم CLI بإنشاء قائمة انتظار المهام في Cloud Tasks مع تحديد الخيارات (تحديد المعدل وإعادة المحاولة) في رمز المصدر.
  4. إضافة مهام إلى قائمة انتظار المهام التي تم إنشاؤها حديثًا، وتمرير المعلمات لإعداد جدول التنفيذ إذا لزم الأمر. يمكنك تحقيق ذلك عن طريق كتابة التعليمات البرمجية باستخدام SDK للمشرف ونشره في Cloud Functions for Firebase.

كتابة دوال قائمة المهام

تستند عيّنات التعليمات البرمجية في هذا القسم إلى تطبيق يضبط خدمة تحتفظ بنسخة احتياطية من جميع الصور من وكالة ناسا صورة اليوم الفلكية. للبدء، عليك استيراد الوحدات المطلوبة:

Node.js

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

// Dependencies for image backup.
const path = require("path");
const fetch = require("node-fetch");
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 مهام على الأكثر في في أي وقت. ويساعد هذا في ضمان تدفق ثابت للطلبات إلى المصدر وتساعد في تقليل عدد الحالات النشطة وعمليات التشغيل على البارد.

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

عرض وظائف قائمة المهام في "مجموعة أدوات المحاكاة المحلية من Firebase" ببساطة دوال HTTP. يمكنك اختبار دالة تمت محاكاتها عن طريق إرسال طلب 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:

$ firebase deploy --only functions:backupapod

عند نشر دالة قائمة انتظار مهام لأول مرة، ينشئ واجهة سطر الأوامر قائمة انتظار المهام في Cloud Tasks مع خيارات (تحديد المعدّل وإعادة المحاولة) المحددة في رمز المصدر.

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

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

يمكن إدراج دوال قائمة المهام في قائمة انتظار المهام في Cloud Tasks من خادم مثل Cloud Functions for Firebase باستخدام حزمة SDK للمشرف في Firebase مكتبات Node.js أو Google Cloud للغة بايثون. إذا كنت مستخدمًا جديدًا لحزم SDK للمشرف، يُرجى مراجعة يمكنك إضافة Firebase إلى خادم للبدء.

ينشئ التدفق النموذجي مهمة جديدة، ويدرجها في قائمة الانتظار "مهام Google" وضبط إعدادات المهمة:

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")
  • يحاول الرمز النموذجي نشر تنفيذ المهام من خلال الارتباط بتأخير الدقائق العددية للمهمة ترتيب. هذا النمط ويترجم إلى تشغيل حوالي مهمة واحدة في الدقيقة. لاحظ أنه يمكنك أيضًا استخدام scheduleTime (Node.js) أو schedule_time (Python) إذا أردت خدمة "مهام Google" لتشغيل مهمة في وقت محدّد.

  • يحدّد رمز النموذج الحدّ الأقصى لمقدار الوقت ستنتظر خدمة "مهام Google" في السحابة الإلكترونية. لإنجاز مهمة ما. ستتم إعادة المحاولة من خلال "مهام Google" الْمَهَمَّة بَعْدَ إِعَادَةِ الْمُحَاوَلَة تهيئة قائمة الانتظار أو حتى الوصول إلى هذا الموعد النهائي. في العينة، تهيئة قائمة الانتظار لإعادة محاولة تنفيذ المهمة حتى 5 مرات، ولكن المهمة يتم إلغاؤها تلقائيًا إذا كانت العملية برمتها (بما في ذلك محاولات إعادة المحاولة) يستغرق أكثر من 5 دقائق

استرداد عنوان URI المستهدف وتضمينه

بسبب الطريقة التي تنشئ بها خدمة "مهام Cloud" رموزًا مميّزة للمصادقة من أجل المصادقة إلى وظائف قائمة انتظار المهام الأساسية، فيجب تحديد عنوان URL لتشغيل السحابة الإلكترونية للدالة عند إدراج المهام في "قائمة المحتوى التالي" أر ننصحك باسترداد عنوان URL لوظيفتك آليًا كما هو موضح أدناه:

Node.js

/**
 * Get the URL of a given v2 cloud function.
 *
 * @param {string} name the function's name
 * @param {string} location the function's location
 * @return {Promise<string>} The URL of the function
 */
async function getFunctionUrl(name, location="us-central1") {
  if (!auth) {
    auth = new GoogleAuth({
      scopes: "https://www.googleapis.com/auth/cloud-platform",
    });
  }
  const projectId = await auth.getProjectId();
  const url = "https://cloudfunctions.googleapis.com/v2beta/" +
    `projects/${projectId}/locations/${location}/functions/${name}`;

  const client = await auth.getClient();
  const res = await client.request({url});
  const uri = res.data?.serviceConfig?.uri;
  if (!uri) {
    throw new Error(`Unable to retreive uri for function at ${url}`);
  }
  return uri;
}

Python

def get_function_url(name: str, location: str = SupportedRegion.US_CENTRAL1) -> str:
    """Get the URL of a given v2 cloud function.

    Params:
        name: the function's name
        location: the function's location

    Returns: The URL of the function
    """
    credentials, project_id = google.auth.default(
        scopes=["https://www.googleapis.com/auth/cloud-platform"])
    authed_session = AuthorizedSession(credentials)
    url = ("https://cloudfunctions.googleapis.com/v2beta/" +
           f"projects/{project_id}/locations/{location}/functions/{name}")
    response = authed_session.get(url)
    data = response.json()
    function_url = data["serviceConfig"]["uri"]
    return function_url

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

تفعيل تسجيل "مهام Google" في السحابة الإلكترونية

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

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

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

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

    في النموذج، يكون هذا هو حساب الخدمة التلقائي في 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