इस दस्तावेज़ में, Cloud Functions को डिज़ाइन करने, लागू करने, टेस्ट करने, और डिप्लॉय करने के सबसे सही तरीके बताए गए हैं.
सही जवाब
इस सेक्शन में, Cloud Functions को डिज़ाइन और लागू करने के सबसे सही तरीकों के बारे में बताया गया है.
आईडेमपोटेंट फ़ंक्शन लिखना
आपके फ़ंक्शन को एक ही नतीजा देना चाहिए, भले ही उन्हें कई बार कॉल किया गया हो. इसकी मदद से, कोड के बीच में पिछला इनवोकेशन फ़ेल होने पर, इनवोकेशन को फिर से आज़माया जा सकता है. ज़्यादा जानकारी के लिए, इवेंट ट्रिगर होने पर चलने वाले फ़ंक्शन को फिर से ट्रिगर करना लेख पढ़ें.
बैकग्राउंड में गतिविधियां शुरू न करें
बैकग्राउंड गतिविधि का मतलब है कि फ़ंक्शन बंद होने के बाद होने वाली कोई भी गतिविधि.
फ़ंक्शन को कॉल करने की प्रोसेस तब पूरी होती है, जब फ़ंक्शन कोई वैल्यू दिखाता है या पूरा होने का सिग्नल देता है. जैसे, Node.js के इवेंट-ड्रिवन फ़ंक्शन में callback
आर्ग्युमेंट को कॉल करके. ग्रेसफ़ुल टर्मिनेशन के बाद चलने वाला कोई भी कोड, सीपीयू को ऐक्सेस नहीं कर सकता. साथ ही, इससे कोई प्रोग्रेस नहीं होगी.
इसके अलावा, जब एक ही एनवायरमेंट में कोई दूसरा अनुरोध किया जाता है, तो बैकग्राउंड में चल रही गतिविधि फिर से शुरू हो जाती है. इससे नए अनुरोध में रुकावट आती है. इस वजह से, अनचाही गड़बड़ियां हो सकती हैं और ऐसी समस्याएं आ सकती हैं जिनका पता लगाना मुश्किल हो. फ़ंक्शन के बंद होने के बाद नेटवर्क को ऐक्सेस करने पर, कनेक्शन रीसेट हो जाते हैं. ऐसा आम तौर पर ECONNRESET
गड़बड़ी कोड की वजह से होता है.
बैकग्राउंड में होने वाली गतिविधि का पता, अलग-अलग इनवोकेशन के लॉग से लगाया जा सकता है. इसके लिए, उस लाइन के बाद लॉग की गई किसी भी चीज़ को ढूंढें जिसमें यह लिखा हो कि इनवोकेशन पूरा हो गया है. बैकग्राउंड में होने वाली गतिविधि कभी-कभी कोड में काफ़ी अंदर तक छिपी हो सकती है. ऐसा खास तौर पर तब होता है, जब कॉलबैक या टाइमर जैसी एसिंक्रोनस कार्रवाइयां मौजूद हों. अपने कोड की समीक्षा करें, ताकि यह पक्का किया जा सके कि फ़ंक्शन को बंद करने से पहले सभी एसिंक्रोनस ऑपरेशन पूरे हो गए हों.
कुछ समय तक सेव रहने वाली फ़ाइलों को हमेशा मिटाएं
अस्थायी डायरेक्ट्री में लोकल डिस्क स्टोरेज, इन-मेमोरी फ़ाइल सिस्टम होता है. लिखी गई फ़ाइलें, आपके फ़ंक्शन के लिए उपलब्ध मेमोरी का इस्तेमाल करती हैं. साथ ही, कभी-कभी ये फ़ाइलें, फ़ंक्शन को बार-बार कॉल करने के दौरान भी बनी रहती हैं. इन फ़ाइलों को साफ़ तौर पर मिटाने में नाकाम रहने पर, मेमोरी से जुड़ी गड़बड़ी हो सकती है. साथ ही, ऐप्लिकेशन को फिर से शुरू करना पड़ सकता है.
Google Cloud Console में, फ़ंक्शन की सूची में जाकर किसी फ़ंक्शन को चुनें. इसके बाद, मेमोरी के इस्तेमाल का प्लॉट चुनें. इससे आपको उस फ़ंक्शन के इस्तेमाल की गई मेमोरी की जानकारी मिलेगी.
अगर आपको लंबे समय तक डेटा सेव करने की सुविधा चाहिए, तो 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 को लागू करने, उसकी जांच करने, और उससे इंटरैक्ट करने के लिए टूल इस्तेमाल करने के दिशा-निर्देश दिए गए हैं.
लोकल डेवलपमेंट
फ़ंक्शन को डिप्लॉय करने में थोड़ा समय लगता है. इसलिए, अक्सर अपने फ़ंक्शन के कोड को स्थानीय तौर पर टेस्ट करना ज़्यादा तेज़ होता है.
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
) लेता है और जवाब का टेक्स्ट दिखाता है. इसके अलावा, यह वैल्यू का कोई भी ऐसा सेट दिखाता है जिसे make_response
का इस्तेमाल करके, Response
ऑब्जेक्ट में बदला जा सकता है.
ग्लोबल स्कोप में नेटवर्क कनेक्शन, लाइब्रेरी रेफ़रंस, और एपीआई क्लाइंट ऑब्जेक्ट को कैश मेमोरी में सेव करना खास तौर पर ज़रूरी है. उदाहरणों के लिए, नेटवर्किंग को ऑप्टिमाइज़ करना देखें.
कम से कम इंस्टेंस की संख्या सेट करके, कोल्ड स्टार्ट की संख्या कम करना
डिफ़ॉल्ट रूप से, Cloud Functions, आने वाले अनुरोधों की संख्या के आधार पर इंस्टेंस की संख्या को स्केल करता है. इस डिफ़ॉल्ट व्यवहार को बदला जा सकता है. इसके लिए, आपको इंस्टेंस की वह कम से कम संख्या सेट करनी होगी जिसे Cloud Functions को अनुरोधों को पूरा करने के लिए तैयार रखना होगा. कम से कम इंस्टेंस की संख्या सेट करने से, आपके ऐप्लिकेशन के कोल्ड स्टार्ट कम हो जाते हैं. अगर आपका ऐप्लिकेशन लेटेन्सी के लिए संवेदनशील है, तो हमारा सुझाव है कि आप कम से कम इंस्टेंस सेट करें. साथ ही, लोड होने के समय इनिशियलाइज़ेशन पूरा करें.
इन रनटाइम विकल्पों के बारे में ज़्यादा जानने के लिए, स्केलिंग के व्यवहार को कंट्रोल करना लेख पढ़ें.कोल्ड स्टार्ट और शुरू करने के बारे में जानकारी
ग्लोबल वैरिएबल को लोड होने के समय ही शुरू किया जाता है. इसके बिना, पहले अनुरोध को मॉड्यूल लोड करने और शुरू करने की प्रोसेस पूरी करनी होगी. इससे ज़्यादा समय लगेगा.
हालांकि, ग्लोबल वैरिएबल के इनिशियलाइज़ेशन से कोल्ड स्टार्ट पर भी असर पड़ता है. इस असर को कम करने के लिए, सिर्फ़ उन चीज़ों को शुरू करें जिनकी पहले अनुरोध के लिए ज़रूरत है. इससे पहले अनुरोध के लिए इंतज़ार का समय कम से कम रहेगा.
यह खास तौर पर तब अहम होता है, जब आपने कम समय में होने वाले फ़ंक्शन के लिए, ऊपर बताए गए तरीके से कम से कम इंस्टेंस कॉन्फ़िगर किए हों. ऐसे में, लोड होने के समय पर ही शुरू होने की प्रोसेस पूरी करने और काम के डेटा को कैश मेमोरी में सेव करने से, यह पक्का किया जा सकता है कि पहले अनुरोध को ऐसा न करना पड़े और उसे कम समय में पूरा किया जा सके.
अगर ग्लोबल स्कोप में वैरिएबल को शुरू किया जाता है, तो भाषा के आधार पर, वैरिएबल को शुरू होने में ज़्यादा समय लग सकता है. इससे दो तरह की समस्याएं हो सकती हैं: - कुछ भाषाओं और एसिंक लाइब्रेरी के कॉम्बिनेशन के लिए, फ़ंक्शन फ़्रेमवर्क एसिंक रूप से चल सकता है और तुरंत जवाब दे सकता है. इससे कोड बैकग्राउंड में चलता रहता है. इससे सीपीयू को ऐक्सेस न कर पाने जैसी समस्याएं हो सकती हैं. इससे बचने के लिए, आपको मॉड्यूल के शुरू होने पर ब्लॉक करना चाहिए. इसके बारे में यहां बताया गया है. इससे यह भी पक्का होता है कि जब तक शुरू होने की प्रोसेस पूरी नहीं हो जाती, तब तक अनुरोध पूरे नहीं किए जाते. - दूसरी ओर, अगर शुरुआत सिंक्रोनस है, तो शुरुआत में ज़्यादा समय लगने की वजह से कोल्ड स्टार्ट में ज़्यादा समय लगेगा. इससे लोड बढ़ने के दौरान, खास तौर पर कम कंकरेंसी वाले फ़ंक्शन में समस्या आ सकती है.
एसिंक्रोनस node.js लाइब्रेरी को प्रीवार्म करने का उदाहरण
Node.js के साथ Firestore, एसिंक 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
) लेता है और जवाब के तौर पर टेक्स्ट दिखाता है. इसके अलावा, यह ऐसी वैल्यू का कोई भी सेट दिखाता है जिसे make_response
का इस्तेमाल करके, Response
ऑब्जेक्ट में बदला जा सकता है.
यह खास तौर पर तब ज़रूरी होता है, जब आपने एक ही फ़ाइल में कई फ़ंक्शन तय किए हों और अलग-अलग फ़ंक्शन अलग-अलग वैरिएबल का इस्तेमाल करते हों. लेज़ी इनिशियलाइज़ेशन का इस्तेमाल न करने पर, हो सकता है कि आप उन वैरिएबल पर संसाधनों को बर्बाद कर दें जिन्हें इनिशियलाइज़ किया गया है, लेकिन कभी इस्तेमाल नहीं किया गया.
अन्य संसाधन
"Google Cloud Performance Atlas" वीडियो में, परफ़ॉर्मेंस को ऑप्टिमाइज़ करने के बारे में ज़्यादा जानें. Cloud Functions कोल्ड बूट टाइम.