

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

# 設計 Lambda 應用程式
<a name="concepts-application-design"></a>

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

許多適用於軟體開發和分散式系統的最佳實務，也適用於無伺服器應用程式開發。整體目標是開發符合下列條件的工作負載：
+  **可靠** – 為您的最終使用者提供高水準的可用性。 AWS 無伺服器服務是可靠的，因為其亦專為解決故障而設計。
+  **耐久** – 提供符合工作負載耐久性需求的儲存選項。
+  **安全** – 遵循最佳實務，並使用提供的工具來保護對工作負載的存取，並在發生任何問題時限制影響範圍。
+  **高效能** – 有效率地使用運算資源，並滿足最終使用者的效能需求。
+  **具成本效益** – 設計可避免不必要的成本的架構，這些架構可以在不超支的情況下進行擴展及除役，而不會產生大量開銷。

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

**Topics**
+ [使用服務而非自訂程式碼](#services-custom-code)
+ [了解 Lambda 抽象層級](#level-abstraction)
+ [在函式中實作無狀態](#statelessness-functions)
+ [最小化耦合度](#minimize-coupling)
+ [針對隨選資料而非批次處理進行建置](#on-demand-batches)
+ [選擇複雜工作流程的協同運作選項](#orchestration)
+ [實作冪等性](#retries-failures)
+ [使用多個 AWS 帳戶來管理配額](#multiple-accounts)

## 使用服務而非自訂程式碼
<a name="services-custom-code"></a>

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


| Category | AWS 服務 | 
| --- | --- | 
|  運算  |  AWS Lambda  | 
|  資料儲存  |  Amazon S3 Amazon DynamoDB Amazon RDS  | 
|  API  |  Amazon API Gateway  | 
|  應用程式整合  |  Amazon EventBridge Amazon SNS Amazon SQS  | 
|  協調  |  Lambda 耐用函數 AWS Step Functions  | 
|  串流資料和分析  |  Amazon Data Firehose  | 

**注意**  
許多無伺服器服務皆提供資料複寫功能與多區域支援，包括 DynamoDB 與 Amazon S3。Lambda 函數可以作為部署管道的一部分部署在多個區域中，並且可以將 API Gateway 設定為支援此組態。請參閱此[範例架構](https://d1.awsstatic.com/architecture-diagrams/ArchitectureDiagrams/serverless-architecture-for-global-applications-ra.pdf?did=wp_card&trk=wp_card)，其示範如何實現這一點。

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


| 模式 | AWS 服務 | 
| --- | --- | 
|  佇列  |  Amazon SQS  | 
|  事件匯流排  |  Amazon EventBridge  | 
|  發布/訂閱 (扇出)  |  Amazon SNS  | 
|  協調  |  Lambda 耐用函數 AWS Step Functions  | 
|  API  |  Amazon API Gateway  | 
|  事件串流  |  Amazon Kinesis  | 

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

## 了解 Lambda 抽象層級
<a name="level-abstraction"></a>

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

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

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

## 在函式中實作無狀態
<a name="statelessness-functions"></a>

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

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

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

## 最小化耦合度
<a name="minimize-coupling"></a>

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

任何不常變更的全域範圍常數都應作為環境變數實作，以允許在不部署的情況下進行更新。任何機密或敏感資訊都應儲存在 [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) 或 [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) 中，並由函數載入。由於這些資源具有帳戶專屬特性，您可建立跨多個帳戶的建置管道。管道根據環境載入適當的機密，而無需向開發人員公開這些機密，也無需變更任何程式碼。

## 針對隨選資料而非批次處理進行建置
<a name="on-demand-batches"></a>

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

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

雖然您可以透過使用 Amazon EventBridge 中規則的[排程表達式](https://docs.aws.amazon.com/eventbridge/latest/userguide/scheduled-events.html)，在無伺服器應用程式中執行 [cron](https://en.wikipedia.org/wiki/Cron) 任務，但應謹慎使用或作為最後的手段。在任何採用批次處理模式的排程任務中，交易量都有可能增長到超出 Lambda 15 分鐘時間限制內可以處理的數量。如果外部系統的限制迫使您使用排程器，您通常應排程最短的合理重複時段。

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

![\[事件驅動型架構圖 10\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/event-driven-architectures-figure-10.png)


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

![\[事件驅動型架構圖 11\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/event-driven-architectures-figure-11.png)


## 選擇複雜工作流程的協同運作選項
<a name="orchestration"></a>

涉及分支邏輯、不同類型故障模型與重試邏輯的工作流程，通常會使用協調器來追蹤整體執行的狀態。請勿在標準 Lambda 函數中建置臨機操作協同運作。這會導致緊密耦合、複雜的路由代碼，而且沒有自動狀態復原。

請改用下列其中一個專用協同運作選項：
+ **[Lambda 耐久函數](durable-functions.md)：**以應用程式為中心的協同運作，使用標準程式設計語言搭配自動檢查點、內建重試和執行復原。非常適合偏好將工作流程邏輯與 Lambda 內的商業邏輯一起保存在程式碼中的開發人員。
+ **[AWS Step Functions](with-step-functions.md)：**視覺化工作流程協同運作與 220 多種 AWS 服務的原生整合。非常適合用於多服務協調、零維護基礎設施和視覺化工作流程設計。

如需在這些選項之間進行選擇的指引，請參閱[耐用函數或 Step Functions](durable-step-functions.md)。

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

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

## 實作冪等性
<a name="retries-failures"></a>

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

由於同一事件可能會被多次接收，因此函數應設計為[等冪](https://en.wikipedia.org/wiki/Idempotence)。這意味著多次接收同一事件不會在第一次收到事件之後變更結果。

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

## 使用多個 AWS 帳戶來管理配額
<a name="multiple-accounts"></a>

中的許多[服務配額](gettingstarted-limits.md) AWS 是在帳戶層級設定。這表示隨著您新增更多工作負載，可能會快速用盡配額上限。

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

 此外，您可以透過 [AWS Organizations](https://aws.amazon.com/organizations/) 集中管理這些帳戶的帳單、合規性與安全性。您可以將政策連接到帳戶群組，避免自訂指令碼和手動程序。

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

![\[應用程式設計圖 3\]](http://docs.aws.amazon.com/zh_tw/lambda/latest/dg/images/application-design-figure-3.png)


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