نصائح

يوضّح هذا المستند أفضل الممارسات لتصميم Cloud Functions وتنفيذها واختبارها ونشرها.

الصحة

يصف هذا القسم أفضل الممارسات العامة لتصميم Cloud Functions وتنفيذه.

كتابة دوالّ idempotent

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

عدم بدء الأنشطة في الخلفية

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

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

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

حذف الملفات المؤقتة دائمًا

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

يمكنك الاطّلاع على الذاكرة المستخدَمة من خلال دالة فردية من خلال اختيارها في قائمة الدوالّ في وحدة تحكّم Google Cloud واختيار الرسم البياني استخدام الذاكرة.

لا تحاول الكتابة خارج الدليل المؤقت، واحرص على استخدام methodsindependent من النظام الأساسي أو نظام التشغيل لإنشاء مسارات الملفات.

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

إطار عمل الدوالّ

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

لإجراء ذلك، أدرِج الإصدار المفضّل لديك في ملف القفل ذي الصلة (على سبيل المثال، package-lock.json لنظام التشغيل Node.js أو requirements.txt لنظام التشغيل Python).

الأدوات

يوفّر هذا القسم إرشادات حول كيفية استخدام الأدوات لتنفيذ Cloud Functions واختبارها والتفاعل معها.

التطوير المحلي

يستغرق نشر الدالة بعض الوقت، لذا يكون اختبار رمز الدالة محليًا أسرع في أغلب الأحيان.

يمكن لمطوّري Firebase استخدام محاكي Cloud Functions سطر أوامر Firebase.

استخدام Sendgrid لإرسال الرسائل الإلكترونية

لا يسمح Cloud Functions بالاتصالات الصادرة على المنفذ 25، لذا لا يمكنك إجراء اتصالات غير آمنة بخادم SMTP. الطريقة المقترَحة لإرسال الرسائل الإلكترونية هي استخدام SendGrid. يمكنك العثور على خيارات أخرى لإرسال الرسائل الإلكترونية في الدليل التمهيدي إرسال الرسائل الإلكترونية من إحدى النُسخ لخدمة Google Compute Engine.

الأداء

يصف هذا القسم أفضل الممارسات لتحسين الأداء.

استخدام التبعيات بحكمة

وبما أنّ الدوالّ لا تعتمد على الحالة، غالبًا ما يتمّ بدء بيئة التنفيذ من الصفر (أثناء ما يُعرف باسم التشغيل البارد). عند حدوث بداية باردة، يتم تقييم السياق العام للدالة.

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

استخدام المتغيّرات العامة لإعادة استخدام العناصر في عمليات الاستدعاء المستقبلية

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

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

Node.js

console.log('Global scope');
const perInstance = heavyComputation();
const functions = require('firebase-functions');

exports.function = functions.https.onRequest((req, res) => {
  console.log('Function invocation');
  const perFunction = lightweightComputation();

  res.send(`Per instance: ${perInstance}, per function: ${perFunction}`);
});

Python

import time

from firebase_functions import https_fn

# Placeholder
def heavy_computation():
  return time.time()

# Placeholder
def light_computation():
  return time.time()

# Global (instance-wide) scope
# This computation runs at instance cold-start
instance_var = heavy_computation()

@https_fn.on_request()
def scope_demo(request):

  # Per-function scope
  # This computation runs every time this function is called
  function_var = light_computation()
  return https_fn.Response(f"Instance: {instance_var}; function: {function_var}")
  

تأخذ دالة HTTP هذه عنصر طلب (flask.Request)، وتُعيد نص الاستجابة أو أي مجموعة من القيم التي يمكن تحويلها إلى عنصر Response باستخدام make_response.

من المهم بشكل خاص تخزين عمليات ربط الشبكة ومراجع المكتبة وعناصر عملاء واجهات برمجة التطبيقات في النطاق العام. اطّلِع على تحسين الشبكة للاطّلاع على أمثلة.

إجراء إعداد بطيء للمتغيّرات العامة

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

Node.js

const functions = require('firebase-functions');
let myCostlyVariable;

exports.function = functions.https.onRequest((req, res) => {
  doUsualWork();
  if(unlikelyCondition()){
      myCostlyVariable = myCostlyVariable || buildCostlyVariable();
  }
  res.status(200).send('OK');
});

Python

from firebase_functions import https_fn

# Always initialized (at cold-start)
non_lazy_global = file_wide_computation()

# Declared at cold-start, but only initialized if/when the function executes
lazy_global = None

@https_fn.on_request()
def lazy_globals(request):

  global lazy_global, non_lazy_global

  # This value is initialized only if (and when) the function is called
  if not lazy_global:
      lazy_global = function_specific_computation()

  return https_fn.Response(f"Lazy: {lazy_global}, non-lazy: {non_lazy_global}.")
  

تستخدِم وظيفة HTTP هذه متغيّرات عالمية يتمّ إعدادها بشكلٍ كسول. تأخذ هذه الدالة كائن طلب (flask.Request)، وتعرض نص الاستجابة أو أي مجموعة من القيم التي يمكن تحويلها إلى كائن Response باستخدام make_response.

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

تقليل عمليات التشغيل على البارد من خلال ضبط الحد الأدنى لعدد النُسخ

بشكلٍ تلقائي، يُعدِّل Cloud Functions عدد النُسخ استنادًا إلى عدد الطلبات الواردة. يمكنك تغيير هذا السلوك التلقائي من خلال ضبط الحد الأدنى لعدد النُسخ التي يجب أن يبقيها Cloud Functions جاهزة ل معالجة الطلبات. يؤدي ضبط الحد الأدنى لعدد النُسخ إلى تقليل عمليات التشغيل المتأخّر لتطبيقك. ننصحك بضبط الحد الأدنى لعدد النُسخ إذا كان تطبيقك حسّاسًا للوقت المستغرَق في إرسال البيانات.

اطّلِع على التحكّم في سلوك التوسيع للحصول على مزيد من المعلومات عن خيارات وقت التشغيل هذه.

مراجع إضافية

اطّلِع على مزيد من المعلومات عن تحسين الأداء في فيديو "أداء Google Cloud Atlas" Cloud Functions وقت بدء التشغيل البارد.