提示與秘訣

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

正確性

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

編寫冪等函式

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

請勿啟動背景活動

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

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

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

一律刪除暫存檔案

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

如要查看個別函式使用的記憶體,請在 Google Cloud 控制台的函式清單中選取該函式,然後選擇「Memory usage」圖表。

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

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

Functions Framework

部署函式時,系統會自動使用目前的版本,將 Functions 架構新增為依附元件。為確保在不同環境中一致安裝相同的依附元件,建議您將函式釘選至特定版本的 Functions Framework。

如要這麼做,請在相關的鎖定檔案中加入偏好的版本 (例如 Node.js 的 package-lock.json,或 Python 的 requirements.txt)。

工具

本節提供使用工具實作、測試及與 Cloud Functions 互動的規範。

本機開發

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

Firebase 開發人員可以使用 Firebase CLI Cloud Functions Emulator

使用 Sendgrid 傳送電子郵件

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

成效

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

謹慎使用依附元件

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

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

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

在日後叫用時,無法保證函式的狀態將會保留。不過,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 Performance Atlas」影片的 Cloud Functions 冷啟動時間