يوضّح هذا المستند أفضل الممارسات لتصميم 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
.
من المهم بشكل خاص تخزين عمليات ربط الشبكة ومراجع المكتبة وعناصر عملاء واجهات برمجة التطبيقات في النطاق العام. اطّلِع على تحسين الاتصال بالشبكة للاطّلاع على أمثلة.
تقليل عمليات التشغيل على البارد من خلال ضبط الحد الأدنى لعدد النُسخ
تعمل Cloud Functions تلقائيًا على زيادة عدد النُسخ استنادًا إلى عدد الطلبات الواردة. يمكنك تغيير هذا السلوك التلقائي من خلال ضبط قاعدة تحديد الحد الأدنى لعدد النُسخ التي يجب أن تبقيها Cloud Functions جاهزة لمعالجة الطلبات. يؤدي ضبط الحد الأدنى لعدد النُسخ إلى تقليل عمليات التشغيل المتأخّر لتطبيقك. ننصحك بتحديد الحد الأدنى لعدد النُسخ، و completing initialization at load time (إكمال عملية الإعداد في وقت التحميل)، إذا كان تطبيقك حسّاسًا للوقت المستغرَق في إرسال البيانات.
اطّلِع على التحكّم في سلوك التوسيع للحصول على مزيد من المعلومات عن خيارات وقت التشغيل هذه.ملاحظات حول التشغيل على البارد وعمليات الإعداد
تحدث عملية الإعداد الشاملة في وقت التحميل. بدون ذلك، يجب أن يُكمل الطلب الأول عملية الإعداد وتحميل الوحدات، ما يؤدي إلى زيادة وقت الاستجابة.
ومع ذلك، يؤثر أيضًا الإعداد العام في عمليات التشغيل المُجرّد. للحدّ من هذا التأثير، عليك إعداد ما هو مطلوب فقط للطلب الأول، وذلك للحفاظ على وقت استجابة الطلب الأول على أدنى مستوى ممكن.
ويُعدّ ذلك مهمًا بشكل خاص إذا أعددت الحد الأدنى من النُسخ كما هو описан أعلاه لوظيفة حسّاسة للوقت الاستجابة. في هذا السيناريو، يضمن إكمال عملية الإعداد في وقت التحميل وتخزين البيانات المفيدة في ذاكرة التخزين المؤقت عدم الحاجة إلى تنفيذ الطلب الأول ويتم عرضه بوقت استجابة منخفض.
في حال بدء متغيّرات في النطاق العام، استنادًا إلى اللغة، يمكن أن تؤدي مدّة البدء الطويلة إلى سلوكَين: - في بعض اللغات والمكتبات غير المتزامنة، يمكن تنفيذ إطار عمل الدوالّ غير المتزامنة والرجوع فورًا، ما يؤدي إلى مواصلة تنفيذ الرمز البرمجي في الخلفية، ما قد يتسبب في مشاكل مثل عدم التمكّن من الوصول إلى وحدة المعالجة المركزية. لتجنّب ذلك، يجب حظر التحميل عند بدء تشغيل الوحدة كما هو موضّح أدناه. ويضمن ذلك أيضًا عدم عرض الطلبات إلى أن تكتمل عملية الإعداد. - من ناحية أخرى، إذا كانت عملية الإعداد متزامنة، سيؤدي وقت الإعداد الطويل إلى زيادة وقت بدء التشغيل، ما قد يشكّل مشكلة خاصةً مع وظائف تعدد المهام المنخفضة أثناء الارتفاعات المفاجئة في الحمل.
مثال على تسخين مكتبة node.js غير المتزامنة مسبقًا
يُعدّ Node.js مع Firestore مثالاً على مكتبة node.js غير المتزامنة. للاستفادة من min_instances، يُكمِل الرمز البرمجي التالي عملية التحميل والإعداد في وقت التحميل، ما يؤدي إلى حظر تحميل الوحدة.
يتم استخدام TLA، ما يعني أنّ ES6 مطلوب، باستخدام إضافة .mjs
لرمز node.js أو إضافة type: module
إلى ملف package.json.
{ "main": "main.js", "type": "module", "dependencies": { "@google-cloud/firestore": "^7.10.0", "@google-cloud/functions-framework": "^3.4.5" } }
Node.js
import Firestore from '@google-cloud/firestore'; import * as functions from '@google-cloud/functions-framework'; const firestore = new Firestore({preferRest: true}); // Pre-warm firestore connection pool, and preload our global config // document in cache. In order to ensure no other request comes in, // block the module loading with a synchronous global request: const config = await firestore.collection('collection').doc('config').get(); functions.http('fetch', (req, res) => { // Do something with config and firestore client, which are now preloaded // and will execute at lower latency. });
أمثلة على الإعداد الشامل
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
.
ويُعدّ ذلك إجراءً مهمًا على وجه الخصوص إذا كنت تحدّد عدة دوالّ في ملف واحد، ويستخدم كلّ منها متغيّرات مختلفة. ما لم تستخدم ميزة البدء المُتأخر، قد تُهدر الموارد على المتغيّرات التي تمّت بدءها ولكن لم يتم استخدامها مطلقًا.
مراجع إضافية
اطّلِع على مزيد من المعلومات عن تحسين الأداء في فيديو "أداء Google Cloud Atlas" Cloud Functions وقت بدء التشغيل البارد.