ترقية دوال Node.js من الجيل الأول إلى الجيل الثاني

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

تفترض الأمثلة الواردة في هذه الصفحة أنّك تستخدم JavaScript مع وحدات CommonJS (عمليات استيراد نمط require)، ولكن تنطبق المبادئ نفسها على JavaScript مع ESM (عمليات استيراد نمط import … from) وTypeScript.

عملية النقل

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

التحقّق من إصدار واجهة سطر الأوامر (CLI) في Firebase وإصدارات firebase-function

يجب استخدام على الأقل الإصدار 12.00 من واجهة سطر الأوامر في Firebase وكذلك الإصدار 4.3.0 من firebase-functions. تتوافق أي إصدارات أحدث مع الجيل الثاني والجيل الأول.

تعديل عمليات الاستيراد

يتم استيراد دوال الجيل الثاني من الحزمة الفرعية v2 في حزمة تطوير البرامج (SDK) firebase-functions. وهذا مسار الاستيراد المختلف هو كل ما يحتاج إليه واجهة سطر الأوامر في Firebase لتحديد ما إذا كان سيتم نشر رمز الدالة كدالة من الجيل الأول أو الثاني.

حزمة v2 الفرعية مفصَّلة، وننصحك باستيراد الوحدة المحدّدة التي تحتاجها فقط.

قبل: الجيل الأول

const functions = require("firebase-functions");

بعد: الجيل الثاني

// explicitly import each trigger
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

تعديل تعريفات المشغِّل

بما أنّ الجيل الثاني من حزمة تطوير البرامج (SDK) يفضّل عمليات الاستيراد الوحداتية، يجب تعديل تعريفات المشغّلات لتعكس عمليات الاستيراد التي تم تغييرها من الخطوة السابقة.

تم تغيير الوسيطات التي تم تمريرها إلى استدعاءات بعض المشغلات. في هذا المثال، لاحِظ أنّه تم دمج الوسائط في استدعاء الدالة onDocumentCreated في عنصر event واحد. بالإضافة إلى ذلك، تتضمّن بعض المشغِّلات ميزات ضبط جديدة مناسبة، مثل خيار cors لمشغِّل onRequest.

قبل: الجيل الأول

const functions = require("firebase-functions");

exports.date = functions.https.onRequest((req, res) => {
  // ...
});

exports.uppercase = functions.firestore
  .document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

بعد: الجيل الثاني

const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

exports.date = onRequest({cors: true}, (req, res) => {
  // ...
});

exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  /* ... */
});

استخدام الإعدادات المَعلَمة

بالنسبة إلى وظائف الجيل الثاني، لم تتوافق مع functions.config لصالح واجهة أكثر أمانًا لتحديد مَعلمات الإعداد بشكل صريح داخل قاعدة الرموز البرمجية. باستخدام وحدة params الجديدة، سيحظر واجهة سطر الأوامر (CLI) النشر ما لم تكن جميع المَعلمات ذات قيمة صالحة، ما يضمن عدم نشر الدالة بدون ضبط الإعدادات.

نقل البيانات إلى حزمة "params" الفرعية

إذا كنت تستخدم إعدادات البيئة من خلال functions.config، يمكنك نقل الإعدادات الحالية إلى الإعداد باستخدام مَعلمات.

قبل: الجيل الأول

const functions = require("firebase-functions");

exports.date = functions.https.onRequest((req, res) => {
  const date = new Date();
  const formattedDate =
date.toLocaleDateString(functions.config().dateformat);

  // ...
});

بعد: الجيل الثاني

const {onRequest} = require("firebase-functions/v2/https");
const {defineString} = require("firebase-functions/params");

const dateFormat = defineString("DATE_FORMAT");

exports.date = onRequest((req, res) => {
  const date = new Date();
  const formattedDate = date.toLocaleDateString(dateFormat.value());

  // ...
});

ضبط قيم المَعلمات

في أول مرة يتم النشر، يطلب واجهة سطر الأوامر في Firebase عرض جميع قيم المعلّمات، ثم احفظ القيم في ملف dotenv. لتصدير قيم functions.config، شغِّل firebase functions:config:export.

لتوفير مزيد من الأمان، يمكنك أيضًا تحديد أنواع المَعلمات وقواعد التحقّق.

حالة خاصة: مفاتيح واجهة برمجة التطبيقات

تتكامل وحدة params مع Cloud Secret Manager الذي يوفّر التحكّم الدقيق في الوصول إلى القيم الحسّاسة مثل مفاتيح واجهة برمجة التطبيقات. راجِع المعلَمات السرية للحصول على مزيد من المعلومات.

قبل: الجيل الأول

const functions = require("firebase-functions");

exports.getQuote = functions.https.onRequest(async (req, res) => {
  const quote = await fetchMotivationalQuote(functions.config().apiKey);
  // ...
});

بعد: الجيل الثاني

const {onRequest} = require("firebase-functions/v2/https");
const {defineSecret} = require("firebase-functions/params");

// Define the secret parameter
const apiKey = defineSecret("API_KEY");

exports.getQuote = onRequest(
  // make the secret available to this function
  { secrets: [apiKey] },
  async (req, res) => {
    // retrieve the value of the secret
    const quote = await fetchMotivationalQuote(apiKey.value());
    // ...
  }
);

ضبط خيارات بيئة التشغيل

تم تغيير إعدادات خيارات وقت التشغيل بين الجيل الأول والجيل الثاني، كما يضيف الجيل الثاني إمكانية جديدة لضبط الخيارات لجميع الدوال.

قبل: الجيل الأول

const functions = require("firebase-functions");

exports.date = functions
  .runWith({
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  })
  // locate function closest to users
  .region("asia-northeast1")
  .https.onRequest((req, res) => {
    // ...
  });

exports.uppercase = functions
  // locate function closest to users and database
  .region("asia-northeast1")
  .firestore.document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

بعد: الجيل الثاني

const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions/v2");

// locate all functions closest to users
setGlobalOptions({ region: "asia-northeast1" });

exports.date = onRequest({
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  }, (req, res) => {
  // ...
});

exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  /* ... */
});

استخدام التزامن

من المزايا المهمة لدوال الجيل الثاني قدرة مثيل دالة واحدة على عرض أكثر من طلب في وقت واحد. يمكن أن يقلل ذلك بشكل كبير من عدد عمليات البدء على البارد التي يواجهها المستخدمون النهائيون. بشكل افتراضي، يتم تعيين التزامن على 80، ولكن يمكنك تعيينه على أي قيمة من 1 إلى 1000:

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // set concurrency value
    concurrency: 500
  },
  (req, res) => {
    // ...
});

يمكن أن يؤدي ضبط التزامن إلى تحسين الأداء وتقليل تكلفة الوظائف. يمكنك الاطّلاع على مزيد من المعلومات حول التوافق في مقالة السماح بالطلبات المتزامنة.

تدقيق استخدام المتغير العمومي

قد تستخدم دوال الجيل الأول المكتوبة بدون تزامن متغيرات عمومية يتم ضبطها وقراءتها لكل طلب. عند تفعيل التزامن ويبدأ مثيل واحد في معالجة طلبات متعددة في آنٍ واحد، قد تظهر أخطاء في الوظيفة عندما تبدأ الطلبات المتزامنة في ضبط المتغيرات العامة وقراءتها بشكل متزامن.

أثناء الترقية، يمكنك ضبط وحدة المعالجة المركزية (CPU) للوظيفة على gcf_gen1 وضبط concurrency على 1 لاستعادة سلوك الجيل الأول:

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // TEMPORARY FIX: remove concurrency
    cpu: "gcf_gen1",
    concurrency: 1
  },
  (req, res) => {
    // ...
});

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

نقل الزيارات إلى وظائف الجيل الثاني الجديدة

مثلما يحدث عند تغيير منطقة الدالة أو نوع المشغِّل، عليك منح دالة الجيل الثاني اسمًا جديدًا ونقل الزيارات إليها ببطء.

لا يمكن ترقية دالة من الجيل الأول إلى الجيل الثاني بالاسم نفسه وتنفيذ firebase deploy. سيؤدي إجراء ذلك إلى حدوث الخطأ:

Upgrading from GCFv1 to GCFv2 is not yet supported. Please delete your old function or wait for this feature to be ready.

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

  1. أعد تسمية الدالة في رمز الدوال. على سبيل المثال، أعِد تسمية resizeImage إلى resizeImageSecondGen.
  2. انشر الدالة حتى تعمل وظيفة الجيل الأول الأصلية ودالة الجيل الثاني.
    1. في حال مشغِّلات HTTP و"قائمة انتظار المهام" و"القابلة للاستدعاء"، ابدأ بتوجيه جميع العملاء إلى دالة الجيل الثاني من خلال تعديل رمز العميل باستخدام اسم أو عنوان URL لدالة الجيل الثاني.
    2. من خلال عمليات التشغيل في الخلفية، ستستجيب دالتا الجيل الأول والجيل الثاني لكل حدث فور النشر.
  3. عند إيقاف تشغيل جميع الزيارات، احذف دالة الجيل الأول باستخدام أمر firebase functions:delete في firebase CLI.
    1. اختياريًا، يمكنك إعادة تسمية دالة الجيل الثاني لتطابق اسم دالة الجيل الأول.