เอกสารนี้อธิบายแนวทางปฏิบัติแนะนำสำหรับการออกแบบ การติดตั้งใช้งาน การทดสอบ และการนำ Cloud Functions ไปใช้งาน
ความถูกต้อง
ส่วนนี้จะอธิบายแนวทางปฏิบัติแนะนำทั่วไปสำหรับการออกแบบและการใช้งาน Cloud Functions
เขียนฟังก์ชันที่ idempotent
ฟังก์ชันควรให้ผลลัพธ์เดียวกันแม้ว่าจะเรียกใช้หลายครั้งก็ตาม ซึ่งจะช่วยให้คุณลองเรียกใช้อีกครั้งได้หากการเรียกใช้ก่อนหน้านี้ดำเนินการไม่สำเร็จกลางคัน ดูข้อมูลเพิ่มเติมได้ที่การลองใช้ฟังก์ชันที่ทำงานตามเหตุการณ์อีกครั้ง
ไม่เริ่มกิจกรรมในเบื้องหลัง
กิจกรรมเบื้องหลังคือสิ่งที่เกิดขึ้นหลังจากที่ฟังก์ชันสิ้นสุดลง
การเรียกใช้ฟังก์ชันจะเสร็จสมบูรณ์เมื่อฟังก์ชันแสดงผลหรือส่งสัญญาณการทำงานเสร็จสิ้น เช่น การเรียกใช้อาร์กิวเมนต์ callback
ในฟังก์ชันที่ทำงานตามเหตุการณ์ของ Node.js โค้ดที่ทำงานหลังจากการสิ้นสุดอย่างราบรื่นจะเข้าถึง CPU ไม่ได้และจะไม่ทํางานใดๆ
นอกจากนี้ เมื่อเรียกใช้ครั้งถัดไปในสภาพแวดล้อมเดียวกัน กิจกรรมเบื้องหลังจะกลับมาทำงานต่อ ซึ่งจะรบกวนการเรียกใช้ใหม่ ซึ่งอาจทําให้เกิดลักษณะการทํางานที่ไม่คาดคิดและข้อผิดพลาดที่วิเคราะห์ได้ยาก การเข้าถึงเครือข่ายหลังจากฟังก์ชันสิ้นสุดการทำงานมักจะทําให้การเชื่อมต่อรีเซ็ต (ECONNRESET
รหัสข้อผิดพลาด)
กิจกรรมเบื้องหลังมักตรวจพบได้ในบันทึกจากคําเรียกแต่ละรายการ โดยค้นหาสิ่งที่บันทึกไว้หลังบรรทัดที่ระบุว่าคําเรียกเสร็จสิ้นแล้ว บางครั้งกิจกรรมเบื้องหลังอาจฝังอยู่ในโค้ดลึกลงไป โดยเฉพาะเมื่อมีการดำเนินการแบบไม่พร้อมกัน เช่น การเรียกกลับหรือตัวจับเวลา ตรวจสอบโค้ดเพื่อให้แน่ใจว่าการดำเนินการแบบอะซิงโครนัสทั้งหมดเสร็จสิ้นแล้วก่อนที่จะสิ้นสุดฟังก์ชัน
ลบไฟล์ชั่วคราวเสมอ
พื้นที่เก็บข้อมูลในดิสก์ในเครื่องของไดเรกทอรีชั่วคราวคือระบบไฟล์ในหน่วยความจำ ไฟล์ที่คุณเขียนจะใช้หน่วยความจําที่มีให้ฟังก์ชัน และบางครั้งจะยังคงอยู่ระหว่างการเรียกใช้ การลบไฟล์เหล่านี้อย่างชัดเจนไม่สำเร็จอาจส่งผลให้เกิดข้อผิดพลาดหน่วยความจำไม่เพียงพอและระบบต้องเริ่มต้นใหม่ในภายหลัง
คุณสามารถดูหน่วยความจำที่ฟังก์ชันแต่ละรายการใช้ได้โดยเลือกฟังก์ชันนั้นในรายการฟังก์ชันในคอนโซล Google Cloud แล้วเลือกผังการใช้หน่วยความจำ
อย่าพยายามเขียนนอกไดเรกทอรีชั่วคราว และอย่าลืมใช้วิธีการที่ไม่ขึ้นอยู่กับแพลตฟอร์ม/ระบบปฏิบัติการเพื่อสร้างเส้นทางไฟล์
คุณลดความต้องการหน่วยความจำได้เมื่อประมวลผลไฟล์ขนาดใหญ่โดยใช้ไปป์ไลน์ เช่น คุณสามารถประมวลผลไฟล์ใน Cloud Storage ได้โดยการสร้างสตรีมการอ่าน ส่งผ่านกระบวนการที่อิงตามสตรีม และเขียนสตรีมเอาต์พุตไปยัง Cloud Storage โดยตรง
เฟรมเวิร์กฟังก์ชัน
เมื่อคุณทําให้ฟังก์ชันใช้งานได้ ระบบจะเพิ่มเฟรมเวิร์กฟังก์ชันเป็นข้อกําหนดโดยอัตโนมัติโดยใช้เวอร์ชันปัจจุบัน เราขอแนะนำให้คุณปักหมุดฟังก์ชันไว้กับเฟรมเวิร์กฟังก์ชันเวอร์ชันที่เจาะจงเพื่อให้แน่ใจว่ามีการติดตั้ง Dependency เดียวกันอย่างสอดคล้องกันในสภาพแวดล้อมต่างๆ
โดยให้ระบุเวอร์ชันที่ต้องการในไฟล์ล็อกที่เกี่ยวข้อง (เช่น package-lock.json
สำหรับ Node.js หรือ requirements.txt
สำหรับ Python)
เครื่องมือ
ส่วนนี้จะให้หลักเกณฑ์เกี่ยวกับวิธีใช้เครื่องมือเพื่อติดตั้งใช้งาน ทดสอบ และโต้ตอบกับ Cloud Functions
การพัฒนาในพื้นที่
การปรับใช้งานฟังก์ชันจะใช้เวลาสักครู่ การทดสอบโค้ดของฟังก์ชันในเครื่องจึงมักจะเร็วกว่า
นักพัฒนาแอป Firebase สามารถใช้โปรแกรมจำลอง Cloud FunctionsFirebase CLIใช้ Sendgrid เพื่อส่งอีเมล
Cloud Functions ไม่อนุญาตให้มีการเชื่อมต่อขาออกในพอร์ต 25 คุณจึงทำการเชื่อมต่อที่ไม่ปลอดภัยกับเซิร์ฟเวอร์ SMTP ไม่ได้ วิธีส่งอีเมลที่แนะนําคือการใช้ SendGrid คุณดูตัวเลือกอื่นๆ สำหรับการส่งอีเมลได้ในบทแนะนำการส่งอีเมลจากอินสแตนซ์สำหรับ Google Compute Engine
ประสิทธิภาพ
ส่วนนี้จะอธิบายแนวทางปฏิบัติแนะนำในการเพิ่มประสิทธิภาพ
ใช้ Dependency อย่างชาญฉลาด
เนื่องจากฟังก์ชันไม่มีสถานะ สภาพแวดล้อมการดําเนินการจึงมักจะเริ่มต้นจากต้น (ในช่วงที่เรียกว่าการเริ่มต้นแบบเย็น) เมื่อเกิด Cold Start ระบบจะประเมินบริบทส่วนกลางของฟังก์ชัน
หากฟังก์ชันนําเข้าโมดูล เวลาในการโหลดโมดูลเหล่านั้นอาจเพิ่มเวลาในการตอบสนองของการเรียกใช้ระหว่างการเริ่มต้นระบบแบบเย็น คุณสามารถลดเวลาในการตอบสนองนี้ รวมถึงเวลาที่จำเป็นในการทำให้ฟังก์ชันใช้งานได้ โดยการโหลดไลบรารีอย่างถูกต้องและไม่โหลดไลบรารีที่ฟังก์ชันไม่ได้ใช้
ใช้ตัวแปรส่วนกลางเพื่อใช้ออบเจ็กต์ซ้ำในการเรียกใช้ในอนาคต
ไม่มีการรับประกันว่าระบบจะเก็บสถานะของฟังก์ชันไว้สําหรับการเรียกใช้ในอนาคต อย่างไรก็ตาม 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}")
ฟังก์ชัน HTTP นี้จะรับออบเจ็กต์คำขอ (flask.Request
) และแสดงผลข้อความตอบกลับ หรือชุดค่าผสมใดๆ ที่เปลี่ยนให้เป็นออบเจ็กต์ Response
ได้โดยใช้ make_response
การแคชการเชื่อมต่อเครือข่าย การอ้างอิงไลบรารี และออบเจ็กต์ไคลเอ็นต์ API ในขอบเขตส่วนกลางมีความสําคัญอย่างยิ่ง ดูตัวอย่างได้ที่การเพิ่มประสิทธิภาพการทํางานร่วมกันของเครือข่าย
ทำการเริ่มต้นตัวแปรส่วนกลางแบบเลื่อนเวลา
หากคุณเริ่มต้นตัวแปรในขอบเขตส่วนกลาง ระบบจะเรียกใช้โค้ดเริ่มต้นผ่านการเรียกใช้แบบเริ่มต้นใหม่เสมอ ซึ่งจะเพิ่มเวลาในการตอบสนองของฟังก์ชัน
ในบางกรณี การดำเนินการนี้อาจทําให้บริการที่เรียกใช้หมดเวลาเป็นระยะๆ หากไม่จัดการอย่างเหมาะสมในบล็อก try
/catch
หากไม่มีการใช้ออบเจ็กต์บางรายการในเส้นทางโค้ดทั้งหมด ให้พิจารณาเริ่มต้นออบเจ็กต์เหล่านั้นแบบตามต้องการ
Node.js
const functions = require('firebase-functions'); let myCostlyVariable; exports.function = functions.https.onRequest((req, res) => { doUsualWork(); if(unlikelyCondition()){ myCostlyVariable = myCostlyVariable || buildCostlyVariable(); } res.status(200).send('OK'); });
Python
from firebase_functions import https_fn # Always initialized (at cold-start) non_lazy_global = file_wide_computation() # Declared at cold-start, but only initialized if/when the function executes lazy_global = None @https_fn.on_request() def lazy_globals(request): global lazy_global, non_lazy_global # This value is initialized only if (and when) the function is called if not lazy_global: lazy_global = function_specific_computation() return https_fn.Response(f"Lazy: {lazy_global}, non-lazy: {non_lazy_global}.")
ฟังก์ชัน HTTP นี้ใช้ตัวแปรส่วนกลางที่เริ่มต้นแบบล่าช้า โดยจะใช้ออบเจ็กต์คำขอ (flask.Request
) และแสดงผลข้อความตอบกลับ หรือชุดค่าผสมใดๆ ที่เปลี่ยนให้เป็นออบเจ็กต์ Response
ได้โดยใช้ make_response
ซึ่งสำคัญอย่างยิ่งหากคุณกำหนดฟังก์ชันหลายรายการในไฟล์เดียว และฟังก์ชันต่างๆ ใช้ตัวแปรที่แตกต่างกัน คุณอาจสิ้นเปลืองทรัพยากรไปกับตัวแปรที่เริ่มต้นแต่ไม่เคยใช้ เว้นแต่จะใช้การเริ่มต้นแบบเลื่อนเวลา
ลดจำนวน Cold Start โดยการกําหนดจํานวนอินสแตนซ์ขั้นต่ำ
โดยค่าเริ่มต้น Cloud Functions จะปรับขนาดจำนวนอินสแตนซ์ตามจำนวนคำขอขาเข้า คุณสามารถเปลี่ยนลักษณะการทำงานเริ่มต้นนี้ได้โดยการตั้งค่าจำนวนอินสแตนซ์ขั้นต่ำที่ Cloud Functions ต้องพร้อมให้บริการคำขอ การตั้งค่าจำนวนอินสแตนซ์ขั้นต่ำจะลดจำนวนครั้งที่แอปพลิเคชันต้องทำการ Cold Start เราขอแนะนำให้ตั้งค่าจำนวนอินสแตนซ์ขั้นต่ำหากแอปพลิเคชันของคุณมีความไวต่อเวลาในการตอบสนอง
ดูข้อมูลเพิ่มเติมเกี่ยวกับตัวเลือกรันไทม์เหล่านี้ได้ที่หัวข้อควบคุมลักษณะการการปรับขนาดแหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมเกี่ยวกับการเพิ่มประสิทธิภาพในวิดีโอ "Google Cloud Performance Atlas" Cloud Functions เวลาในการบูตเย็น