本文檔介紹了設計、實施、測試和部署 Cloud Functions 的最佳實踐。
正確性
本節介紹設計和實現 Cloud Functions 的一般最佳實踐。
編寫冪等函數
即使多次調用您的函數,也應該產生相同的結果。如果前一個調用在代碼中途失敗,您可以重試調用。有關詳細信息,請參閱重試事件驅動函數。
不啟動後台活動
後台活動是指函數終止後發生的任何事情。一旦函數返回或以其他方式發出完成信號(例如通過調用 Node.js 事件驅動函數中的callback
參數),函數調用就會完成。優雅終止後運行的任何代碼都無法訪問 CPU,也不會取得任何進展。
此外,當在同一環境中執行後續調用時,您的後台活動將恢復,從而乾擾新的調用。這可能會導致難以診斷的意外行為和錯誤。函數終止後訪問網絡通常會導致連接重置( ECONNRESET
錯誤代碼)。
通過查找調用完成行之後記錄的任何內容,通常可以在各個調用的日誌中檢測後台活動。後台活動有時可能會埋藏在代碼中更深處,尤其是當存在回調或計時器等異步操作時。檢查您的代碼以確保在終止函數之前完成所有異步操作。
始終刪除臨時文件
臨時目錄中的本地磁盤存儲是內存中的文件系統。您編寫的文件會消耗函數可用的內存,並且有時會在調用之間持續存在。未能顯式刪除這些文件可能最終會導致內存不足錯誤和隨後的冷啟動。
您可以通過在 GCP Console 的函數列表中選擇單個函數並選擇內存使用情況圖來查看該函數使用的內存。
不要嘗試在臨時目錄之外進行寫入,並確保使用與平台/操作系統無關的方法來構造文件路徑。
使用管道處理較大文件時,您可以減少內存需求。例如,您可以通過創建讀取流、將其傳遞到基於流的進程並將輸出流直接寫入 Cloud Storage 來處理 Cloud Storage 上的文件。
功能框架
當您部署函數時,函數框架會使用其當前版本自動添加為依賴項。為了確保在不同環境中一致安裝相同的依賴項,我們建議您將函數固定到特定版本的 Functions Framework。
為此,請在相關鎖定文件中包含您的首選版本(例如,Node.js 的package-lock.json
或 Python 的requirements.txt
)。
工具
本部分提供有關如何使用工具來實現、測試 Cloud Functions 以及與 Cloud Functions 交互的指南。
當地發展
函數部署需要一些時間,因此在本地測試函數代碼通常會更快。
Firebase 開發人員可以使用Firebase CLI Cloud Functions 模擬器。使用Sendgrid發送電子郵件
Cloud Functions 不允許在端口 25 上進行出站連接,因此您無法與 SMTP 服務器建立非安全連接。發送電子郵件的推薦方法是使用SendGrid 。您可以在從 Google Compute Engine 實例發送電子郵件教程中找到用於發送電子郵件的其他選項。
表現
本節介紹優化性能的最佳實踐。
明智地使用依賴項
由於函數是無狀態的,因此執行環境通常是從頭開始初始化的(在所謂的冷啟動期間)。當發生冷啟動時,將評估函數的全局上下文。
如果您的函數導入模塊,這些模塊的加載時間可能會增加冷啟動期間的調用延遲。您可以通過正確加載依賴項而不加載函數不使用的依賴項來減少此延遲以及部署函數所需的時間。
使用全局變量在將來的調用中重用對象
無法保證雲函數的狀態將被保留以供將來調用。但是,Cloud Functions 通常會回收先前調用的執行環境。如果在全局範圍內聲明變量,則可以在後續調用中重用其值,而無需重新計算。
通過這種方式,您可以緩存在每次函數調用時重新創建的成本可能很高的對象。將此類對像從函數體移至全局範圍可能會帶來顯著的性能改進。以下示例為每個函數實例僅創建一次重對象,並在到達給定實例的所有函數調用之間共享它:
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}`);
});
在全局範圍內緩存網絡連接、庫引用和 API 客戶端對象尤為重要。有關示例,請參閱優化網絡。
執行全局變量的延遲初始化
如果在全局範圍內初始化變量,初始化代碼將始終通過冷啟動調用執行,從而增加函數的延遲。在某些情況下,如果在try
/ catch
塊中未正確處理這些服務,這會導致所調用的服務間歇性超時。如果某些對象未在所有代碼路徑中使用,請考慮按需延遲初始化它們:
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');
});
如果您在單個文件中定義多個函數,並且不同的函數使用不同的變量,這一點尤其重要。除非使用延遲初始化,否則可能會在已初始化但從未使用過的變量上浪費資源。
通過設置最小實例數減少冷啟動
默認情況下,Cloud Functions 根據傳入請求的數量縮放實例數量。您可以通過設置 Cloud Functions 必須保持準備好服務請求的最小實例數來更改此默認行為。設置最小實例數可以減少應用程序的冷啟動。如果您的應用程序對延遲敏感,我們建議設置最小實例數。
有關這些運行時選項的更多信息,請參閱控制縮放行為。其他資源
在“Google Cloud Performance Atlas”視頻雲功能冷啟動時間中了解有關優化性能的更多信息。