نصائح وأمبير. الخدع

يصف هذا المستند أفضل الممارسات لتصميم الوظائف السحابية وتنفيذها واختبارها ونشرها.

صحة

يصف هذا القسم أفضل الممارسات العامة لتصميم وظائف السحابة وتنفيذها.

اكتب وظائف العاجزة

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

لا تبدأ أنشطة الخلفية

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

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

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

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

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

يمكنك رؤية الذاكرة المستخدمة بواسطة وظيفة فردية عن طريق تحديدها في قائمة الوظائف في وحدة تحكم GCP واختيار مخطط استخدام الذاكرة .

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

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

إطار الوظائف

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

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

أدوات

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

التنمية المحلية

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

يمكن لمطوري Firebase استخدام Firebase CLI Cloud Functions Emulator .

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

لا تسمح وظائف السحابة بالاتصالات الصادرة على المنفذ 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}`);
});

بايثون

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 .

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

هل التهيئة البطيئة للمتغيرات العالمية

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

بايثون

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 Performance Atlas" وقت التمهيد البارد لوظائف السحابة .