सलाह और सुझाव

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

सुधार

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

आइपोटेंट फलन लिखें

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

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

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

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

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

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

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

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

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

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

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

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

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

टूल

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

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

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

Firebase के डेवलपर, Firebase सीएलआई क्लाउड फ़ंक्शन एम्युलेटर का इस्तेमाल कर सकते हैं.

ईमेल भेजने के लिए Sendग्रिड का इस्तेमाल करें

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 को कोल्ड बूट करने में लगने वाला समय.