เคล็ดลับ & เทคนิค

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

ความถูกต้อง

ส่วนนี้จะอธิบายแนวทางปฏิบัติที่ดีที่สุดโดยทั่วไปสำหรับการออกแบบและการใช้งานฟังก์ชันระบบคลาวด์

เขียนฟังก์ชันไอดีโพเทนต์

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

อย่าเริ่มกิจกรรมเบื้องหลัง

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

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

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

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

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

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

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

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

กรอบฟังก์ชัน

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

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

เครื่องมือ

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

การพัฒนาท้องถิ่น

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

นักพัฒนา Firebase สามารถใช้ Firebase CLI Cloud Functions Emulator ได้

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

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

ผลงาน

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

ใช้การพึ่งพาอย่างชาญฉลาด

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

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

ใช้ตัวแปรส่วนกลางเพื่อนำวัตถุกลับมาใช้ใหม่ในการเรียกใช้ในอนาคต

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

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

โหนด 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 ) และส่งคืนข้อความตอบกลับหรือชุดของค่าใดๆ ที่สามารถเปลี่ยนเป็นวัตถุ Response โดยใช้ make_response

เป็นสิ่งสำคัญอย่างยิ่งในการแคชการเชื่อมต่อเครือข่าย การอ้างอิงไลบรารี และออบเจ็กต์ไคลเอ็นต์ API ในขอบเขตส่วนกลาง ดูตัวอย่าง การปรับเครือข่ายให้เหมาะสม

ทำการเริ่มต้นแบบ Lazy ของตัวแปรโกลบอล

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

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

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

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

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

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

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

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