این سند بهترین روشها را برای طراحی، پیادهسازی، آزمایش و استقرار 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 استفاده کنند.از Sendgrid برای ارسال ایمیل استفاده کنید
Cloud Functions اجازه اتصالات خروجی را در پورت 25 نمی دهد، بنابراین نمی توانید اتصالات غیر ایمن به سرور SMTP برقرار کنید. روش توصیه شده برای ارسال ایمیل استفاده از یک سرویس شخص ثالث مانند SendGrid است. میتوانید گزینههای دیگری برای ارسال ایمیل در «ارسال ایمیل از یک آموزش نمونه برای موتور محاسباتی Google» بیابید.
عملکرد
این بخش بهترین روش ها برای بهینه سازی عملکرد را شرح می دهد.
از همزمانی کم خودداری کنید
از آنجایی که شروع های سرد گران هستند، امکان استفاده مجدد از نمونه هایی که اخیراً شروع شده اند در طول یک spike بهینه سازی عالی برای مدیریت بار است. محدود کردن همزمانی نحوه استفاده از نمونه های موجود را محدود می کند، بنابراین شروع سرد بیشتری را متحمل می شود.
افزایش همزمانی به به تعویق انداختن درخواستهای متعدد در هر نمونه کمک میکند و رسیدگی به بارگذاری را آسانتر میکند.از وابستگی ها عاقلانه استفاده کنید
از آنجایی که توابع بدون حالت هستند، محیط اجرا اغلب از ابتدا مقداردهی اولیه می شود (در طول چیزی که به عنوان شروع سرد شناخته می شود). هنگامی که شروع سرد رخ می دهد، زمینه کلی تابع ارزیابی می شود.
اگر توابع شما ماژولها را وارد میکنند، زمان بارگذاری آن ماژولها میتواند در زمان شروع سرد به تأخیر فراخوانی اضافه کند. میتوانید با بارگیری صحیح وابستگیها و بار نکردن وابستگیهایی که عملکرد شما از آنها استفاده نمیکند، این تأخیر و همچنین زمان لازم برای استقرار عملکرد خود را کاهش دهید.
از متغیرهای سراسری برای استفاده مجدد از اشیاء در فراخوانی های آینده استفاده کنید
هیچ تضمینی وجود ندارد که وضعیت یک تابع برای فراخوانی های آینده حفظ شود. با این حال، 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}`); });
پایتون
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" } }
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'); });
پایتون
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 زمان بوت سرد .