इस दस्तावेज़ में, डिज़ाइन करने, लागू करने, टेस्ट करने, और Cloud Functions को डिप्लॉय करने के सबसे सही तरीके बताए गए हैं.
सटीकता
इस सेक्शन में, डिज़ाइन और लागू करने के सबसे सही सामान्य तरीके बताए गए हैं Cloud Functions.
आइडेंटपोटेंट फ़ंक्शन लिखना
आपके फ़ंक्शन को एक ही नतीजा देना चाहिए, भले ही उन्हें कई बार कॉल किया जाए. इससे, अगर आपका कोड बीच में ही रुक जाता है, तो आपके पास इनवोकेशन को फिर से आज़माने का विकल्प होता है. ज़्यादा जानकारी के लिए, इवेंट-ड्रिवन फ़ंक्शन को फिर से आज़माना लेख पढ़ें.
बैकग्राउंड में गतिविधियां शुरू न करें
बैकग्राउंड में होने वाली गतिविधि, वह गतिविधि होती है जो आपके फ़ंक्शन के खत्म होने के बाद होती है.
फ़ंक्शन का इनवोकेशन तब खत्म होता है, जब फ़ंक्शन कोई वैल्यू दिखाता है या पूरा होने का सिग्नल देता है. जैसे, Node.js के इवेंट-ड्रिवन फ़ंक्शन में callback आर्ग्युमेंट को कॉल करके. ग्रेसफ़ुल टर्मिनेशन के बाद चलने वाला कोई भी कोड, सीपीयू को ऐक्सेस नहीं कर सकता और उसमें कोई प्रोग्रेस नहीं होगी.
इसके अलावा, जब उसी एनवायरमेंट में कोई दूसरा इनवोकेशन एक्ज़ीक्यूट किया जाता है, तो आपकी बैकग्राउंड गतिविधि फिर से शुरू हो जाती है. इससे नए इनवोकेशन में रुकावट आती है. इससे अनचाहा व्यवहार और ऐसी गड़बड़ियां हो सकती हैं जिनका विश्लेषण करना मुश्किल होता है. किसी फ़ंक्शन के टर्मिनेट होने के बाद नेटवर्क को ऐक्सेस करने पर, आम तौर पर कनेक्शन रीसेट हो जाते हैं (ECONNRESET गड़बड़ी कोड).
बैकग्राउंड में होने वाली गतिविधि का पता, अलग-अलग इनवोकेशन के लॉग में लगाया जा सकता है. इसके लिए, इनवोकेशन के खत्म होने की लाइन के बाद लॉग की गई किसी भी चीज़ को ढूंढें. बैकग्राउंड में होने वाली गतिविधि, कभी-कभी कोड में गहराई से छिपी हो सकती है. खास तौर पर, जब कॉलबैक या टाइमर जैसे एसिंक्रोनस ऑपरेशन मौजूद हों. अपने कोड की समीक्षा करें, ताकि यह पक्का किया जा सके कि फ़ंक्शन को टर्मिनेट करने से पहले, सभी एसिंक्रोनस ऑपरेशन खत्म हो जाएं.
अस्थायी फ़ाइलों को हमेशा मिटाएं
अस्थायी डायरेक्ट्री में लोकल डिस्क स्टोरेज, इन-मेमोरी फ़ाइल सिस्टम होता है. आपके द्वारा लिखी गई फ़ाइलें, आपके फ़ंक्शन के लिए उपलब्ध मेमोरी का इस्तेमाल करती हैं. साथ ही, कभी-कभी इनवोकेशन के बीच भी बनी रहती हैं. इन फ़ाइलों को साफ़ तौर पर न मिटाने पर, आखिर में आउट-ऑफ़-मेमोरी गड़बड़ी और उसके बाद कोल्ड स्टार्ट हो सकता है.
Google Cloud कंसोल में, फ़ंक्शन की सूची में किसी फ़ंक्शन को चुनकर और मेमोरी के इस्तेमाल का प्लॉट चुनकर, किसी फ़ंक्शन के इस्तेमाल की गई मेमोरी देखी जा सकती है.
अगर आपको लंबे समय तक स्टोरेज को ऐक्सेस करना है, तो Cloud Run वॉल्यूम माउंट का इस्तेमाल करें Cloud Storage या NFS वॉल्यूम.
पाइपलाइनिंग का इस्तेमाल करके, बड़ी फ़ाइलों को प्रोसेस करते समय मेमोरी की ज़रूरत को कम किया जा सकता है. उदाहरण के लिए, Cloud Storage पर किसी फ़ाइल को प्रोसेस करने के लिए, रीड स्ट्रीम बनाई जा सकती है. इसके बाद, इसे स्ट्रीम-आधारित प्रोसेस के ज़रिए पास किया जा सकता है. साथ ही, आउटपुट स्ट्रीम को सीधे Cloud Storage पर लिखा जा सकता है.
Functions Framework
यह पक्का करने के लिए कि सभी एनवायरमेंट में एक ही डिपेंडेंसी इंस्टॉल की गई हों, हमारा सुझाव है कि आप अपने पैकेज मैनेजर में Functions Framework लाइब्रेरी शामिल करें. साथ ही, डिपेंडेंसी को Functions Framework के किसी खास वर्शन पर पिन करें.
इसके लिए, अपनी पसंद का वर्शन, संबंधित लॉक फ़ाइल में शामिल करें. जैसे, Node.js के लिए package-lock.json या Python के लिए requirements.txt.
अगर Functions Framework को डिपेंडेंसी के तौर पर साफ़ तौर पर लिस्ट नहीं किया जाता है, तो इसे बिल्ड प्रोसेस के दौरान, उपलब्ध सबसे नए वर्शन का इस्तेमाल करके अपने-आप जोड़ दिया जाएगा.
टूल
इस सेक्शन में, Cloud Functions को लागू करने, टेस्ट करने, और Cloud Functions के साथ इंटरैक्ट करने के लिए टूल इस्तेमाल करने के बारे में दिशा-निर्देश दिए गए हैं.
लोकल डेवलपमेंट
फ़ंक्शन को डिप्लॉय करने में थोड़ा समय लगता है. इसलिए, अक्सर अपने फ़ंक्शन के कोड को लोकल तौर पर टेस्ट करना ज़्यादा तेज़ होता है.
Firebase डेवलपर, Firebase CLI Cloud Functions Emulator का इस्तेमाल कर सकते हैं.शुरू करने के दौरान, डिप्लॉयमेंट के टाइम आउट से बचें
अगर आपका फ़ंक्शन, टाइम आउट की गड़बड़ी के साथ डिप्लॉय नहीं हो पाता है, तो इसका मतलब है कि डिप्लॉयमेंट प्रोसेस के दौरान, आपके फ़ंक्शन के ग्लोबल स्कोप कोड को एक्ज़ीक्यूट होने में ज़्यादा समय लग रहा है.
Firebase CLI में, डिप्लॉयमेंट के दौरान आपके फ़ंक्शन को ढूंढने के लिए डिफ़ॉल्ट टाइम आउट होता है. अगर आपके फ़ंक्शन के सोर्स कोड में, शुरू करने की लॉजिक (मॉड्यूल लोड करना, नेटवर्क कॉल करना वगैरह) इस टाइम आउट से ज़्यादा है, तो डिप्लॉयमेंट नहीं हो पाएगा.
टाइम आउट से बचने के लिए, इनमें से कोई एक रणनीति अपनाएं:
(सुझाया गया) शुरू करने की प्रोसेस को बाद में करने के लिए, onInit() का इस्तेमाल करें
डिप्लॉयमेंट के दौरान, शुरू करने का कोड चलाने से बचने के लिए, onInit() हुक का इस्तेमाल करें. onInit() हुक के अंदर मौजूद कोड, सिर्फ़ तब चलेगा, जब फ़ंक्शन को Cloud Run फ़ंक्शन पर डिप्लॉय किया जाएगा. यह डिप्लॉयमेंट प्रोसेस के दौरान नहीं चलेगा.
Node.js
const { onInit } = require('firebase-functions/v2/core'); const { onRequest } = require('firebase-functions/v2/https'); // Example of a slow initialization task function slowInitialization() { // Simulate a long-running operation (e.g., loading a large model, network request). return new Promise(resolve => { setTimeout(() => { console.log("Slow initialization complete"); resolve("Initialized Value"); }, 20000); // Simulate a 20-second delay }); } let initializedValue; onInit(async () => { initializedValue = await slowInitialization(); }); exports.myFunction = onRequest((req, res) => { // Access the initialized value. It will be ready after the first invocation. res.send(`Value: ${initializedValue}`); });
Python
from firebase_functions.core import init from firebase_functions import https_fn import time # Example of a slow initialization task def _slow_initialization(): time.sleep(20) # Simulate a 20-second delay print("Slow initialization complete") return "Initialized Value" _initialized_value = None @init def initialize(): global _initialized_value _initialized_value = _slow_initialization() @https_fn.on_request() def my_function(req: https_fn.Request) -> https_fn.Response: # Access the initialized value. It will be ready after the first invocation. return https_fn.Response(f"Value: {_initialized_value}")
(दूसरा तरीका) डिस्कवरी टाइम आउट बढ़ाना
अगर onInit() का इस्तेमाल करने के लिए, अपने कोड को रीफ़ैक्टर नहीं किया जा सकता, तो FUNCTIONS_DISCOVERY_TIMEOUT एनवायरमेंट वैरिएबल का इस्तेमाल करके, सीएलआई के डिप्लॉयमेंट टाइम आउट को बढ़ाया जा सकता है:
$ export FUNCTIONS_DISCOVERY_TIMEOUT=30
$ firebase deploy --only 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) लेता है. साथ ही, रिस्पॉन्स टेक्स्ट या वैल्यू का कोई भी ऐसा सेट दिखाता है जिसे
Response ऑब्जेक्ट में बदला जा सकता है.
make_response.
नेटवर्क कनेक्शन, लाइब्रेरी रेफ़रंस, और एपीआई क्लाइंट ऑब्जेक्ट को ग्लोबल स्कोप में कैश करना खास तौर पर ज़रूरी है. उदाहरण के लिए, नेटवर्क को ऑप्टिमाइज़ करना लेख पढ़ें.
इंस्टेंस की कम से कम संख्या सेट करके, कोल्ड स्टार्ट की संख्या कम करना
डिफ़ॉल्ट रूप से, Cloud Functions, आने वाले अनुरोधों की संख्या के आधार पर इंस्टेंस की संख्या को स्केल करता है. Cloud Functions को अनुरोधों को पूरा करने के लिए, इंस्टेंस की कम से कम संख्या को तैयार रखने के लिए सेट करके, इस डिफ़ॉल्ट सेटिंग को बदला जा सकता है. इंस्टेंस की कम से कम संख्या सेट करने से, आपके ऐप्लिकेशन के कोल्ड स्टार्ट की संख्या कम हो जाती है. हमारा सुझाव है कि अगर आपका ऐप्लिकेशन लेटेन्सी के लिए संवेदनशील है, तो इंस्टेंस की कम से कम संख्या सेट करें और लोड होने के समय, शुरू करने की प्रोसेस पूरी करें.
इन रनटाइम विकल्पों के बारे में ज़्यादा जानने के लिए, स्केलिंग के व्यवहार को कंट्रोल करना लेख पढ़ें.कोल्ड स्टार्ट और शुरू करने की प्रोसेस के बारे में सूचनाएं
ग्लोबल इनिशियलाइज़ेशन, लोड होने के समय होता है. इसके बिना, पहले अनुरोध को शुरू करने की प्रोसेस पूरी करनी होगी और मॉड्यूल लोड करने होंगे. इससे लेटेन्सी ज़्यादा होगी.
हालांकि, ग्लोबल इनिशियलाइज़ेशन का असर कोल्ड स्टार्ट पर भी पड़ता है. इस असर को कम करने के लिए, सिर्फ़ पहले अनुरोध के लिए ज़रूरी चीज़ों को शुरू करें, ताकि पहले अनुरोध की लेटेन्सी को कम से कम रखा जा सके.
यह खास तौर पर तब ज़रूरी है, जब आपने लेटेन्सी के लिए संवेदनशील फ़ंक्शन के लिए, ऊपर बताए गए तरीके से इंस्टेंस की कम से कम संख्या कॉन्फ़िगर की हो. ऐसे में, लोड होने के समय, शुरू करने की प्रोसेस पूरी करने और काम के डेटा को कैश करने से यह पक्का होता है कि पहले अनुरोध को यह प्रोसेस पूरी न करनी पड़े और उसे कम लेटेन्सी के साथ पूरा किया जाए.
अगर ग्लोबल स्कोप में वैरिएबल शुरू किए जाते हैं, तो भाषा के आधार पर, शुरू होने में ज़्यादा समय लगने पर दो तरह के व्यवहार हो सकते हैं: - कुछ भाषाओं और एसिंक लाइब्रेरी के कॉम्बिनेशन के लिए, फ़ंक्शन फ़्रेमवर्क एसिंक्रोनस तरीके से चल सकता है और तुरंत वैल्यू दिखा सकता है. इससे कोड बैकग्राउंड में चलता रहता है. इससे सीपीयू को ऐक्सेस न कर पाने जैसी समस्याएं हो सकती हैं. सीपीयू को ऐक्सेस न कर पाना. इससे बचने के लिए, आपको मॉड्यूल इनिशियलाइज़ेशन को ब्लॉक करना चाहिए. इसके बारे में नीचे बताया गया है. इससे यह भी पक्का होता है कि शुरू करने की प्रोसेस पूरी होने तक, अनुरोधों को पूरा न किया जाए. - दूसरी ओर, अगर शुरू करने की प्रोसेस सिंक्रोनस है, तो शुरू होने में ज़्यादा समय लगने से कोल्ड स्टार्ट ज़्यादा समय तक चलेगा. इससे लोड के स्पाइक के दौरान, खास तौर पर कम कंकरेंसी वाले फ़ंक्शन में समस्या हो सकती है.
एसिंक node.js लाइब्रेरी को प्रीवार्म करने का उदाहरण
Firestore के साथ Node.js, एसिंक node.js लाइब्रेरी का एक उदाहरण है. min_instances का फ़ायदा लेने के लिए, यहां दिया गया कोड, लोड होने के समय, लोड करने और शुरू करने की प्रोसेस पूरी करता है. साथ ही, मॉड्यूल लोड होने को ब्लॉक करता है.
इसमें टीएलए का इस्तेमाल किया जाता है. इसका मतलब है कि ES6 की ज़रूरत होती है. इसके लिए, node.js कोड के लिए .mjs एक्सटेंशन का इस्तेमाल किया जाता है या package.json फ़ाइल में type: module जोड़ा जाता है.
{ "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}.")
यह एचटीटीपी फ़ंक्शन, लेज़ी-इनिशियलाइज़्ड ग्लोबल का इस्तेमाल करता है. यह अनुरोध ऑब्जेक्ट
(flask.Request) लेता है. साथ ही, रिस्पॉन्स टेक्स्ट या वैल्यू का कोई भी ऐसा सेट दिखाता है जिसे
Response ऑब्जेक्ट में बदला जा सकता है
make_response.
यह खास तौर पर तब ज़रूरी है, जब एक ही फ़ाइल में कई फ़ंक्शन तय किए गए हों और अलग-अलग फ़ंक्शन, अलग-अलग वैरिएबल का इस्तेमाल करते हों. लेज़ी इनिशियलाइज़ेशन का इस्तेमाल न करने पर, उन वैरिएबल पर संसाधन बर्बाद हो सकते हैं जिन्हें शुरू तो किया गया है, लेकिन कभी इस्तेमाल नहीं किया गया.
अन्य संसाधन
"Google Cloud Performance Atlas" वीडियो Cloud Functions Cold Boot Time में, परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के बारे में ज़्यादा जानें.