設計 Lambda 應用程式 - AWS Lambda

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

設計 Lambda 應用程式

架構良好的事件驅動應用程式使用 AWS 服務和自訂程式碼的組合來處理和管理請求和資料。本章著重介紹應用程式設計中與 Lambda 相關的主題。在為忙碌的生產系統設計應用程式時,無伺服器架構師有許多重要的考量。

許多適用於軟體開發和分散式系統的最佳實務,也適用於無伺服器應用程式開發。整體目標是開發符合下列條件的工作負載:

  • 可靠 – 為您的最終使用者提供高水準的可用性。 AWS 無伺服器服務是可靠的,因為其亦專為解決故障而設計。

  • 耐久 – 提供符合工作負載耐久性需求的儲存選項。

  • 安全 – 遵循最佳實務,並使用提供的工具來保護對工作負載的存取,並在發生任何問題時限制影響範圍。

  • 高效能 – 有效率地使用運算資源,並滿足最終使用者的效能需求。

  • 具成本效益 – 設計可避免不必要的成本的架構,這些架構可以在不超支的情況下進行擴展及除役,而不會產生大量開銷。

下列設計原則可協助您建置符合這些目標的工作負載。並非每一項原則都適用於所有架構,但這些原則應能為您的總體架構決策提供指引。

使用服務而非自訂程式碼

無伺服器應用程式通常包含數個 AWS 服務,與 Lambda 函數中執行的自訂程式碼整合。雖然 Lambda 可以與大多數 AWS 服務整合,但無伺服器應用程式中最常使用的服務如下:

Category AWS 服務

運算

AWS Lambda

資料儲存

Amazon S3

Amazon DynamoDB

Amazon RDS

API

Amazon API Gateway

應用程式整合

Amazon EventBridge

Amazon SNS

Amazon SQS

協調

AWS Step Functions

串流資料和分析

Amazon Data Firehose

注意

許多無伺服器服務皆提供資料複寫功能與多區域支援,包括 DynamoDB 與 Amazon S3。Lambda 函數可以作為部署管道的一部分部署在多個區域中,並且可以將 API Gateway 設定為支援此組態。請參閱此範例架構,其示範如何實現這一點。

分散式架構中有許多建立良好的常見模式,您可以使用 AWS 服務自行建置或實作。對於大多數客戶,投資時間從頭開始開發這些模式幾乎沒有商業價值。當您的應用程式需要其中一個模式時,請使用對應的 AWS 服務:

模式 AWS 服務

佇列

Amazon SQS

事件匯流排

Amazon EventBridge

發布/訂閱 (扇出)

Amazon SNS

協調

AWS Step Functions

API

Amazon API Gateway

事件串流

Amazon Kinesis

這些服務旨在與 Lambda 整合,您可以使用基礎設施即程式碼 (IaC) 來建立和捨棄服務中的資源。您可以透過 AWS SDK 使用其中任何服務,而無需安裝應用程式或設定伺服器。熟悉透過 Lambda 函數中的程式碼使用這些服務,是生產設計良好的無伺服器應用程式的重要步驟。

了解 Lambda 抽象層級

Lambda 服務會限制您對執行 Lambda 函數和基礎作業系統、Hypervisor 和硬體的存取。此服務會持續改善和變更基礎設施,以新增功能、降低成本,並提高服務效能。您的程式碼應假設不了解 Lambda 的架構方式,且假設沒有硬體親和性。

同樣地,Lambda 與其他 服務的整合是由 管理 AWS,只有少量的組態選項會公開給您。例如,API Gateway 與 Lambda 互動時無需考慮負載平衡,因為這項功能已完全由服務本身管理。您也無法直接控制服務在任何時間點調用函式時所使用的可用區域,亦無法控制 Lambda 如何決定執行環境數量的向上擴展或縮減時機。

此抽象概念可協助您專注於應用程式的整合層面、資料流程,以及工作負載為最終使用者提供價值的商業邏輯。允許服務管理基礎機制可協助您更快速地開發應用程式,同時減少要維護的自訂程式碼。

在函式中實作無狀態

對於標準 Lambda 函數,您應該假設環境僅存在於單一調用中。函式應在首次啟動時初始化任何必要的狀態。例如,函式可能需要從 DynamoDB 資料表擷取資料。在結束前,函式應將所有永久性資料變更提交至 Amazon S3、DynamoDB 或 Amazon SQS 等耐久存放區。其不應依賴任何現有的資料結構或暫存檔案,或任何需要多次調用才能管理的內部狀態。

使用持久性函數時,狀態會在叫用之間自動保留,無需手動將狀態保留至外部儲存。不過,對於未透過 DurableContext 明確管理的任何資料,您仍應遵循無狀態原則。

若要初始化資料庫連線與程式庫,或是載入狀態資訊,您可以使用靜態初始化功能。由於執行環境會盡可能重複使用以改善效能,因此您可以在多次調用中分攤初始化這些資源所花費的時間。但是,您不應在此全域範圍內儲存函數中使用的任何變數或資料。

最小化耦合度

大多數架構應偏好許多、較短的函數,而不是較少、較大的函數。每個函數的用途應該是處理傳遞到函數的事件,而無需了解或預期整個工作流程或交易量。這使得此函數與事件來源無關,且與其他服務的耦合最少。

任何不常變更的全域範圍常數都應作為環境變數實作,以允許在不部署的情況下進行更新。任何機密或敏感資訊都應儲存在 AWS Systems Manager Parameter StoreAWS Secrets Manager 中,並由函數載入。由於這些資源具有帳戶專屬特性,您可建立跨多個帳戶的建置管道。管道根據環境載入適當的機密,而無需向開發人員公開這些機密,也無需變更任何程式碼。

針對隨選資料而非批次處理進行建置

許多傳統系統旨在定期執行並處理隨著時間累積的交易批次。例如,銀行應用程式可能每小時執行一次,以處理中央分類帳中的 ATM 交易。在 Lambda 型應用程式中,每個事件都應觸發自訂處理,從而允許服務視需要向上擴展並行,以提供近乎即時的交易處理。

雖然標準 Lambda 函數的執行時間限制為 15 分鐘,但耐用函數最多可執行一年,因此適合長時間執行的處理需求。不過,您仍然應該盡可能偏好事件驅動型處理,而不是批次處理。

雖然您可以透過使用 Amazon EventBridge 中規則的排程表達式,在無伺服器應用程式中執行 cron 任務,但應謹慎使用或作為最後的手段。在任何採用批次處理模式的排程任務中,交易量都有可能增長到超出 Lambda 15 分鐘時間限制內可以處理的數量。如果外部系統的限制迫使您使用排程器,您通常應排程最短的合理重複時段。

例如,使用批次程序觸發 Lambda 函式來擷取新的 Amazon S3 物件清單並非最佳實務。這是因為服務在批次之間接收到的新物件可能多於 15 分鐘 Lambda 函數內可以處理的物件。

事件驅動型架構圖 10

相反,Amazon S3 儲存貯體應在每次有新物件放入其中時都調用 Lambda 函式。此方法的可擴展性顯著提高,並且可以近乎即時地進行處理。

事件驅動型架構圖 11

AWS Step Functions 考慮協調

涉及分支邏輯、不同類型故障模型與重試邏輯的工作流程,通常會使用協調器來追蹤整體執行的狀態。請避免使用 Lambda 函式實現此用途,因為這會導致緊耦合問題,且使負責路由的程式碼更趨複雜。

透過 AWS Step Functions,您可以使用狀態機器來管理協同運作。這會從您的程式碼擷取錯誤處理、路由和分支邏輯,並將其取代為使用 JSON 宣告的狀態機器。除了提升工作流程的穩健性與可觀測性之外,您還可以為工作流程新增版本控制功能,並將狀態機器轉化成可新增至程式碼儲存庫的編碼資源。

Lambda 函式中的簡易工作流程,往往會隨著時間推移逐漸變得更加複雜。操作生產無伺服器應用程式時,務必識別何時發生這種情況,以便將此邏輯遷移至狀態機器。

實作冪等性

AWS 無伺服器服務,包括 Lambda,具有容錯能力,旨在處理故障。例如,若某項服務調用 Lambda 函式時發生服務中斷,Lambda 會自動在另一個可用區域中調用您的函式。如果您的函式擲回錯誤,Lambda 會重試此調用請求。

由於同一事件可能會被多次接收,因此函數應設計為等冪。這意味著多次接收同一事件不會在第一次收到事件之後變更結果。

您可在 Lambda 函式中實作冪等性:透過 DynamoDB 資料表追蹤近期處理過的識別碼,藉此判斷該交易先前是否已處理完畢。DynamoDB 資料表通常實作存留時間 (TTL) 值來使項目過期,以限制所使用的儲存空間。

使用多個 AWS 帳戶來管理配額

中的許多服務配額 AWS 是在帳戶層級設定。這表示隨著您新增更多工作負載,可能會快速用盡配額上限。

解決此問題的有效方法是使用多個 AWS 帳戶,將每個工作負載分配到自己的 帳戶。這樣可防止與其他工作負載或非生產資源共用配額。

此外,您可以透過 AWS Organizations 集中管理這些帳戶的帳單、合規性與安全性。您可以將政策連接到帳戶群組,避免自訂指令碼和手動程序。

一種常見的方法是為每個開發人員提供 AWS 帳戶,然後針對 Beta 部署階段和生產環境使用不同的帳戶:

應用程式設計圖 3

在此模型中,每位開發人員的帳戶都具備獨立的配額限制,因此其資源使用行為不會影響您的生產環境。此做法還能讓開發人員在本機開發環境中測試 Lambda 函式,並直接對接其個人帳戶中的真實雲端資源。