本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
對 Lambda 中的組態問題進行故障診斷
您的函數組態設定可能會影響 Lambda 函數的整體效能和行為。這些可能不會造成實際的函數錯誤,但可能會導致意外的逾時和結果。
下列主題提供與 Lambda 函數組態設定相關的常見問題疑難排解建議。
記憶體組態
您可以設定 Lambda 函數,以使用介於 128 MB 到 10,240 MB 之間的記憶體。依預設,在主控台中建立的任何函數都會被指派最小數量的記憶體。許多 Lambda 函數在此最低設定下都具有效能。不過,如果您匯入大型程式碼庫或完成記憶體密集型任務,則 128 MB 是不夠的。
如果您的函數執行速度比預期慢得多,則第一個步驟是增加記憶體設定。對於記憶體受限的函數,這將解決瓶頸問題,並可能改善函數的效能。
CPU繫結組態
對於運算密集的操作,如果您的函數體驗 slower-than-expected效能,這可能是由於您的函數受到 CPU限制。在這種情況下,函數的運算容量跟不上工作的速度。
雖然 Lambda 不允許您直接修改CPU組態,但 CPU 會透過記憶體設定間接控制。當您配置更多記憶體CPU時,Lambda 服務會按比例配置更多虛擬記憶體。在 1.8 GB 記憶體中,Lambda 函數已配置整個 vCPU,在此層級以上,它可存取多個 vCPU 核心。在 10,240MB 時,它有 6 個 vCPUs 可用。換句話說,即使函數並未使用所有記憶體,您也可以增加記憶體配置來改善效能。
逾時
Lambda 函數的逾時可以設定為 1 到 900 秒 (15 分鐘)。根據預設,Lambda 主控台會將此設定為 3 秒。逾時值是一種安全閥,可確保函數不會無限期執行。達到逾時值後,Lambda 會停止函數叫用。
將逾時值設定為接近函數的平均持續時間會提高函數意外逾時的風險。函數的持續時間會有所不同,這取決於資料傳輸和處理量,以及函數與之互動的任何服務的延遲。逾時的常見原因包括:
-
從 S3 儲存貯體或其他資料存放區下載資料時,下載量較大或下載時間較平均時間長。
-
函數會向另一個服務提出請求,這需要更長的時間才能回應。
-
提供給函數的參數要求函數具有更高的運算複雜性,這會導致調用時間更長。
測試應用程式時,請確定您的測試準確反映資料的大小和數量,以及逼真的參數值。重要的是,使用工作負載合理預期上限的資料集。
此外,在可行的情況下,在您的工作負載中實作上限限制。在本範例中,應用程式可以使用每種檔案類型的大小上限。然後,您可以測試應用程式在一系列預期檔案大小 (最高可達並包括上限) 下的效能。
調用之間的記憶體外洩
存放在 Lambda 調用INIT階段的全域變數和物件會在暖調用之間保留其狀態。它們只會在執行環境第一次執行 (也稱為「冷啟動」) 時完全重設。處理常式結束時,儲存在處理常式中的變數都會銷毀。最佳實務是使用 INIT階段來設定資料庫連線、載入程式庫、建立快取,以及載入不可變的資產。
當您在相同執行環境中跨多個調用使用第三方程式庫時,請檢查其文件是否有在無伺服器運算環境中使用。有些資料庫連線和日誌記錄程式庫可能會儲存中繼調用結果和其他資料。這會導致這些程式庫的記憶體使用量隨著後續暖調用而增加。如果發生這種情況,您可能會發現 Lambda 函數耗盡記憶體,即使您的自訂程式碼已正確處置變數。
此問題會影響暖執行環境中發生的調用。例如,下列程式碼會在調用之間造成記憶體外洩。Lambda 函數透過增加全域陣列的大小,在每次調用時使用額外的記憶體:
let a = []
exports.handler = async (event) => {
a.push(Array(100000).fill(1))
}
使用 128 MB 的記憶體設定,在調用此函數 1000 次之後,Lambda 函數的監控索引標籤會顯示發生記憶體洩漏時的調用、持續時間和錯誤計數中的典型變更:
-
調用 – 穩定的交易速率會定期中斷,因為調用需要更長的時間才能完成。狀態穩定時,記憶體外洩不會耗用函數配置的所有記憶體。隨著效能降低,作業系統正在對本機儲存體進行分頁,以容納函數所需的不斷增長的記憶體,這會導致完成的交易數減少。
-
持續時間 – 在函數耗盡記憶體之前,它會以穩定的兩位數毫秒速率完成調用。分頁發生時,持續時間會增加一個數量級。
-
錯誤計數 – 當記憶體流失超過配置的記憶體時,最終會因為運算超過逾時而導致函數錯誤,或執行環境會停止函數。
發生錯誤後,Lambda 會重新啟動執行環境,說明為什麼所有三個圖形都顯示返回原始狀態。展開持續時間 CloudWatch 指標可提供最短、最長和平均持續時間統計資料的更多詳細資訊:
若要尋找在 1000 個叫用中產生的錯誤,您可以使用 CloudWatch Insights 查詢語言。下列查詢排除僅報告錯誤的資訊日誌:
fields @timestamp, @message | sort @timestamp desc | filter @message not like 'EXTENSION' | filter @message not like 'Lambda Insights' | filter @message not like 'INFO' | filter @message not like 'REPORT' | filter @message not like 'END' | filter @message not like 'START'
在針對此函數的日誌群組執行時,這表示週期性錯誤是逾時造成的:
非同步結果傳回至稍後的調用
對於使用非同步模式的函數程式碼,一次調用的回呼結果可能在未來的調用中傳回。此範例使用 Node.js,但相同的邏輯可以使用非同步模式套用至其他執行時間。函數使用傳統的回呼語法 JavaScript。它會呼叫一個非同步函數,該函數具有增量計數器,可追蹤調用次數:
let seqId = 0 exports.handler = async (event, context) => { console.log(`Starting: sequence Id=${++seqId}`) doWork(seqId, function(id) { console.log(`Work done: sequence Id=${id}`) }) } function doWork(id, callback) { setTimeout(() => callback(id), 3000) }
連續調用多次時,回呼的結果會出現在後續調用中:
-
程式碼會呼叫
doWork
函數,提供回呼函數做為最後一個參數。 -
該
doWork
函數在叫用回呼之前需要一些時間來完成。 -
函數的記錄指出呼叫在
doWork
函數完成執行之前已結束。此外,在開始反覆運算後,正在處理先前反覆運算的回呼,如日誌中所示。
在 中 JavaScript,非同步回呼會使用事件迴圈
這會造成前一次調用的隱私資料可能出現在後續的調用中。有兩種方法可以防止或偵測到此行為。首先, JavaScript 提供非同步和等待關鍵字
let seqId = 0 exports.handler = async (event) => { console.log(`Starting: sequence Id=${++seqId}`) const result = await doWork(seqId) console.log(`Work done: sequence Id=${result}`) } function doWork(id) { return new Promise(resolve => { setTimeout(() => resolve(id), 4000) }) }
使用此語法可防止處理常式在非同步函數完成之前結束。在這種情況下,如果回呼花費的時間超過 Lambda 函數的逾時時間,函數會擲回錯誤,而不是在稍後的調用中傳回回呼結果:
-
程式碼會使用處理常式中的等待關鍵字呼叫非同步
doWork
函數。 -
doWork
函數需要一段時間才能完成,才能解決承諾。 -
函數會逾時,因為
doWork
所需的時間超過逾時限制允許的時間,且回呼結果不會在稍後的調用中傳回。
一般而言,您應該確定程式碼中的任何背景程序或回呼在程式碼結束前完成。如果這在您的使用案例中無法做到,可以使用識別符來確定回呼屬於目前的調用。若要這樣做,您可以使用內容物件awsRequestId提供的 。透過將此值傳遞至非同步回呼,您可以將傳遞的值與目前的值進行比較,以偵測回呼是否來自另一個調用:
let currentContext exports.handler = async (event, context) => { console.log(`Starting: request id=$\{context.awsRequestId}`) currentContext = context doWork(context.awsRequestId, function(id) { if (id != currentContext.awsRequestId) { console.info(`This callback is from another invocation.`) } }) } function doWork(id, callback) { setTimeout(() => callback(id), 3000) }
-
Lambda 函數處理常式採用內容參數,提供對唯一調用請求 ID 的存取權。
-
awsRequestId
會傳遞至 doWork 函數。在回呼中,ID 會與目前呼叫awsRequestId
的 進行比較。如果這些值不同,程式碼可以採取相應的動作。