सलाह और सुझाव

इस दस्तावेज़ में Cloud Functions को डिज़ाइन करने, लागू करने, टेस्ट करने, और डिप्लॉय करने के सबसे सही तरीकों के बारे में बताया गया है.

सही जानकारी

इस सेक्शन में, Cloud फ़ंक्शन डिज़ाइन करने और उन्हें लागू करने के सबसे सही तरीकों के बारे में बताया गया है.

स्थायी फलन लिखना

आपके फ़ंक्शन को एक ही नतीजा मिलना चाहिए, भले ही उन्हें कई बार कॉल किया जाए. इसकी मदद से, उस कोड को दूसरे तरीके से शुरू करने की कोशिश की जा सकती है जिससे पिछला कोड फ़ेल हो जाता है. ज़्यादा जानकारी के लिए, इवेंट-ड्रिवन फ़ंक्शन को फिर से आज़माना लेख पढ़ें.

बैकग्राउंड में होने वाली गतिविधियां शुरू न करें

बैकग्राउंड में होने वाली गतिविधि, वह होता है जो आपके फ़ंक्शन के बंद होने के बाद होता है. फ़ंक्शन के वापस आने या उसके पूरा होने का सिग्नल देने पर, फ़ंक्शन शुरू करने की प्रोसेस खत्म हो जाती है. जैसे, Node.js इवेंट-ड्रिवन फ़ंक्शन में callback आर्ग्युमेंट को कॉल करके. ग्रेसफ़ुल तरीके से खत्म होने के बाद चलने वाला कोई भी कोड, सीपीयू को ऐक्सेस नहीं कर सकता. साथ ही, इससे कोई प्रोग्रेस भी नहीं होगी.

साथ ही, जब उसी एनवायरमेंट में किसी अन्य प्रॉम्प्ट को लागू किया जाता है, तो आपके बैकग्राउंड में होने वाली गतिविधि फिर से शुरू हो जाती है. इससे, नए शुरू करने के दौरान रुकावट आती है. इसकी वजह से, हो सकता है कि अनचाहा व्यवहार और गड़बड़ियां हों. इन गड़बड़ियों का पता लगाना मुश्किल होता है. फ़ंक्शन के खत्म होने के बाद, नेटवर्क को ऐक्सेस करने से आम तौर पर कनेक्शन रीसेट हो जाते हैं (ECONNRESET गड़बड़ी कोड).

बैकग्राउंड में होने वाली गतिविधि का पता अक्सर लोगों के अलग-अलग बोले जाने वाले कॉल के लॉग में लगाया जा सकता है. इसके लिए, शुरू के बाद जो लाइन शुरू होती है उसके खत्म होने के बाद लॉग में जो कुछ भी लिखा होता है उसे खोजा जाता है. कभी-कभी बैकग्राउंड में होने वाली गतिविधि को कोड में बहुत ज़्यादा देर तक रखा जा सकता है. खास तौर पर, ऐसा तब होता है, जब कॉलबैक या टाइमर जैसी एसिंक्रोनस कार्रवाइयां मौजूद हों. अपने कोड की समीक्षा करके पक्का करें कि फ़ंक्शन को बंद करने से पहले, सभी एसिंक्रोनस कार्रवाइयां पूरी हो गई हैं.

कुछ समय के लिए सेव की गई फ़ाइलें हमेशा मिटाएं

अस्थायी डायरेक्ट्री में मौजूद लोकल डिस्क स्टोरेज, एक इन-मेमोरी फ़ाइल सिस्टम होता है. जो फ़ाइलें आप लिखी जाती हैं वे आपके फ़ंक्शन के लिए उपलब्ध मेमोरी का इस्तेमाल करती हैं और कभी-कभी बोला जाने के बीच में बनी रहती हैं. इन फ़ाइलों को साफ़ तौर पर न मिटाने पर, स्टोरेज खत्म होने की गड़बड़ी हो सकती है. साथ ही, कोल्ड स्टार्ट की समस्या भी हो सकती है.

किसी फ़ंक्शन के लिए इस्तेमाल की गई मेमोरी को देखने के लिए, उसे GCP कंसोल में फ़ंक्शन की सूची में जाकर मेमोरी का इस्तेमाल प्लॉट चुनें.

अस्थायी डायरेक्ट्री के बाहर लिखने की कोशिश न करें. साथ ही, फ़ाइल पाथ बनाने के लिए, प्लैटफ़ॉर्म/ओएस-इंडिपेंडेंट तरीकों का इस्तेमाल ज़रूर करें.

पाइपलाइनिंग की सुविधा का इस्तेमाल करके, बड़ी फ़ाइलों को प्रोसेस करते समय, मेमोरी की ज़रूरी शर्तों को कम किया जा सकता है. उदाहरण के लिए, Cloud Storage में मौजूद फ़ाइल को प्रोसेस किया जा सकता है. इसके लिए, एक रीड स्ट्रीम बनाएं, उसे स्ट्रीम पर आधारित प्रोसेस की मदद से पास करें, और आउटपुट स्ट्रीम को सीधे Cloud Storage में लिखें.

फ़ंक्शन फ़्रेमवर्क

किसी फ़ंक्शन को डिप्लॉय करने पर, फ़ंक्शन फ़्रेमवर्क अपने मौजूदा वर्शन का इस्तेमाल करके डिपेंडेंसी के तौर पर अपने-आप जुड़ जाता है. यह पक्का करने के लिए कि एक ही डिपेंडेंसी को अलग-अलग एनवायरमेंट में लगातार इंस्टॉल किया जाए, हमारा सुझाव है कि आप अपने फ़ंक्शन को फ़ंक्शन फ़्रेमवर्क के किसी खास वर्शन में पिन करें.

ऐसा करने के लिए, सही लॉक फ़ाइल में अपना पसंदीदा वर्शन शामिल करें (उदाहरण के लिए, Node.js के लिए package-lock.json या Python के लिए requirements.txt).

टूल

इस सेक्शन में, Cloud Functions को लागू करने, उनकी जांच करने, और उनके साथ इंटरैक्ट करने के लिए टूल इस्तेमाल करने के दिशा-निर्देश दिए गए हैं.

लोकल डेवलपमेंट

फ़ंक्शन डिप्लॉयमेंट में कुछ समय लगता है. इसलिए, आपके फ़ंक्शन के कोड की स्थानीय तौर पर जांच करना अक्सर तेज़ होता है.

Firebase डेवलपर, Firebase CLI Cloud Functions एम्युलेटर का इस्तेमाल कर सकते हैं.

ईमेल भेजने के लिए Sendgrid का इस्तेमाल करना

Cloud Functions, पोर्ट 25 पर आउटबाउंड कनेक्शन की अनुमति नहीं देता है, इसलिए आप एसएमटीपी सर्वर पर असुरक्षित कनेक्शन नहीं बना सकते. हमारा सुझाव है कि ईमेल भेजने के लिए SendGrid का इस्तेमाल करें. Google Compute Engine के लिए, किसी इंस्टेंस से ईमेल भेजना ट्यूटोरियल में, ईमेल भेजने के अन्य विकल्प देखे जा सकते हैं.

परफ़ॉर्मेंस रिपोर्ट

इस सेक्शन में, परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के सबसे सही तरीकों के बारे में बताया गया है.

डिपेंडेंसी का समझदारी से इस्तेमाल करना

फ़ंक्शन स्टेटलेस होते हैं, इसलिए एक्ज़ीक्यूशन एनवायरमेंट अक्सर शुरुआत से शुरू किया जाता है (इसे कोल्ड स्टार्ट के दौरान कहा जाता है). जब कोल्ड स्टार्ट होता है, तो फ़ंक्शन के ग्लोबल कॉन्टेक्स्ट का आकलन किया जाता है.

अगर आपके फ़ंक्शन, मॉड्यूल इंपोर्ट करते हैं, तो उन मॉड्यूल के लोड होने में लगने वाला समय, कोल्ड स्टार्ट के दौरान शुरू होने में लगने वाला समय बढ़ा सकता है. डिपेंडेंसी को सही तरीके से लोड करके और इस्तेमाल न की जाने वाली डिपेंडेंसी लोड करके, इस इंतज़ार के समय को कम किया जा सकता है. साथ ही, अपने फ़ंक्शन को डिप्लॉय करने में लगने वाला समय भी कम किया जा सकता है.

आने वाले समय के न्योते में ऑब्जेक्ट का फिर से इस्तेमाल करने के लिए, ग्लोबल वैरिएबल का इस्तेमाल करें

इस बात की कोई गारंटी नहीं है कि आने वाले समय में शामिल होने के लिए, Cloud फ़ंक्शन की स्थिति को बनाए रखा जाएगा. हालांकि, 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}")
  

यह एचटीटीपी फ़ंक्शन एक अनुरोध ऑब्जेक्ट (flask.Request) लेता है. यह रिस्पॉन्स टेक्स्ट या वैल्यू का ऐसा कोई भी सेट दिखाता है जिसे make_response का इस्तेमाल करके, 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}.")
  

यह एचटीटीपी फ़ंक्शन, लेज़ीली-इनीशियलाइज़्ड ग्लोबल का इस्तेमाल करता है. यह एक अनुरोध ऑब्जेक्ट (flask.Request) लेता है और रिस्पॉन्स टेक्स्ट या वैल्यू का कोई भी सेट दिखाता है, जिसे make_response का इस्तेमाल करके Response ऑब्जेक्ट में बदला जा सकता है.

यह खास तौर पर तब अहम होता है, जब एक ही फ़ाइल में कई फ़ंक्शन तय किए जाते हैं और अलग-अलग फ़ंक्शन अलग-अलग वैरिएबल का इस्तेमाल करते हैं. जब तक लेज़ी इनिशलाइज़ेशन का इस्तेमाल नहीं किया जाता, तब तक ऐसे वैरिएबल पर संसाधनों की बर्बादी हो सकती है जिन्हें शुरू तो किया गया है, लेकिन कभी इस्तेमाल नहीं किया गया है.

कोल्ड स्टार्ट को कम से कम इंस्टेंस की संख्या सेट करने से रोकें

डिफ़ॉल्ट रूप से, Cloud Functions, आने वाले अनुरोधों की संख्या के आधार पर इंस्टेंस की संख्या को बढ़ाता है. इस डिफ़ॉल्ट तरीके को बदलने के लिए, कम से कम उन इंस्टेंस की संख्या सेट की जा सकती है जिनके लिए Cloud Functions, अनुरोधों को पूरा करने के लिए तैयार रहे. इंस्टेंस की कम से कम संख्या सेट करने से, आपके ऐप्लिकेशन की कोल्ड स्टार्ट कम हो जाता है. अगर आपका ऐप्लिकेशन इंतज़ार के समय के हिसाब से संवेदनशील है, तो हमारा सुझाव है कि आप कम से कम इंस्टेंस सेट करें.

रनटाइम के इन विकल्पों के बारे में ज़्यादा जानकारी के लिए, स्केलिंग के व्यवहार को कंट्रोल करना देखें.

अन्य संसाधन

"Google Cloud Performance Atlas" वीडियो में, परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के बारे में ज़्यादा जानें Cloud Functions कोल्ड बूट टाइम.