نصائح

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

التصحيح

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

كتابة الدوال غير الثابتة

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

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

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

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

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

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

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

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

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

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

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

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

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

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 مدة بدء التشغيل البارد.