این سند بهترین روشها را برای طراحی، پیادهسازی، آزمایش و استقرار Cloud Functions توضیح میدهد.
صحت
این بخش بهترین شیوه های کلی برای طراحی و پیاده سازی Cloud Functions را شرح می دهد.
توابع idempotent را بنویسید
توابع شما باید نتیجه یکسانی داشته باشند حتی اگر چندین بار فراخوانی شوند. این به شما امکان می دهد اگر فراخوانی قبلی بخشی از کد شما انجام نشد، یک فراخوان را دوباره امتحان کنید. برای اطلاعات بیشتر، به تلاش مجدد توابع مبتنی بر رویداد مراجعه کنید.
فعالیت های پس زمینه را شروع نکنید
فعالیت پس زمینه هر چیزی است که پس از پایان عملکرد شما اتفاق می افتد. فراخوانی تابع پس از بازگشت تابع یا سیگنالهای تکمیلی دیگر، مانند فراخوانی آرگومان callback
در توابع رویداد محور Node.js به پایان میرسد. هر کدی که پس از خاتمه برازنده اجرا شود نمی تواند به CPU دسترسی داشته باشد و هیچ پیشرفتی نخواهد داشت.
علاوه بر این، هنگامی که فراخوانی بعدی در همان محیط اجرا می شود، فعالیت پس زمینه شما از سر گرفته می شود و در فراخوانی جدید تداخل ایجاد می کند. این ممکن است منجر به رفتارهای غیرمنتظره و خطاهایی شود که تشخیص آنها سخت است. دسترسی به شبکه پس از پایان عملکرد معمولاً منجر به تنظیم مجدد اتصالات می شود (کد خطای ECONNRESET
).
فعالیت پسزمینه را میتوان با یافتن هر چیزی که بعد از خطی که فراخوانی پایان یافت ثبت شده است، در گزارشهای مربوط به فراخوانهای فردی شناسایی کرد. گاهی اوقات میتوان فعالیت پسزمینه را عمیقتر در کد مدفون کرد، بهویژه زمانی که عملیات ناهمزمان مانند تماسها یا تایمرها وجود دارد. کد خود را مرور کنید تا مطمئن شوید که تمام عملیات ناهمزمان قبل از خاتمه عملکرد به پایان می رسد.
همیشه فایل های موقت را حذف کنید
ذخیره سازی دیسک محلی در دایرکتوری موقت یک فایل سیستم در حافظه است. فایل هایی که می نویسید حافظه موجود برای عملکرد شما را مصرف می کنند و گاهی اوقات بین فراخوانی ها باقی می مانند. عدم حذف صریح این فایل ها ممکن است در نهایت منجر به خطای کمبود حافظه و متعاقبا شروع سرد شود.
با انتخاب آن در لیست توابع در کنسول Google Cloud و انتخاب نمودار استفاده از حافظه، می توانید حافظه استفاده شده توسط یک تابع را مشاهده کنید.
اگر نیاز به دسترسی به فضای ذخیرهسازی طولانیمدت دارید، از نصبهای حجمی Cloud Run با حجمهای Cloud Storage یا NFS استفاده کنید.
هنگام پردازش فایل های بزرگتر با استفاده از خط لوله، می توانید نیاز به حافظه را کاهش دهید. برای مثال، میتوانید با ایجاد یک جریان خواندن، عبور دادن آن از طریق یک فرآیند مبتنی بر جریان و نوشتن جریان خروجی مستقیماً در فضای ذخیرهسازی ابری، یک فایل را در فضای ذخیرهسازی ابری پردازش کنید.
چارچوب توابع
برای اطمینان از اینکه وابستگیهای یکسان به طور مداوم در بین محیطها نصب میشوند، توصیه میکنیم که کتابخانه Functions Framework را در مدیریت بسته خود قرار دهید و وابستگی را به نسخه خاصی از Functions Framework پین کنید.
برای انجام این کار، نسخه دلخواه خود را در فایل قفل مربوطه قرار دهید (به عنوان مثال، package-lock.json
برای Node.js، یا requirements.txt
برای پایتون).
اگر Functions Framework به صراحت به عنوان یک وابستگی فهرست نشده باشد، به طور خودکار در طول فرآیند ساخت با استفاده از آخرین نسخه موجود اضافه می شود.
ابزار
این بخش دستورالعمل هایی در مورد نحوه استفاده از ابزارها برای پیاده سازی، آزمایش و تعامل با Cloud Functions ارائه می دهد.
توسعه محلی
استقرار تابع کمی زمان می برد، بنابراین آزمایش کد عملکرد خود به صورت محلی اغلب سریعتر است.
توسعه دهندگان Firebase می توانند از Firebase CLI Cloud Functions Emulator استفاده کنند.از وقفه های زمانی استقرار در طول اولیه سازی اجتناب کنید
اگر استقرار تابع شما با خطای مهلت زمانی ناموفق باشد، احتمالاً به این معنی است که اجرای کد دامنه جهانی عملکرد شما در طول فرآیند استقرار بیش از حد طول می کشد.
Firebase CLI دارای یک بازه زمانی پیشفرض برای کشف عملکردهای شما در حین استقرار است. اگر منطق اولیه سازی در کد منبع توابع شما (بارگیری ماژول ها، برقراری تماس های شبکه و غیره) از این مهلت زمانی فراتر رود، ممکن است استقرار با شکست مواجه شود.
برای جلوگیری از وقفه، از یکی از استراتژی های زیر استفاده کنید:
(توصیه می شود) onInit()
برای به تعویق انداختن مقداردهی اولیه استفاده کنید
از قلاب onInit()
برای جلوگیری از اجرای کد اولیه در حین استقرار استفاده کنید. کد داخل قلاب onInit()
فقط زمانی اجرا می شود که تابع در توابع Cloud Run مستقر شود، نه در طول خود فرآیند استقرار.
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}`); });
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()
استفاده کنید، می توانید زمان استقرار CLI را با استفاده از متغیر محیطی FUNCTIONS_DISCOVERY_TIMEOUT
افزایش دهید:
$ export FUNCTIONS_DISCOVERY_TIMEOUT=30
$ firebase deploy --only functions
از Sendgrid برای ارسال ایمیل استفاده کنید
Cloud Functions اجازه اتصالات خروجی را در پورت 25 نمی دهد، بنابراین نمی توانید اتصالات غیر ایمن به سرور SMTP برقرار کنید. روش توصیه شده برای ارسال ایمیل استفاده از یک سرویس شخص ثالث مانند SendGrid است. میتوانید گزینههای دیگری برای ارسال ایمیل در « ارسال ایمیل از یک آموزش نمونه برای موتور محاسباتی Google» بیابید.
عملکرد
این بخش بهترین روش ها برای بهینه سازی عملکرد را شرح می دهد.
از همزمانی کم خودداری کنید
از آنجایی که شروع های سرد گران هستند، امکان استفاده مجدد از نمونه هایی که اخیراً شروع شده اند در طول یک spike بهینه سازی عالی برای مدیریت بار است. محدود کردن همزمانی نحوه استفاده از نمونه های موجود را محدود می کند، بنابراین شروع سرد بیشتری را متحمل می شود.
افزایش همزمانی به به تعویق انداختن درخواستهای متعدد در هر نمونه کمک میکند و رسیدگی به بارگذاری را آسانتر میکند.از وابستگی ها عاقلانه استفاده کنید
از آنجایی که توابع بدون حالت هستند، محیط اجرا اغلب از ابتدا مقداردهی اولیه می شود (در طول چیزی که به عنوان شروع سرد شناخته می شود). هنگامی که شروع سرد رخ می دهد، زمینه کلی تابع ارزیابی می شود.
اگر توابع شما ماژولها را وارد میکنند، زمان بارگذاری آن ماژولها میتواند در زمان شروع سرد به تأخیر فراخوانی اضافه کند. میتوانید با بارگیری صحیح وابستگیها و بار نکردن وابستگیهایی که عملکرد شما از آنها استفاده نمیکند، این تأخیر و همچنین زمان لازم برای استقرار عملکرد خود را کاهش دهید.
از متغیرهای سراسری برای استفاده مجدد از اشیاء در فراخوانی های آینده استفاده کنید
هیچ تضمینی وجود ندارد که وضعیت یک تابع برای فراخوانی های آینده حفظ شود. با این حال، Cloud Functions اغلب محیط اجرای فراخوان قبلی را بازیافت می کند. اگر متغیری را در دامنه جهانی اعلام کنید، مقدار آن را میتوان در فراخوانیهای بعدی بدون نیاز به محاسبه مجدد مورد استفاده قرار داد.
به این ترتیب می توانید اشیایی را که ممکن است در هر فراخوانی تابع گران باشد، ذخیره کنید. انتقال چنین اشیایی از بدنه عملکرد به محدوده جهانی ممکن است منجر به بهبود عملکرد قابل توجهی شود. مثال زیر یک شی سنگین را فقط یک بار در هر نمونه تابع ایجاد می کند و آن را در تمام فراخوانی های تابعی که به نمونه داده شده می رسد به اشتراک می گذارد:
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}`); });
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}")
این تابع HTTP یک شی درخواست ( flask.Request
) می گیرد و متن پاسخ یا هر مجموعه ای از مقادیر را که می تواند با استفاده از make_response
به یک شی Response
تبدیل شود، برمی گرداند.
کش کردن اتصالات شبکه، مراجع کتابخانه و اشیاء کلاینت API در حوزه جهانی بسیار مهم است. برای مثال به بهینه سازی شبکه مراجعه کنید.
با تنظیم حداقل تعداد موارد شروع سرد را کاهش دهید
به طور پیشفرض، Cloud Functions تعداد نمونهها را بر اساس تعداد درخواستهای دریافتی مقیاس میدهد. میتوانید این رفتار پیشفرض را با تنظیم حداقل تعداد نمونههایی که Cloud Function باید برای ارائه درخواستها آماده نگه دارد، تغییر دهید. تنظیم حداقل تعداد نمونه شروع سرد برنامه شما را کاهش می دهد. اگر برنامه شما به تأخیر حساس است، توصیه میکنیم حداقل تعداد نمونهها را تنظیم کنید و مقداردهی اولیه را در زمان بارگذاری کامل کنید.
برای اطلاعات بیشتر در مورد این گزینههای زمان اجرا، رفتار مقیاسبندی کنترل را ببینید.نکاتی در مورد شروع سرد و مقداردهی اولیه
اولیه سازی جهانی در زمان بارگذاری اتفاق می افتد. بدون آن، اولین درخواست نیاز به تکمیل مقداردهی اولیه و بارگذاری ماژولها دارد، در نتیجه تاخیر بیشتری را متحمل میشود.
با این حال، مقداردهی اولیه جهانی نیز بر شروع سرد تأثیر دارد. برای به حداقل رساندن این تأثیر، فقط آنچه را که برای اولین درخواست لازم است، مقداردهی اولیه کنید تا تأخیر درخواست اول را تا حد امکان پایین نگه دارید.
اگر موارد min را همانطور که در بالا توضیح داده شد برای یک تابع حساس به تأخیر پیکربندی کرده باشید، به ویژه مهم است. در آن سناریو، تکمیل مقداردهی اولیه در زمان بارگذاری و ذخیره داده های مفید در حافظه پنهان، تضمین می کند که اولین درخواست نیازی به انجام آن ندارد و با تأخیر کم ارائه می شود.
اگر متغیرها را در دامنه جهانی مقداردهی اولیه کنید، بسته به زبان، زمانهای اولیهسازی طولانی میتواند منجر به دو رفتار شود: - برای ترکیبی از زبانها و کتابخانههای همگام، چارچوب تابع میتواند به صورت ناهمزمان اجرا شود و بلافاصله برگردد، که باعث میشود اجرای کد در پسزمینه ادامه یابد، که میتواند باعث مشکلاتی مانند عدم دسترسی به CPU شود. برای جلوگیری از این امر، باید مقدار دهی اولیه ماژول را همانطور که در زیر توضیح داده شده مسدود کنید. این همچنین تضمین می کند که درخواست ها تا زمانی که اولیه سازی کامل نشده است، ارائه نمی شوند. - از طرف دیگر، اگر مقداردهی اولیه همزمان باشد، زمان طولانیسازی اولیه باعث شروع سرد طولانیتر میشود، که میتواند بهویژه با توابع همزمانی کم در هنگام افزایش بار، مشکلساز باشد.
نمونه ای از پیش گرم کردن کتابخانه async node.js
Node.js با Firestore نمونه ای از کتابخانه async node.js است. به منظور استفاده از min_instance ها، کد زیر بارگذاری و مقداردهی اولیه را در زمان بارگذاری کامل می کند و بارگذاری ماژول را مسدود می کند.
TLA استفاده می شود، به این معنی که ES6 مورد نیاز است، با استفاده از پسوند .mjs
برای کد node.js یا افزودن type: module
به فایل package.json.
{ "main": "main.js", "type": "module", "dependencies": { "@google-cloud/firestore": "^7.10.0", "@google-cloud/functions-framework": "^3.4.5" } }
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. });
نمونه هایی از مقداردهی اولیه جهانی
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'); });
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}.")
این تابع HTTP از جهانی های اولیه با تنبلی استفاده می کند. یک شی درخواست ( flask.Request
) را می گیرد و متن پاسخ یا هر مجموعه ای از مقادیر را که می تواند با استفاده از make_response
به یک شی Response
تبدیل شود، برمی گرداند.
اگر چندین تابع را در یک فایل واحد تعریف کنید و توابع مختلف از متغیرهای متفاوتی استفاده کنند، این امر به ویژه مهم است. مگر اینکه از مقداردهی اولیه تنبل استفاده کنید، ممکن است منابع را بر روی متغیرهایی که مقداردهی اولیه شده اند اما هرگز استفاده نشده اند هدر دهید.
منابع اضافی
درباره بهینهسازی عملکرد در ویدیوی «اطلس عملکرد ابر Google» اطلاعات بیشتری کسب کنید Cloud Functions زمان بوت سرد .
،این سند بهترین روشها را برای طراحی، پیادهسازی، آزمایش و استقرار Cloud Functions توضیح میدهد.
صحت
این بخش بهترین شیوه های کلی برای طراحی و پیاده سازی Cloud Functions را شرح می دهد.
توابع idempotent را بنویسید
توابع شما باید نتیجه یکسانی داشته باشند حتی اگر چندین بار فراخوانی شوند. این به شما امکان می دهد اگر فراخوانی قبلی بخشی از کد شما انجام نشد، یک فراخوان را دوباره امتحان کنید. برای اطلاعات بیشتر، به تلاش مجدد توابع مبتنی بر رویداد مراجعه کنید.
فعالیت های پس زمینه را شروع نکنید
فعالیت پس زمینه هر چیزی است که پس از پایان عملکرد شما اتفاق می افتد. فراخوانی تابع پس از بازگشت تابع یا سیگنالهای تکمیلی دیگر، مانند فراخوانی آرگومان callback
در توابع رویداد محور Node.js به پایان میرسد. هر کدی که پس از خاتمه برازنده اجرا شود نمی تواند به CPU دسترسی داشته باشد و هیچ پیشرفتی نخواهد داشت.
علاوه بر این، هنگامی که فراخوانی بعدی در همان محیط اجرا می شود، فعالیت پس زمینه شما از سر گرفته می شود و در فراخوانی جدید تداخل ایجاد می کند. این ممکن است منجر به رفتارهای غیرمنتظره و خطاهایی شود که تشخیص آنها سخت است. دسترسی به شبکه پس از پایان عملکرد معمولاً منجر به تنظیم مجدد اتصالات می شود (کد خطای ECONNRESET
).
فعالیت پسزمینه را میتوان با یافتن هر چیزی که بعد از خطی که فراخوانی پایان یافت ثبت شده است، در گزارشهای مربوط به فراخوانهای فردی شناسایی کرد. گاهی اوقات میتوان فعالیت پسزمینه را عمیقتر در کد مدفون کرد، بهویژه زمانی که عملیات ناهمزمان مانند تماسها یا تایمرها وجود دارد. کد خود را مرور کنید تا مطمئن شوید که تمام عملیات ناهمزمان قبل از خاتمه عملکرد به پایان می رسد.
همیشه فایل های موقت را حذف کنید
ذخیره سازی دیسک محلی در دایرکتوری موقت یک فایل سیستم در حافظه است. فایل هایی که می نویسید حافظه موجود برای عملکرد شما را مصرف می کنند و گاهی اوقات بین فراخوانی ها باقی می مانند. عدم حذف صریح این فایل ها ممکن است در نهایت منجر به خطای کمبود حافظه و متعاقبا شروع سرد شود.
با انتخاب آن در لیست توابع در کنسول Google Cloud و انتخاب نمودار استفاده از حافظه، می توانید حافظه استفاده شده توسط یک تابع را مشاهده کنید.
اگر نیاز به دسترسی به فضای ذخیرهسازی طولانیمدت دارید، از نصبهای حجمی Cloud Run با حجمهای Cloud Storage یا NFS استفاده کنید.
هنگام پردازش فایل های بزرگتر با استفاده از خط لوله، می توانید نیاز به حافظه را کاهش دهید. برای مثال، میتوانید با ایجاد یک جریان خواندن، عبور دادن آن از طریق یک فرآیند مبتنی بر جریان و نوشتن جریان خروجی مستقیماً در فضای ذخیرهسازی ابری، یک فایل را در فضای ذخیرهسازی ابری پردازش کنید.
چارچوب توابع
برای اطمینان از اینکه وابستگیهای یکسان به طور مداوم در بین محیطها نصب میشوند، توصیه میکنیم که کتابخانه Functions Framework را در مدیریت بسته خود قرار دهید و وابستگی را به نسخه خاصی از Functions Framework پین کنید.
برای انجام این کار، نسخه دلخواه خود را در فایل قفل مربوطه قرار دهید (به عنوان مثال، package-lock.json
برای Node.js، یا requirements.txt
برای پایتون).
اگر Functions Framework به صراحت به عنوان یک وابستگی فهرست نشده باشد، به طور خودکار در طول فرآیند ساخت با استفاده از آخرین نسخه موجود اضافه می شود.
ابزار
این بخش دستورالعمل هایی در مورد نحوه استفاده از ابزارها برای پیاده سازی، آزمایش و تعامل با Cloud Functions ارائه می دهد.
توسعه محلی
استقرار تابع کمی زمان می برد، بنابراین آزمایش کد عملکرد خود به صورت محلی اغلب سریعتر است.
توسعه دهندگان Firebase می توانند از Firebase CLI Cloud Functions Emulator استفاده کنند.از وقفه های زمانی استقرار در طول اولیه سازی اجتناب کنید
اگر استقرار تابع شما با خطای مهلت زمانی ناموفق باشد، احتمالاً به این معنی است که اجرای کد دامنه جهانی عملکرد شما در طول فرآیند استقرار بیش از حد طول می کشد.
Firebase CLI دارای یک بازه زمانی پیشفرض برای کشف عملکردهای شما در حین استقرار است. اگر منطق اولیه سازی در کد منبع توابع شما (بارگیری ماژول ها، برقراری تماس های شبکه و غیره) از این مهلت زمانی فراتر رود، ممکن است استقرار با شکست مواجه شود.
برای جلوگیری از وقفه، از یکی از استراتژی های زیر استفاده کنید:
(توصیه می شود) onInit()
برای به تعویق انداختن مقداردهی اولیه استفاده کنید
از قلاب onInit()
برای جلوگیری از اجرای کد اولیه در حین استقرار استفاده کنید. کد داخل قلاب onInit()
فقط زمانی اجرا می شود که تابع در توابع Cloud Run مستقر شود، نه در طول خود فرآیند استقرار.
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}`); });
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()
استفاده کنید، می توانید زمان استقرار CLI را با استفاده از متغیر محیطی FUNCTIONS_DISCOVERY_TIMEOUT
افزایش دهید:
$ export FUNCTIONS_DISCOVERY_TIMEOUT=30
$ firebase deploy --only functions
از Sendgrid برای ارسال ایمیل استفاده کنید
Cloud Functions اجازه اتصالات خروجی را در پورت 25 نمی دهد، بنابراین نمی توانید اتصالات غیر ایمن به سرور SMTP برقرار کنید. روش توصیه شده برای ارسال ایمیل استفاده از یک سرویس شخص ثالث مانند SendGrid است. میتوانید گزینههای دیگری برای ارسال ایمیل در « ارسال ایمیل از یک آموزش نمونه برای موتور محاسباتی Google» بیابید.
عملکرد
این بخش بهترین روش ها برای بهینه سازی عملکرد را شرح می دهد.
از همزمانی کم خودداری کنید
از آنجایی که شروع های سرد گران هستند، امکان استفاده مجدد از نمونه هایی که اخیراً شروع شده اند در طول یک spike بهینه سازی عالی برای مدیریت بار است. محدود کردن همزمانی نحوه استفاده از نمونه های موجود را محدود می کند، بنابراین شروع سرد بیشتری را متحمل می شود.
افزایش همزمانی به به تعویق انداختن درخواستهای متعدد در هر نمونه کمک میکند و رسیدگی به بارگذاری را آسانتر میکند.از وابستگی ها عاقلانه استفاده کنید
از آنجایی که توابع بدون حالت هستند، محیط اجرا اغلب از ابتدا مقداردهی اولیه می شود (در طول چیزی که به عنوان شروع سرد شناخته می شود). هنگامی که شروع سرد رخ می دهد، زمینه کلی تابع ارزیابی می شود.
اگر توابع شما ماژولها را وارد میکنند، زمان بارگذاری آن ماژولها میتواند در زمان شروع سرد به تأخیر فراخوانی اضافه کند. میتوانید با بارگیری صحیح وابستگیها و بار نکردن وابستگیهایی که عملکرد شما از آنها استفاده نمیکند، این تأخیر و همچنین زمان لازم برای استقرار عملکرد خود را کاهش دهید.
از متغیرهای سراسری برای استفاده مجدد از اشیاء در فراخوانی های آینده استفاده کنید
هیچ تضمینی وجود ندارد که وضعیت یک تابع برای فراخوانی های آینده حفظ شود. با این حال، Cloud Functions اغلب محیط اجرای فراخوان قبلی را بازیافت می کند. اگر متغیری را در دامنه جهانی اعلام کنید، مقدار آن را میتوان در فراخوانیهای بعدی بدون نیاز به محاسبه مجدد مورد استفاده قرار داد.
به این ترتیب می توانید اشیایی را که ممکن است در هر فراخوانی تابع گران باشد، ذخیره کنید. انتقال چنین اشیایی از بدنه عملکرد به محدوده جهانی ممکن است منجر به بهبود عملکرد قابل توجهی شود. مثال زیر یک شی سنگین را فقط یک بار در هر نمونه تابع ایجاد می کند و آن را در تمام فراخوانی های تابعی که به نمونه داده شده می رسد به اشتراک می گذارد:
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}`); });
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}")
این تابع HTTP یک شی درخواست ( flask.Request
) می گیرد و متن پاسخ یا هر مجموعه ای از مقادیر را که می تواند با استفاده از make_response
به یک شی Response
تبدیل شود، برمی گرداند.
کش کردن اتصالات شبکه، مراجع کتابخانه و اشیاء کلاینت API در حوزه جهانی بسیار مهم است. برای مثال به بهینه سازی شبکه مراجعه کنید.
با تنظیم حداقل تعداد موارد شروع سرد را کاهش دهید
به طور پیشفرض، Cloud Functions تعداد نمونهها را بر اساس تعداد درخواستهای دریافتی مقیاس میدهد. میتوانید این رفتار پیشفرض را با تنظیم حداقل تعداد نمونههایی که Cloud Function باید برای ارائه درخواستها آماده نگه دارد، تغییر دهید. تنظیم حداقل تعداد نمونه شروع سرد برنامه شما را کاهش می دهد. اگر برنامه شما به تأخیر حساس است، توصیه میکنیم حداقل تعداد نمونهها را تنظیم کنید و مقداردهی اولیه را در زمان بارگذاری کامل کنید.
برای اطلاعات بیشتر در مورد این گزینههای زمان اجرا، رفتار مقیاسبندی کنترل را ببینید.نکاتی در مورد شروع سرد و مقداردهی اولیه
اولیه سازی جهانی در زمان بارگذاری اتفاق می افتد. بدون آن، اولین درخواست نیاز به تکمیل مقداردهی اولیه و بارگذاری ماژولها دارد، در نتیجه تاخیر بیشتری را متحمل میشود.
با این حال، مقداردهی اولیه جهانی نیز بر شروع سرد تأثیر دارد. برای به حداقل رساندن این تأثیر، فقط آنچه را که برای اولین درخواست لازم است، مقداردهی اولیه کنید تا تأخیر درخواست اول را تا حد امکان پایین نگه دارید.
اگر موارد min را همانطور که در بالا توضیح داده شد برای یک تابع حساس به تأخیر پیکربندی کرده باشید، به ویژه مهم است. در آن سناریو، تکمیل مقداردهی اولیه در زمان بارگذاری و ذخیره داده های مفید در حافظه پنهان، تضمین می کند که اولین درخواست نیازی به انجام آن ندارد و با تأخیر کم ارائه می شود.
اگر متغیرها را در دامنه جهانی مقداردهی اولیه کنید، بسته به زبان، زمانهای اولیهسازی طولانی میتواند منجر به دو رفتار شود: - برای ترکیبی از زبانها و کتابخانههای همگام، چارچوب تابع میتواند به صورت ناهمزمان اجرا شود و بلافاصله برگردد، که باعث میشود اجرای کد در پسزمینه ادامه یابد، که میتواند باعث مشکلاتی مانند عدم دسترسی به CPU شود. برای جلوگیری از این امر، باید مقدار دهی اولیه ماژول را همانطور که در زیر توضیح داده شده مسدود کنید. این همچنین تضمین می کند که درخواست ها تا زمانی که اولیه سازی کامل نشده است، ارائه نمی شوند. - از طرف دیگر، اگر مقداردهی اولیه همزمان باشد، زمان طولانیسازی اولیه باعث شروع سرد طولانیتر میشود، که میتواند بهویژه با توابع همزمانی کم در هنگام افزایش بار، مشکلساز باشد.
نمونه ای از پیش گرم کردن کتابخانه async node.js
Node.js با Firestore نمونه ای از کتابخانه async node.js است. به منظور استفاده از min_instance ها، کد زیر بارگذاری و مقداردهی اولیه را در زمان بارگذاری کامل می کند و بارگذاری ماژول را مسدود می کند.
TLA استفاده می شود، به این معنی که ES6 مورد نیاز است، با استفاده از پسوند .mjs
برای کد node.js یا افزودن type: module
به فایل package.json.
{ "main": "main.js", "type": "module", "dependencies": { "@google-cloud/firestore": "^7.10.0", "@google-cloud/functions-framework": "^3.4.5" } }
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. });
نمونه هایی از مقداردهی اولیه جهانی
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'); });
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}.")
این تابع HTTP از جهانی های اولیه با تنبلی استفاده می کند. یک شی درخواست ( flask.Request
) را می گیرد و متن پاسخ یا هر مجموعه ای از مقادیر را که می تواند با استفاده از make_response
به یک شی Response
تبدیل شود، برمی گرداند.
اگر چندین تابع را در یک فایل واحد تعریف کنید و توابع مختلف از متغیرهای متفاوتی استفاده کنند، این امر به ویژه مهم است. مگر اینکه از مقداردهی اولیه تنبل استفاده کنید، ممکن است منابع را بر روی متغیرهایی که مقداردهی اولیه شده اند اما هرگز استفاده نشده اند هدر دهید.
منابع اضافی
درباره بهینهسازی عملکرد در ویدیوی «اطلس عملکرد ابر Google» اطلاعات بیشتری کسب کنید Cloud Functions زمان بوت سرد .
،این سند بهترین روشها را برای طراحی، پیادهسازی، آزمایش و استقرار Cloud Functions توضیح میدهد.
صحت
این بخش بهترین شیوه های کلی برای طراحی و پیاده سازی Cloud Functions را شرح می دهد.
توابع idempotent را بنویسید
توابع شما باید نتیجه یکسانی داشته باشند حتی اگر چندین بار فراخوانی شوند. این به شما امکان می دهد اگر فراخوانی قبلی بخشی از کد شما انجام نشد، یک فراخوان را دوباره امتحان کنید. برای اطلاعات بیشتر، به تلاش مجدد توابع مبتنی بر رویداد مراجعه کنید.
فعالیت های پس زمینه را شروع نکنید
فعالیت پس زمینه هر چیزی است که پس از پایان عملکرد شما اتفاق می افتد. فراخوانی تابع پس از بازگرداندن تابع به پایان می رسد یا در غیر این صورت سیگنال تکمیل را نشان می دهد، مثلاً با فراخوانی آرگومان callback
در توابع رویداد محور Node.js. هر کدی که پس از خاتمه برازنده اجرا شود نمی تواند به CPU دسترسی داشته باشد و هیچ پیشرفتی نخواهد داشت.
علاوه بر این، هنگامی که فراخوانی بعدی در همان محیط اجرا می شود، فعالیت پس زمینه شما از سر گرفته می شود و در فراخوانی جدید تداخل ایجاد می کند. این ممکن است منجر به رفتارهای غیرمنتظره و خطاهایی شود که تشخیص آنها سخت است. دسترسی به شبکه پس از پایان عملکرد معمولاً منجر به تنظیم مجدد اتصالات می شود (کد خطای ECONNRESET
).
فعالیت پسزمینه را میتوان با یافتن هر چیزی که بعد از خطی که فراخوانی پایان یافت ثبت شده است، در گزارشهای مربوط به فراخوانهای فردی شناسایی کرد. گاهی اوقات میتوان فعالیت پسزمینه را عمیقتر در کد مدفون کرد، بهویژه زمانی که عملیات ناهمزمان مانند تماسها یا تایمرها وجود دارد. کد خود را مرور کنید تا مطمئن شوید که تمام عملیات ناهمزمان قبل از خاتمه عملکرد به پایان می رسد.
همیشه فایل های موقت را حذف کنید
ذخیره سازی دیسک محلی در دایرکتوری موقت یک فایل سیستم در حافظه است. فایل هایی که می نویسید حافظه موجود برای عملکرد شما را مصرف می کنند و گاهی اوقات بین فراخوانی ها باقی می مانند. عدم حذف صریح این فایل ها ممکن است در نهایت منجر به خطای کمبود حافظه و متعاقبا شروع سرد شود.
با انتخاب آن در لیست توابع در کنسول Google Cloud و انتخاب نمودار استفاده از حافظه، می توانید حافظه استفاده شده توسط یک تابع را مشاهده کنید.
اگر نیاز به دسترسی به فضای ذخیرهسازی طولانیمدت دارید، از نصبهای حجمی Cloud Run با حجمهای Cloud Storage یا NFS استفاده کنید.
هنگام پردازش فایل های بزرگتر با استفاده از خط لوله، می توانید نیاز به حافظه را کاهش دهید. برای مثال، میتوانید با ایجاد یک جریان خواندن، عبور دادن آن از طریق یک فرآیند مبتنی بر جریان و نوشتن جریان خروجی مستقیماً در فضای ذخیرهسازی ابری، یک فایل را در فضای ذخیرهسازی ابری پردازش کنید.
چارچوب توابع
برای اطمینان از اینکه وابستگیهای یکسان به طور مداوم در بین محیطها نصب میشوند، توصیه میکنیم که کتابخانه Functions Framework را در مدیریت بسته خود قرار دهید و وابستگی را به نسخه خاصی از Functions Framework پین کنید.
برای انجام این کار، نسخه دلخواه خود را در فایل قفل مربوطه قرار دهید (به عنوان مثال، package-lock.json
برای Node.js، یا requirements.txt
برای پایتون).
اگر Functions Framework به صراحت به عنوان یک وابستگی فهرست نشده باشد، به طور خودکار در طول فرآیند ساخت با استفاده از آخرین نسخه موجود اضافه می شود.
ابزار
این بخش دستورالعمل هایی در مورد نحوه استفاده از ابزارها برای پیاده سازی، آزمایش و تعامل با Cloud Functions ارائه می دهد.
توسعه محلی
استقرار تابع کمی زمان می برد، بنابراین آزمایش کد عملکرد خود به صورت محلی اغلب سریعتر است.
توسعه دهندگان Firebase می توانند از Firebase CLI Cloud Functions Emulator استفاده کنند.از وقفه های زمانی استقرار در طول اولیه سازی اجتناب کنید
اگر استقرار تابع شما با خطای مهلت زمانی ناموفق باشد، احتمالاً به این معنی است که اجرای کد دامنه جهانی عملکرد شما در طول فرآیند استقرار بیش از حد طول می کشد.
Firebase CLI دارای یک بازه زمانی پیشفرض برای کشف عملکردهای شما در حین استقرار است. اگر منطق اولیه سازی در کد منبع توابع شما (بارگیری ماژول ها، برقراری تماس های شبکه و غیره) از این مهلت زمانی فراتر رود، ممکن است استقرار با شکست مواجه شود.
برای جلوگیری از وقفه، از یکی از استراتژی های زیر استفاده کنید:
(توصیه می شود) onInit()
برای به تعویق انداختن مقداردهی اولیه استفاده کنید
از قلاب onInit()
برای جلوگیری از اجرای کد اولیه در حین استقرار استفاده کنید. کد داخل قلاب onInit()
فقط زمانی اجرا می شود که تابع در توابع Cloud Run مستقر شود، نه در طول خود فرآیند استقرار.
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}`); });
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()
استفاده کنید، می توانید زمان استقرار CLI را با استفاده از متغیر محیطی FUNCTIONS_DISCOVERY_TIMEOUT
افزایش دهید:
$ export FUNCTIONS_DISCOVERY_TIMEOUT=30
$ firebase deploy --only functions
از Sendgrid برای ارسال ایمیل استفاده کنید
Cloud Functions اجازه اتصالات خروجی را در پورت 25 نمی دهد، بنابراین نمی توانید اتصالات غیر ایمن به سرور SMTP برقرار کنید. روش توصیه شده برای ارسال ایمیل استفاده از یک سرویس شخص ثالث مانند SendGrid است. میتوانید گزینههای دیگری برای ارسال ایمیل در « ارسال ایمیل از یک آموزش نمونه برای موتور محاسباتی Google» بیابید.
عملکرد
این بخش بهترین روش ها برای بهینه سازی عملکرد را شرح می دهد.
از همزمانی کم خودداری کنید
از آنجایی که شروع های سرد گران هستند، امکان استفاده مجدد از نمونه هایی که اخیراً شروع شده اند در طول یک spike بهینه سازی عالی برای مدیریت بار است. محدود کردن همزمانی نحوه استفاده از نمونه های موجود را محدود می کند، بنابراین شروع سرد بیشتری را متحمل می شود.
افزایش همزمانی به به تعویق انداختن درخواستهای متعدد در هر نمونه کمک میکند و رسیدگی به بارگذاری را آسانتر میکند.از وابستگی ها عاقلانه استفاده کنید
از آنجایی که توابع بدون حالت هستند، محیط اجرا اغلب از ابتدا مقداردهی اولیه می شود (در طول چیزی که به عنوان شروع سرد شناخته می شود). هنگامی که شروع سرد رخ می دهد، زمینه کلی تابع ارزیابی می شود.
اگر توابع شما ماژولها را وارد میکنند، زمان بارگذاری آن ماژولها میتواند در زمان شروع سرد به تأخیر فراخوانی اضافه کند. میتوانید با بارگیری صحیح وابستگیها و بار نکردن وابستگیهایی که عملکرد شما از آنها استفاده نمیکند، این تأخیر و همچنین زمان لازم برای استقرار عملکرد خود را کاهش دهید.
از متغیرهای سراسری برای استفاده مجدد از اشیاء در فراخوانی های آینده استفاده کنید
هیچ تضمینی وجود ندارد که وضعیت یک تابع برای فراخوانی های آینده حفظ شود. با این حال، Cloud Functions اغلب محیط اجرای فراخوان قبلی را بازیافت می کند. اگر متغیری را در دامنه جهانی اعلام کنید، مقدار آن را میتوان در فراخوانیهای بعدی بدون نیاز به محاسبه مجدد مورد استفاده قرار داد.
به این ترتیب می توانید اشیایی را که ممکن است در هر فراخوانی تابع گران باشد، ذخیره کنید. انتقال چنین اشیایی از بدنه عملکرد به محدوده جهانی ممکن است منجر به بهبود عملکرد قابل توجهی شود. مثال زیر یک شی سنگین را فقط یک بار در هر نمونه تابع ایجاد می کند و آن را در تمام فراخوانی های تابعی که به نمونه داده شده می رسد به اشتراک می گذارد:
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}`); });
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}")
این تابع HTTP یک شی درخواست ( flask.Request
) می گیرد و متن پاسخ یا هر مجموعه ای از مقادیر را که می تواند با استفاده از make_response
به یک شی Response
تبدیل شود، برمی گرداند.
کش کردن اتصالات شبکه، مراجع کتابخانه و اشیاء کلاینت API در حوزه جهانی بسیار مهم است. برای مثال به بهینه سازی شبکه مراجعه کنید.
با تنظیم حداقل تعداد موارد شروع سرد را کاهش دهید
به طور پیشفرض، Cloud Functions تعداد نمونهها را بر اساس تعداد درخواستهای دریافتی مقیاس میدهد. میتوانید این رفتار پیشفرض را با تنظیم حداقل تعداد نمونههایی که Cloud Function باید برای ارائه درخواستها آماده نگه دارد، تغییر دهید. تنظیم حداقل تعداد نمونه شروع سرد برنامه شما را کاهش می دهد. اگر برنامه شما به تأخیر حساس است، توصیه میکنیم حداقل تعداد نمونهها را تنظیم کنید و مقداردهی اولیه را در زمان بارگذاری کامل کنید.
برای اطلاعات بیشتر در مورد این گزینههای زمان اجرا، رفتار مقیاسبندی کنترل را ببینید.نکاتی در مورد شروع سرد و مقداردهی اولیه
اولیه سازی جهانی در زمان بارگذاری اتفاق می افتد. بدون آن، اولین درخواست نیاز به تکمیل مقداردهی اولیه و بارگذاری ماژولها دارد، در نتیجه تاخیر بیشتری را متحمل میشود.
با این حال، مقداردهی اولیه جهانی نیز بر شروع سرد تأثیر دارد. برای به حداقل رساندن این تأثیر، فقط آنچه را که برای اولین درخواست لازم است، مقداردهی اولیه کنید تا تأخیر درخواست اول را تا حد امکان پایین نگه دارید.
اگر موارد min را همانطور که در بالا توضیح داده شد برای یک تابع حساس به تأخیر پیکربندی کرده باشید، به ویژه مهم است. در آن سناریو، تکمیل مقداردهی اولیه در زمان بارگذاری و ذخیره داده های مفید در حافظه پنهان، تضمین می کند که اولین درخواست نیازی به انجام آن ندارد و با تأخیر کم ارائه می شود.
اگر متغیرها را در دامنه جهانی آغاز کنید ، بسته به زبان ، زمان اولیه سازی طولانی می تواند منجر به دو رفتار شود: - برای برخی از ترکیبی از زبانها و کتابخانه های ASYNC ، چارچوب عملکرد می تواند به صورت ناهمزمان اجرا شود و بلافاصله بازگردد و باعث می شود کد در پس زمینه ادامه یابد ، که این امر می تواند مشکلاتی از قبیل دسترسی به CPU ایجاد کند. برای جلوگیری از این ، شما باید همانطور که در زیر توضیح داده شده است ، شروع به کار در ماژول کنید. این همچنین تضمین می کند که درخواست ها تا زمان کامل شدن اولیه ارائه نشوند. - از طرف دیگر ، اگر اولیه سازی همزمان باشد ، زمان اولیه سازی طولانی باعث شروع سرماخوردگی می شود ، که می تواند مسئله ای بخصوص با عملکردهای همزمانی کم در طول سنبله بار باشد.
نمونه ای از قبل از تولد یک کتابخانه async node.js
node.js با firestore نمونه ای از کتابخانه async node.js است. به منظور استفاده از min_instances ، کد زیر بارگذاری و اولیه سازی را در زمان بار انجام می دهد و در بارگیری ماژول مسدود می شود.
TLA استفاده می شود ، به این معنی که ES6 مورد نیاز است ، با استفاده از یک پسوند .mjs
برای کد node.js یا اضافه کردن type: module
به پرونده pack.json.
{ "main": "main.js", "type": "module", "dependencies": { "@google-cloud/firestore": "^7.10.0", "@google-cloud/functions-framework": "^3.4.5" } }
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. });
نمونه هایی از اولیه سازی جهانی
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'); });
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}.")
این تابع HTTP از گلوبال های تنبل بازی شده استفاده می کند. این یک شیء درخواست ( flask.Request
) می گیرد و متن پاسخ را برمی گرداند یا هر مجموعه ای از مقادیر را می توان با استفاده از make_response
به یک شی Response
تبدیل کرد.
این امر به ویژه در صورتی که چندین عملکرد را در یک فایل واحد تعریف کنید ، بسیار مهم است و توابع مختلف از متغیرهای مختلف استفاده می کنند. مگر اینکه از اولیه سازی تنبل استفاده کنید ، ممکن است منابع را بر روی متغیرهایی که اولیه شده اند ، هدر دهید اما هرگز مورد استفاده قرار نمی گیرند.
منابع اضافی
اطلاعات بیشتری در مورد بهینه سازی عملکرد در "Atlas Performance Google Performance" ویدیویی Cloud Functions زمان بوت سرد .
،این سند بهترین شیوه ها را برای طراحی ، اجرای ، آزمایش و استقرار Cloud Functions توصیف می کند.
صحت
در این بخش بهترین شیوه های کلی برای طراحی و اجرای Cloud Functions توضیح داده شده است.
توابع idempotent را بنویسید
کارکردهای شما باید حتی اگر چندین بار خوانده شود ، نتیجه مشابهی داشته باشد. این امر به شما امکان می دهد اگر دعوت قبلی از طریق کد شما به صورت بخشی از آن استفاده کند ، مجدداً فراخوانی را امتحان کنید. برای اطلاعات بیشتر ، به آزمایش مجدد توابع رویداد محور مراجعه کنید.
فعالیت های پس زمینه را شروع نکنید
فعالیت پس زمینه هر اتفاقی است که پس از خاتمه عملکرد شما اتفاق می افتد. یک فراخوان عملکرد پس از بازگشت عملکرد یا تکمیل سیگنال ها ، مانند تماس با آرگومان پاسخ callback
در Node.js ، به پایان می رسد. هر کد پس از خاتمه بخشنده ، نمی تواند به CPU دسترسی پیدا کند و هیچ پیشرفتی نخواهد داشت.
علاوه بر این ، هنگامی که یک دعوت بعدی در همان محیط اجرا می شود ، فعالیت پس زمینه شما از سر گرفته می شود و در دعوت جدید دخالت می کند. این ممکن است به رفتار و خطاهای غیر منتظره ای منجر شود که تشخیص آنها دشوار است. دسترسی به شبکه پس از خاتمه عملکرد ، معمولاً منجر به تنظیم مجدد اتصالات می شود (کد خطای ECONNRESET
).
فعالیت پس زمینه اغلب با پیدا کردن هر چیزی که پس از خط وارد شده است می تواند در سیاهههای مربوط به دعوت های فردی تشخیص داده شود. فعالیت پس زمینه گاهی اوقات می تواند عمیق تر در کد دفن شود ، به ویژه هنگامی که عملیات ناهمزمان مانند تماس تلفنی یا تایمر وجود داشته باشد. کد خود را مرور کنید تا مطمئن شوید که تمام عملیات ناهمزمان قبل از خاتمه عملکرد به پایان می رسد.
همیشه پرونده های موقت را حذف کنید
ذخیره سازی دیسک محلی در فهرست موقت یک سیستم فایل در حافظه است. پرونده هایی که می نویسید ، حافظه را در دسترس عملکرد خود مصرف می کنند و گاهی اوقات بین دعوت ها ادامه می دهند. عدم حذف صریح این پرونده ها ممکن است در نهایت منجر به خطای خارج از حافظه و شروع سرماخوردگی شود.
با انتخاب آن در لیست توابع موجود در کنسول Google Cloud و انتخاب نقشه استفاده از حافظه ، می توانید حافظه مورد استفاده توسط یک عملکرد فردی را مشاهده کنید.
اگر به دسترسی به فضای طولانی مدت نیاز دارید ، استفاده از مونت های حجم Cloud Run را با Cloud Storage یا حجم NFS در نظر بگیرید.
می توانید هنگام پردازش پرونده های بزرگتر با استفاده از لوله کشی ، نیازهای حافظه را کاهش دهید. به عنوان مثال ، شما می توانید با ایجاد یک جریان خوانده شده ، عبور از آن از طریق یک فرآیند مبتنی بر جریان و نوشتن جریان خروجی به طور مستقیم به ذخیره سازی ، یک فایل را در ذخیره سازی ابر پردازش کنید.
چارچوب توابع
برای اطمینان از نصب همان وابستگی ها به طور مداوم در محیط ها ، توصیه می کنیم کتابخانه Framework را در مدیر بسته خود درج کنید و وابستگی را به یک نسخه خاص از چارچوب توابع وارد کنید.
برای انجام این کار ، نسخه مورد نظر خود را در پرونده قفل مربوطه قرار دهید (به عنوان مثال ، package-lock.json
for node.js یا requirements.txt
برای پایتون).
اگر چارچوب توابع به صراحت به عنوان وابستگی ذکر نشده باشد ، با استفاده از آخرین نسخه موجود ، در طی فرآیند ساخت به طور خودکار اضافه می شود.
ابزار
در این بخش دستورالعمل هایی در مورد نحوه استفاده از ابزارهایی برای پیاده سازی ، آزمایش و تعامل با Cloud Functions ارائه شده است.
توسعه محلی
استقرار عملکرد کمی زمان می برد ، بنابراین اغلب آزمایش کد عملکرد خود به صورت محلی سریعتر است.
توسعه دهندگان Firebase می توانند از شبیه ساز Cloud Functions CLI CLI Firebase استفاده کنند.در هنگام اولیه سازی از زمان استقرار خودداری کنید
اگر استقرار عملکرد شما با یک خطای زمان انجام نشود ، احتمالاً به این معنی است که کد دامنه جهانی عملکرد شما برای اجرای در طی فرآیند استقرار خیلی طولانی است.
Firebase CLI برای کشف کارکردهای شما در حین استقرار یک زمان پیش فرض دارد. اگر منطق اولیه سازی در کد منبع توابع شما (بارگذاری ماژول ها ، برقراری تماس های شبکه ، بنابراین) از این زمان فراتر رود ، ممکن است استقرار انجام شود.
برای جلوگیری از زمان بندی ، از یکی از استراتژی های زیر استفاده کنید:
(توصیه می شود) onInit()
برای به تعویق انداختن اولیه استفاده کنید
برای جلوگیری از اجرای کد اولیه سازی در هنگام استقرار ، از قلاب onInit()
استفاده کنید. کد داخل onInit()
قلاب فقط هنگامی اجرا می شود که عملکرد به توابع Cloud Run اعزام شود ، نه در طی خود فرآیند استقرار.
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}`); });
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
، زمان استقرار CLI را افزایش دهید:
$ export FUNCTIONS_DISCOVERY_TIMEOUT=30
$ firebase deploy --only functions
برای ارسال ایمیل از SendGrid استفاده کنید
Cloud Functions اجازه اتصالات برون مرزی در پورت 25 را نمی دهد ، بنابراین شما نمی توانید اتصالات غیر ایمن را به یک سرور SMTP برقرار کنید. روش پیشنهادی برای ارسال ایمیل استفاده از سرویس شخص ثالث مانند SendGrid است. می توانید گزینه های دیگری برای ارسال ایمیل در ایمیل ارسال از یک آموزش نمونه برای Google Compute Engine پیدا کنید.
عملکرد
این بخش بهترین روش ها برای بهینه سازی عملکرد را شرح می دهد.
از همزمانی کم خودداری کنید
از آنجا که شروع سرماخوردگی گران است ، قادر به استفاده مجدد از موارد اخیراً در طول سنبله ، بهینه سازی بسیار خوبی برای رسیدگی به بار است. محدود کردن همزمانی محدودیت چگونگی استفاده از موارد موجود را محدود می کند ، بنابراین شروع به سرماخوردگی می کند.
افزایش همزمانی به تعویق درخواست های متعدد در هر نمونه کمک می کند و باعث می شود سنبله های بار آسانتر شوند.از وابستگی ها عاقلانه استفاده کنید
از آنجا که توابع بدون تابعیت هستند ، محیط اجرای اغلب از ابتدا آغاز می شود (در طول آنچه که به عنوان شروع سرما شناخته می شود). هنگامی که یک شروع سرما رخ می دهد ، زمینه جهانی عملکرد ارزیابی می شود.
اگر توابع شما ماژول ها را وارد می کند ، زمان بار برای آن ماژول ها می تواند در هنگام شروع سرد به تأخیر دعوت اضافه شود. شما می توانید با بارگیری صحیح وابستگی ها و عدم بارگذاری وابستگی های عملکرد شما ، این تأخیر و همچنین زمان لازم برای استقرار عملکرد خود را کاهش دهید.
از متغیرهای جهانی برای استفاده مجدد از اشیاء در دعوت های آینده استفاده کنید
هیچ تضمینی وجود ندارد که وضعیت یک عملکرد برای دعوت های آینده حفظ شود. با این حال ، Cloud Functions اغلب محیط اجرای یک دعوت قبلی را بازیافت می کند. اگر متغیر را در دامنه جهانی اعلام کنید ، می توان مقدار آن را در دعوت های بعدی بدون نیاز به بازپرداخت مجدد استفاده کرد.
به این ترتیب می توانید اشیاء را ذخیره کنید که ممکن است برای بازآفرینی در هر یک از دعوت های عملکردی گران باشند. انتقال چنین اشیاء از بدنه عملکرد به دامنه جهانی ممکن است منجر به بهبود عملکرد قابل توجهی شود. مثال زیر یک شیء سنگین را فقط یک بار در هر نمونه عملکرد ایجاد می کند و آن را در تمام دعوت های عملکردی که به نمونه داده شده می رسند به اشتراک می گذارد:
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}`); });
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}")
این عملکرد HTTP یک شیء درخواست ( flask.Request
) را می گیرد و متن پاسخ را برمی گرداند یا هر مجموعه ای از مقادیر را می توان با استفاده از make_response
به یک شی Response
تبدیل کرد.
این امر به ویژه برای اتصالات شبکه ، منابع کتابخانه و اشیاء مشتری API در دامنه جهانی بسیار مهم است. برای مثال به بهینه سازی شبکه مراجعه کنید.
با تعیین حداقل تعداد موارد ، شروع سرما را کاهش دهید
به طور پیش فرض ، عملکرد Cloud تعداد موارد را بر اساس تعداد درخواست های دریافتی مقیاس می دهد. شما می توانید این رفتار پیش فرض را با تعیین حداقل تعداد مواردی که توابع ابری باید برای ارائه درخواست ها آماده باشند ، تغییر دهید. تنظیم حداقل تعداد موارد باعث کاهش سرماخوردگی برنامه شما می شود. توصیه می کنیم اگر برنامه شما حساس به تأخیر باشد ، حداقل تعداد موارد را تنظیم کنید و اولیه سازی را در زمان بار انجام دهید.
برای اطلاعات بیشتر در مورد این گزینه های زمان اجرا ، به رفتار مقیاس گذاری کنترل مراجعه کنید.یادداشت هایی در مورد شروع سرد و اولیه سازی
اولیه سازی جهانی در زمان بار اتفاق می افتد. بدون آن ، اولین درخواست نیاز به تکمیل ماژول های اولیه سازی و بارگذاری دارد و از این طریق تأخیر بیشتری متحمل می شود.
با این حال ، اولیه سازی جهانی نیز در شروع سرما تأثیر دارد. برای به حداقل رساندن این تأثیر ، فقط آنچه را که برای اولین درخواست لازم است ، تنظیم کنید تا اولین تأخیر درخواست را تا حد ممکن نگه دارید.
این امر به ویژه در صورتی مهم است که ماین نمونه هایی را که در بالا برای یک عملکرد حساس به تأخیر توضیح داده شده است ، پیکربندی کرده اید. در آن سناریو ، تکمیل اولیه سازی در زمان بار و ذخیره کردن داده های مفید تضمین می کند که اولین درخواست نیازی به انجام آن ندارد و با تأخیر کم ارائه می شود.
اگر متغیرها را در دامنه جهانی آغاز کنید ، بسته به زبان ، زمان اولیه سازی طولانی می تواند منجر به دو رفتار شود: - برای برخی از ترکیبی از زبانها و کتابخانه های ASYNC ، چارچوب عملکرد می تواند به صورت ناهمزمان اجرا شود و بلافاصله بازگردد و باعث می شود کد در پس زمینه ادامه یابد ، که این امر می تواند مشکلاتی از قبیل دسترسی به CPU ایجاد کند. برای جلوگیری از این ، شما باید همانطور که در زیر توضیح داده شده است ، شروع به کار در ماژول کنید. این همچنین تضمین می کند که درخواست ها تا زمان کامل شدن اولیه ارائه نشوند. - از طرف دیگر ، اگر اولیه سازی همزمان باشد ، زمان اولیه سازی طولانی باعث شروع سرماخوردگی می شود ، که می تواند مسئله ای بخصوص با عملکردهای همزمانی کم در طول سنبله بار باشد.
نمونه ای از قبل از تولد یک کتابخانه async node.js
node.js با firestore نمونه ای از کتابخانه async node.js است. به منظور استفاده از min_instances ، کد زیر بارگذاری و اولیه سازی را در زمان بار انجام می دهد و در بارگیری ماژول مسدود می شود.
TLA استفاده می شود ، به این معنی که ES6 مورد نیاز است ، با استفاده از یک پسوند .mjs
برای کد node.js یا اضافه کردن type: module
به پرونده pack.json.
{ "main": "main.js", "type": "module", "dependencies": { "@google-cloud/firestore": "^7.10.0", "@google-cloud/functions-framework": "^3.4.5" } }
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. });
نمونه هایی از اولیه سازی جهانی
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'); });
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}.")
این تابع HTTP از گلوبال های تنبل بازی شده استفاده می کند. این یک شیء درخواست ( flask.Request
) می گیرد و متن پاسخ را برمی گرداند یا هر مجموعه ای از مقادیر را می توان با استفاده از make_response
به یک شی Response
تبدیل کرد.
این امر به ویژه در صورتی که چندین عملکرد را در یک فایل واحد تعریف کنید ، بسیار مهم است و توابع مختلف از متغیرهای مختلف استفاده می کنند. مگر اینکه از اولیه سازی تنبل استفاده کنید ، ممکن است منابع را بر روی متغیرهایی که اولیه شده اند ، هدر دهید اما هرگز مورد استفاده قرار نمی گیرند.
منابع اضافی
اطلاعات بیشتری در مورد بهینه سازی عملکرد در "Atlas Performance Google Performance" ویدیویی Cloud Functions زمان بوت سرد .