يوضّح هذا المستند أفضل الممارسات لتصميم Cloud Functions وتنفيذها واختبارها ونشرها.
الصحة
يصف هذا القسم أفضل الممارسات العامة لتصميم Cloud Functions وتنفيذه.
كتابة دوالّ idempotent
من المفترض أن تؤدي دوالّك إلى النتيجة نفسها حتى إذا تمّت دعوتها لعدة مرّات. يتيح لك ذلك إعادة محاولة طلب البيانات إذا تعذّر الطلب السابق في مرحلة ما من الرمز البرمجي. لمزيد من المعلومات، يُرجى الاطّلاع على إعادة محاولة الدوالّ المستندة إلى الأحداث.
عدم بدء الأنشطة في الخلفية
النشاط في الخلفية هو أيّ شيء يحدث بعد انتهاء وظيفتك.
ينتهي استدعاء الدالة بعد أن تعرض الدالة قيمة أو تشير بطريقة أخرى إلى اكتمالها، مثل استدعاء الوسيطة callback
في الدوالّ المستندة إلى الأحداث في Node.js. لا يمكن لأي رمز يتم تشغيله بعد الإنهاء الهادئ الوصول إلى وحدة المعالجة المركزية
ولن يحقّق أي تقدّم.
بالإضافة إلى ذلك، عند تنفيذ طلب لاحق في البيئة نفسها، تتم استعادة نشاطك في الخلفية، ما يتداخل مع الطلب الجديد. وقد يؤدي ذلك
إلى حدوث سلوك غير متوقّع وأخطاء يصعب تشخيصها. يؤدي الوصول إلى
الشبكة بعد انتهاء إحدى الوظائف عادةً إلى إعادة ضبط عمليات الربط
(رمز الخطأ ECONNRESET
).
يمكن غالبًا رصد النشاط في الخلفية في السجلات من عمليات الاستدعاء الفردية، من خلال العثور على أي شيء يتم تسجيله بعد السطر الذي يشير إلى أنّ عملية الاستدعاء انتهت. يمكن أحيانًا أن يكون النشاط في الخلفية مدفونًا في رمز التطبيق بشكل أعمق، خاصةً عند تنفيذ عمليات غير متزامنة، مثل عمليات الاستدعاء أو الموقّتات. راجِع الرمز البرمجي للتأكّد من انتهاء جميع العمليات غير المتزامنة قبل إنهاء الدالة.
حذف الملفات المؤقتة دائمًا
مساحة التخزين على القرص المحلي في الدليل المؤقت هي نظام ملفات في الذاكرة. تستهلك الملفات التي تكتبها الذاكرة المتوفّرة لوظيفتك، وتبقى أحيانًا مخزّنة بين عمليات الاستدعاء. قد يؤدي عدم حذف هذه الملفات صراحةً إلى ظهور خطأ "تعذّر تحميل البيانات إلى الذاكرة" وبدء تشغيل بارد لاحقًا.
يمكنك الاطّلاع على الذاكرة المستخدَمة من خلال دالة فردية من خلال اختيارها في قائمة الدوالّ في وحدة تحكّم Google Cloud واختيار الرسم البياني استخدام الذاكرة.
إذا كنت بحاجة إلى الوصول إلى مساحة تخزين طويلة المدى، ننصحك باستخدام Cloud Run عمليات ربط الوحدات باستخدام Cloud Storage أو وحدات NFS.
يمكنك تقليل متطلبات الذاكرة عند معالجة الملفات الأكبر حجمًا باستخدام ميزة "المعالجة المخطّط لها". على سبيل المثال، يمكنك معالجة ملف على Cloud Storage من خلال إنشاء بث للقراءة، وإجراؤه من خلال عملية مستندة إلى البث، وكتابة بث الإخراج مباشرةً إلى Cloud Storage.
إطار عمل الدوالّ
لضمان تثبيت التبعيات نفسها بشكلٍ متسق في جميع البيئات، ننصحك بتضمين مكتبة Functions Framework في مدير الحِزم وتثبيت التبعية على إصدار محدّد من Functions Framework.
لإجراء ذلك، أدرِج الإصدار المفضّل لديك في ملف القفل ذي الصلة (على سبيل المثال،
package-lock.json
لنظام التشغيل Node.js أو requirements.txt
لنظام التشغيل Python).
إذا لم يتم إدراج إطار عمل Functions Framework صراحةً كعنصر تابع، سيتم إضافته تلقائيًا أثناء عملية الإنشاء باستخدام أحدث إصدار متاح.
الأدوات
يوفّر هذا القسم إرشادات حول كيفية استخدام الأدوات لتنفيذ 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 وقت بدء التشغيل البارد.