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

เอกสารฉบับนี้อธิบายแนวทางปฏิบัติที่ดีที่สุดในการออกแบบ การติดตั้งใช้งาน การทดสอบ และการทำให้ Cloud Functions ใช้งานได้

ความถูกต้อง

ส่วนนี้จะอธิบายแนวทางปฏิบัติแนะนำทั่วไปสำหรับการออกแบบและนำไปใช้ Cloud Functions

เขียนฟังก์ชันเอกลักษณ์

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

ไม่ต้องเริ่มกิจกรรมในเบื้องหลัง

กิจกรรมในเบื้องหลังคือสิ่งที่เกิดขึ้นหลังจากฟังก์ชันสิ้นสุดลง การเรียกใช้ฟังก์ชันจะเสร็จสิ้นเมื่อฟังก์ชันแสดงผลหรือมีสัญญาณอย่างอื่น เช่น การเรียกใช้อาร์กิวเมนต์ callback ใน Node.js ที่ขับเคลื่อนด้วยเหตุการณ์ โค้ดใดๆ ที่เรียกใช้หลังจากการสิ้นสุดอย่างค่อยเป็นค่อยไปจะเข้าถึง CPU ไม่ได้และ จะไม่คืบหน้า

นอกจากนี้ เมื่อมีการเรียกใช้การเรียกใช้ครั้งต่อๆ ไปในสภาพแวดล้อมเดียวกัน กิจกรรมในเบื้องหลังจะกลับมาทำงานอีกครั้ง ซึ่งจะรบกวนการเรียกใช้ใหม่ ซึ่งอาจ ทำให้เกิดลักษณะการทำงานที่ไม่คาดคิดและข้อผิดพลาดซึ่งวิเคราะห์ได้ยาก การเข้าถึง เครือข่ายหลังจากฟังก์ชันสิ้นสุดลงมักจะทำให้การเชื่อมต่อได้รับการรีเซ็ต (รหัสข้อผิดพลาด ECONNRESET)

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

ลบไฟล์ชั่วคราวเสมอ

ที่จัดเก็บดิสก์ในเครื่องในไดเรกทอรีชั่วคราวเป็นระบบไฟล์ในหน่วยความจำ ไฟล์ ที่คุณเขียนจะใช้หน่วยความจำที่มีให้กับฟังก์ชัน และบางครั้งก็คงอยู่ ระหว่างการเรียกใช้ได้ การไม่ลบไฟล์เหล่านี้อย่างชัดแจ้งอาจท้ายที่สุด ทำให้เกิดข้อผิดพลาดนอกหน่วยความจำและ Cold Start ที่ตามมา

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

อย่าพยายามเขียนนอกไดเรกทอรีชั่วคราว และโปรดใช้ เมธอดในการสร้างเส้นทางไฟล์ที่ไม่ขึ้นอยู่กับแพลตฟอร์ม/ระบบปฏิบัติการ

คุณลดความต้องการหน่วยความจำได้เมื่อประมวลผลไฟล์ขนาดใหญ่โดยใช้ไปป์ไลน์ เช่น คุณสามารถประมวลผลไฟล์ใน Cloud Storage โดยการสร้างสตรีมการอ่าน ผ่านกระบวนการแบบสตรีม และเขียนสตรีมเอาต์พุต ไปยัง Cloud Storage โดยตรง

เฟรมเวิร์กฟังก์ชัน

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

โดยใส่เวอร์ชันที่ต้องการไว้ในไฟล์ล็อกที่เกี่ยวข้อง (เช่น package-lock.json สำหรับ Node.js หรือ requirements.txt สำหรับ Python)

เครื่องมือ

ส่วนนี้จะแสดงหลักเกณฑ์วิธีใช้เครื่องมือเพื่อติดตั้งใช้งาน ทดสอบ และ โต้ตอบกับ Cloud Functions

การพัฒนาในพื้นที่

การทำให้ฟังก์ชันใช้งานได้จะใช้เวลาเล็กน้อย การทดสอบโค้ดจึงมักรวดเร็วกว่า ของฟังก์ชันภายในเครื่อง

นักพัฒนาซอฟต์แวร์ Firebase สามารถใช้ โปรแกรมจำลอง Firebase CLI Cloud Functions

ใช้ Sendgrid เพื่อส่งอีเมล

Cloud Functions ไม่อนุญาตการเชื่อมต่อขาออกบนพอร์ต 25 คุณจึงดำเนินการไม่ได้ ทำการเชื่อมต่อที่ไม่ปลอดภัยกับเซิร์ฟเวอร์ SMTP วิธีที่แนะนำในการส่ง อีเมลคือการใช้ SendGrid คุณดูตัวเลือกอื่นๆ ได้ ในการส่งอีเมลใน การส่งอีเมลจากอินสแตนซ์ สำหรับ Google Compute Engine

ประสิทธิภาพ

ส่วนนี้จะอธิบายแนวทางปฏิบัติแนะนำในการเพิ่มประสิทธิภาพ

ใช้ทรัพยากร Dependency อย่างชาญฉลาด

เนื่องจากฟังก์ชันไม่เก็บสถานะ สภาพแวดล้อมของการดำเนินการจึงมักจะเริ่มต้นอยู่แล้ว ใหม่ตั้งแต่ต้น (ในช่วงที่เรียกว่า Cold Start) เมื่อเกิด Cold Start ระบบจะประเมินบริบทส่วนกลางของฟังก์ชันดังกล่าว

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

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

ไม่มีการรับประกันว่าจะมีการเก็บรักษาสถานะของ Cloud Function ไว้สำหรับ คำขอในอนาคต อย่างไรก็ตาม Cloud Functions มักจะนำการดำเนินการกลับมาใช้ใหม่ ของการเรียกใช้ก่อนหน้า ถ้าคุณประกาศตัวแปรในขอบเขตรวม ใช้ค่าในการเรียกครั้งต่อๆ ไปได้โดยไม่ต้องมี คำนวณใหม่

วิธีนี้จะทำให้คุณแคชออบเจ็กต์ที่อาจมีราคาแพงในการสร้างขึ้นมาใหม่ได้ในแต่ละกรณี การเรียกใช้ฟังก์ชัน ย้ายวัตถุดังกล่าวจากส่วนฟังก์ชันไปยังขอบเขตรวม อาจส่งผลให้ประสิทธิภาพดีขึ้นอย่างมาก ตัวอย่างต่อไปนี้ สร้างวัตถุหนักเพียง 1 ครั้งต่ออินสแตนซ์ของฟังก์ชัน และแชร์ การเรียกใช้ฟังก์ชันที่ไปถึงอินสแตนซ์ที่ระบุ

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 ในขอบเขตทั้งหมด ดูการเพิ่มประสิทธิภาพเครือข่ายสำหรับตัวอย่าง

การเริ่มต้นแบบ Lazy Loading ของตัวแปรร่วม

หากคุณเริ่มต้นตัวแปรในขอบเขตรวม โค้ดเริ่มต้นจะ เรียกใช้ผ่านการเรียกใช้ Cold Start ซึ่งจะเพิ่มเวลาในการตอบสนองของฟังก์ชัน ในบางกรณี การจำกัดนี้จะทำให้บริการที่เรียกใช้อยู่หมดเวลาเป็นช่วงๆ หากไม่มีการจัดการอย่างเหมาะสมในบล็อก try/catch ถ้า ออบเจ็กต์บางรายการไม่ได้ใช้ในเส้นทางโค้ดทั้งหมด ลองเริ่มต้นใช้งานแบบ Lazy Loading ออนดีมานด์:

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 นี้ใช้ส่วนกลางที่เริ่มต้นแบบ Lazily รับออบเจ็กต์คำขอ (flask.Request) และแสดงผลข้อความตอบกลับหรือชุดของค่าที่ เปลี่ยนเป็นออบเจ็กต์ Response ได้โดยใช้ make_response

ซึ่งมีความสำคัญอย่างยิ่งหากคุณกำหนดหลายฟังก์ชันในไฟล์เดียว และฟังก์ชันต่างกันจะใช้ตัวแปรที่ต่างกัน ยกเว้นในกรณีที่คุณใช้โหมด Lazy Loading คุณอาจสิ้นเปลืองทรัพยากรไปกับตัวแปรที่ได้กำหนดขึ้นแต่แรก ไม่เคยใช้

ลด Cold Start โดยการตั้งค่าจำนวนอินสแตนซ์ขั้นต่ำ

โดยค่าเริ่มต้น Cloud Functions จะปรับขนาดจำนวนอินสแตนซ์ตาม จำนวนคำขอที่เข้ามาใหม่ คุณสามารถเปลี่ยนลักษณะการทำงานเริ่มต้นนี้ได้โดยการตั้งค่า จำนวนอินสแตนซ์ขั้นต่ำที่ Cloud Functions ต้องพร้อมใช้งาน แสดง การตั้งค่าจำนวนอินสแตนซ์ขั้นต่ำจะลด Cold Start ของ แอปพลิเคชันของคุณ เราขอแนะนำให้ตั้งค่าจำนวนอินสแตนซ์ขั้นต่ำหาก จะต้องคำนึงถึงเวลาในการตอบสนอง

โปรดดู ควบคุมลักษณะการทำงานของการปรับขนาด เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับตัวเลือกรันไทม์เหล่านี้

แหล่งข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับการเพิ่มประสิทธิภาพใน "ประสิทธิภาพของ Google Cloud Atlas" วิดีโอ เวลาเริ่มระบบ Cold Boot ของฟังก์ชันระบบคลาวด์