सलाह और सुझाव

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

सही है या नहीं

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

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

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

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

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

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

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

कुछ समय तक सेव रहने वाली फ़ाइलों को हमेशा मिटाना

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

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

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

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

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

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

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

टूल

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

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

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

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

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

Cloud Functions, पोर्ट 25 पर आउटबाउंड कनेक्शन की अनुमति नहीं देता. इसलिए, किसी एसएमटीपी सर्वर से असुरक्षित कनेक्शन नहीं बनाए जा सकते. हमारा सुझाव है कि आप 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}")
  

यह एचटीटीपी फ़ंक्शन एक अनुरोध ऑब्जेक्ट (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 परफ़ॉर्मेंस एटलस" वीडियो में, Cloud Functions कोल्ड बूट टाइम में परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के बारे में ज़्यादा जानें.