เคล็ดลับและคำแนะนำ

เอกสารนี้อธิบายแนวทางปฏิบัติแนะนำสำหรับการออกแบบ การติดตั้งใช้งาน การทดสอบ และการนำ 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 ระบบจะประเมินบริบทส่วนกลางของฟังก์ชัน

หากฟังก์ชันนําเข้าโมดูล เวลาในการโหลดโมดูลเหล่านั้นอาจเพิ่มเวลาในการตอบสนองของการเรียกใช้ระหว่างการเริ่มต้นระบบแบบเย็น คุณลดเวลาในการตอบสนองนี้ รวมถึงเวลาที่ใช้ในการทำให้ฟังก์ชันใช้งานได้ด้วยการโหลดทรัพยากร Dependency อย่างถูกต้องและไม่โหลดทรัพยากร Dependency ที่ฟังก์ชันไม่ได้ใช้งาน

ใช้ตัวแปรร่วมเพื่อนำออบเจ็กต์มาใช้ซ้ำในการเรียกใช้ในอนาคต

ไม่มีการรับประกันว่าจะเก็บรักษาสถานะของฟังก์ชันไว้สำหรับการเรียกใช้ในอนาคต อย่างไรก็ตาม 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 เวลาในการบูตเย็น