秘訣與技巧

本文件說明設計、實作、測試及部署 Cloud Functions 的最佳做法。

正確性

本節說明設計和實作 Cloud Functions 的一般最佳做法。

編寫冪等函式

即使多次呼叫函式,這些函式也應該產生相同結果。這樣一來,如果之前叫用程式碼的作業中途失敗,您便可以重試叫用。詳情請參閱重試事件導向函式

請勿啟動背景活動

背景活動是指函式終止後發生的任何活動。當函式傳回或以其他方式通知完成時 (例如,在 Node.js 事件導向的函式中呼叫 callback 引數),函式叫用就會完成。在安全終止後執行的任何程式碼都無法存取 CPU,也不會有任何進度。

此外,在相同環境中執行後續叫用時,背景活動會繼續執行,幹擾新的叫用。這可能會產生難以診斷的非預期行為和錯誤。在函式終止後存取網路,通常會導致連線重設 (ECONNRESET 錯誤代碼)。

通常可以在個別叫用的記錄中偵測到背景活動,方法是尋找在表示叫用已完成的那一行之後記錄的任何資料。背景活動有時可能會掩蓋在程式碼中,尤其是在出現回呼或計時器等非同步作業時。請檢查程式碼,確保在您終止函式前,所有非同步作業皆已完成。

一律刪除暫存檔案

暫存目錄中的本機磁碟儲存空間是一個記憶體內部檔案系統。您編寫的檔案會耗用用於函式的記憶體,而且有時會在叫用間持續存在。不明確刪除這些檔案最終可能會導致發生記憶體不足的錯誤,並造成後續冷啟動。

如要查看個別函式使用的記憶體,請在 GCP Console 的函式清單中選取函式,然後選擇「Memory usage」(記憶體用量) 圖表。

請勿嘗試在暫存目錄外部編寫檔案,並務必使用獨立於平台/OS 的方法建構檔案路徑。

您可以使用管道來減少記憶體需求,例如,您可以建立讀取串流,透過串流式程序傳遞檔案,並將輸出串流直接寫入 Cloud Storage,來處理 Cloud Storage 上的檔案。

Functions Framework

當您部署函式時,系統會使用其現行版本自動新增 Functions Framework 做為依附元件。為確保在不同環境中一致安裝相同的依附元件,建議您將函式固定至特定版本的 Functions Framework。

方法是在相關的鎖定檔案中加入您偏好的版本 (例如,package-lock.json 適用於 Node.js,或者適用於 Python 的 requirements.txt)。

工具

本節提供如何使用工具實作、測試 Cloud Functions,及與 Cloud Functions 互動的指南。

本機開發

函式部署需要一點時間,因此在本機測試函式的程式碼通常會比較快。

Firebase 開發人員可以使用 Firebase CLI Cloud Functions 模擬器

使用 Sendgrid 傳送電子郵件

Cloud Functions 不允許在通訊埠 25 上進行傳出連線,因此您無法與 SMTP 伺服器之間建立不安全的連線。傳送電子郵件時,建議使用 SendGrid。您可以在 Google Compute Engine 的從執行個體傳送電子郵件教學課程中找到傳送電子郵件的其他選項。

效能

本節說明最佳化效能的最佳做法。

謹慎使用依附元件

由於函式是無狀態的,因此執行環境通常是從頭開始初始化 (這期間就是所謂的「冷啟動」)。發生冷啟動時,會評估函式的全域背景資訊。

如果函式匯入模組,在冷啟動期間,這些模組的載入時間會增加叫用的延遲時間。您可以正確載入依附元件,而不載入函式不使用的依附元件,來減少這一延遲時間以及部署函式需要的時間。

使用全域變數在未來叫用中重複使用物件

在未來叫用中,無法保證會保留 Cloud 函式的狀態。不過,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),並傳回回應文字,或任何可使用 make_response 轉換為 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),並傳回回應文字,或任何可使用 make_response 轉換為 Response 物件的一組值。

如果您在單一檔案中定義多個函式,且不同函式使用不同變數,這個方法尤為重要。除非您使用延遲初始化,否則會浪費已初始化但從未使用的變數資源。

設定執行個體數量下限,藉此減少冷啟動的情形

根據預設,Cloud Functions 會依據傳入要求的數量調整執行個體數量。如要變更這個預設行為,您可以設定 Cloud Functions 必須持續準備處理要求的執行個體數量下限。設定執行個體數量下限可減少應用程式的冷啟動情形。如果您的應用程式會受延遲影響,建議您設定執行個體的數量下限。

如要進一步瞭解這些執行階段選項,請參閱控管資源調度行為

其他資源

如要進一步瞭解最佳化效能,請觀看「Google Cloud 效能集錦」影片:Cloud Functions 冷啟動時間