本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
在 C# 中定義 Lambda 函數處理常式
Lambda 函數處理常式是您的函數程式碼中處理事件的方法。當有人呼叫您的函數時,Lambda 會執行處理常式方法。函數會執行,直到處理常式傳回回應、結束或逾時為止。
當您的函數遭調用,且 Lambda 執行了函數的處理常式方法時,系統會將兩個引數傳遞給函數。第一個引數是 event
物件。當另一個 AWS 服務 叫用您的函數時,event
物件包含導致叫用函數的事件相關資料。例如,APIGateway 中的event
物件包含路徑、HTTP方法和HTTP標頭的相關資訊。確切的事件結構會根據 AWS 服務 叫用函數而有所不同。如需個別服務事件格式的詳細資訊,請參閱:使用來自其他服務的事件叫用 Lambda AWS。
Lambda 也會傳遞 context
物件給函數。此物件包含有關調用、函數以及執行環境的資訊。如需詳細資訊,請參閱使用 Lambda 上下文對象來檢索 C# 函數信息。
所有 Lambda 事件的原生格式都是代表JSON格式化事件的位元組串流。除非您的函數輸入與輸出參數為 System.IO.Stream
類型,否則必須將其序列化。透過設定 LambdaSerializer
組件屬性來指定要使用的序列化程式。如需詳細資訊,請參閱Lambda 函數中的序列化。
主題
Lambda 的 .NET 執行模型
在 . 中執行 Lambda 函數有兩種不同的執行模型NET:類別程式庫方法和可執行的組合方法。
若使用類別庫做法,您可以為 Lambda 提供一個字串,指出要調用之函數的 AssemblyName
、ClassName
、和 Method
。如需此字串格式的詳細資訊,請參閱:類別庫處理常式。在函數的初始化階段,系統會初始化函數的類別,並執行建構函數中的任何程式碼。
若使用可執行組件做法,您可以使用 C# 9 的頂層陳述式
以下各節提供這兩種做法的範例函數程式碼。
類別庫處理常式
下列 Lambda 函數程式碼顯示使用類別庫做法之 Lambda 函數的處理常式方法 (FunctionHandler
) 範例。在此範例中,Lambda 會從叫用函數的API閘道接收事件。函數會從資料庫讀取記錄,並傳回記錄作為API閘道回應的一部分。
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] namespace GetProductHandler; public class Function { private readonly IDatabaseRepository _repo; public Function() { this._repo = new DatabaseRepository(); } public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest request) { var id = request.PathParameters["id"]; var databaseRecord = await this._repo.GetById(id); return new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = JsonSerializer.Serialize(databaseRecord) }; } }
建立 Lambda 函數時,您需要以處理常式字串的形式向 Lambda 提供函數處理常式的相關資訊。目的是指示 Lambda 在調用函數時要在程式碼中執行哪種方法。若使用 C#,使用類別庫做法時處理常式字串的格式如下:
ASSEMBLY::TYPE::METHOD
,其中:
-
ASSEMBLY
是應用程式 .NET 組件檔案的名稱。如果您使用Amazon.Lambda.Tools
CLI建置應用程式,但並未使用 .csproj 檔案中的AssemblyName
屬性設定組件名稱,則ASSEMBLY
只是您的 .csproj 檔案的名稱。 -
TYPE
是處理常式類型的完整名稱,包含Namespace
和ClassName
。 -
METHOD
是程式碼中函數處理常式方法的名稱。
在顯示的範例程式碼中,如果組件名為 GetProductHandler
,則處理常式字串會是 GetProductHandler::GetProductHandler.Function::FunctionHandler
。
可執行組件處理常式
在下列範例中,Lambda 函數定義為可執行的組件。此程式碼中的處理常式方法名為 Handler
。使用可執行組件時,必須引導 Lambda 執行期。若要執行此作業,請使用 LambdaBootstrapBuilder.Create
方法。此方法的輸入是函數當成處理常式使用的方法,以及要使用的 Lambda 序列化程式。
如需使用頂層陳述式的詳細資訊,請參閱 AWS 運算部落格上的介紹 的 .NET 6 執行時間 AWS Lambda
namespace GetProductHandler; IDatabaseRepository repo = new DatabaseRepository(); await LambdaBootstrapBuilder.Create<APIGatewayProxyRequest>(Handler, new DefaultLambdaJsonSerializer()) .Build() .RunAsync(); async Task<APIGatewayProxyResponse> Handler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context) { var id = apigProxyEvent.PathParameters["id"]; var databaseRecord = await this.repo.GetById(id); return new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = JsonSerializer.Serialize(databaseRecord) }; };
使用可執行組件時,指示 Lambda 如何執行程式碼的處理常式字串就是組件的名稱。在這個範例中,即為 GetProductHandler
。
Lambda 函數中的序列化
若您的 Lambda 函數使用輸入或輸出類型而非 Stream
物件,您必須將序列化程式庫新增至您的應用程式。若要實作序列化,您可以使用 System.Text.Json
和 Newtonsoft.Json
提供的標準反射式序列化,或是使用原始碼產生的序列化
使用原始碼產生的序列化
來源產生的序列化是 .NET 第 6 版及更新版本的功能,允許在編譯時產生序列化程式碼。此方式不需要使用反射,且可提高函數的效能。若要在函數中使用原始碼產生的序列化,請執行以下操作:
-
建立一個繼承自
JsonSerializerContext
的新部分分類,為需要序列化或還原序列化的所有類型新增JsonSerializable
屬性。 -
設定
LambdaSerializer
以使用SourceGeneratorLambdaJsonSerializer<T>
。 -
更新應用程式程式碼中的任何手動序列化或還原序列化,以使用新建立的類別。
以下程式碼顯示的範例函數,是使用原始碼產生的序列化。
[assembly: LambdaSerializer(typeof(SourceGeneratorLambdaJsonSerializer<CustomSerializer>))] public class Function { private readonly IDatabaseRepository _repo; public Function() { this._repo = new DatabaseRepository(); } public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest request) { var id = request.PathParameters["id"]; var databaseRecord = await this._repo.GetById(id); return new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = JsonSerializer.Serialize(databaseRecord, CustomSerializer.Default.Product) }; } } [JsonSerializable(typeof(APIGatewayProxyRequest))] [JsonSerializable(typeof(APIGatewayProxyResponse))] [JsonSerializable(typeof(Product))] public partial class CustomSerializer : JsonSerializerContext { }
注意
如果您想要搭配 Lambda 使用原生預先編譯 (AOT),則必須使用來源產生的序列化。
使用反射式序列化
AWS 提供預先建置的程式庫,可讓您快速將序列化新增至應用程式。您可以使用 Amazon.Lambda.Serialization.SystemTextJson
或 Amazon.Lambda.Serialization.Json
NuGet 套件來設定此設定。Amazon.Lambda.Serialization.SystemTextJson
會在背景使用 System.Text.Json
來執行序列化任務,而 Amazon.Lambda.Serialization.Json
會使用 Newtonsoft.Json
套件。
您也可以實作 ILambdaSerializer
界面 (隨 Amazon.Lambda.Core
程式庫提供) 來建立自己的序列化程式庫。此界面定義了兩種方法:
-
T Deserialize<T>(Stream requestStream);
您可以實作此方法,將請求承載從 還原序列化
Invoke
API至傳遞至 Lambda 函數處理常式的物件。 -
T Serialize<T>(T response, Stream responseStream);
您實作此方法,將從 Lambda 函數處理常式傳回的結果序列化為
Invoke
API操作傳回的回應承載。
使用 Lambda Annotations 架構簡化函數程式碼
Lambda Annotations 是 .NET 6 和 .NET 8 的架構,可簡化使用 C# 編寫 Lambda 函數的程序。透過 Annotations 架構,您可以取代使用一般程式設計模型編寫的大部分 Lambda 函數程式碼。使用此架構編寫的程式碼使用更簡單的表達式,讓您可專注於商業邏輯。
以下範例程式碼示範使用 Annotations 架構可如何簡化編寫 Lambda 函數的程序。第一個範例顯示使用一般 Lambda 程式模型編寫的程式碼,第二個範例顯示使用 Annotations 架構的對等程式碼。
public APIGatewayHttpApiV2ProxyResponse LambdaMathAdd(APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) { if (!request.PathParameters.TryGetValue("x", out var xs)) { return new APIGatewayHttpApiV2ProxyResponse { StatusCode = (int)HttpStatusCode.BadRequest }; } if (!request.PathParameters.TryGetValue("y", out var ys)) { return new APIGatewayHttpApiV2ProxyResponse { StatusCode = (int)HttpStatusCode.BadRequest }; } var x = int.Parse(xs); var y = int.Parse(ys); return new APIGatewayHttpApiV2ProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = (x + y).ToString(), Headers = new Dictionary≪string, string> { { "Content-Type", "text/plain" } } }; }
[LambdaFunction] [HttpApi(LambdaHttpMethod.Get, "/add/{x}/{y}")] public int Add(int x, int y) { return x + y; }
如需使用 Lambda Annotations 如何簡化程式碼的另一個範例,請參閱 awsdocs/aws-doc-sdk-examples
GitHub 儲存庫中的此跨服務範例應用程式PamApiAnnotations
資料夾在主要 function.cs
檔案中使用 Lambda Annotations。為了進行比較,PamApi
資料夾包含使用一般 Lambda 程式設計模型編寫的對等檔案。
Annotations 架構使用原始碼產生器
如需如何為 使用 Lambda 註釋的詳細資訊NET,請參閱下列資源:
-
aws/aws-lambda-dotnet
GitHub 儲存庫。 -
Amazon.Lambda.Annotations
NuGet 套件。
使用 Lambda Annotations 架構進行相依性插入
您也可以透過 Lambda Annotations 架構,使用熟悉的語法將相依性插入新增至 Lambda 函數。將 [LambdaStartup]
屬性新增至 Startup.cs
檔案時,Lambda Annotations 架構會在編譯時產生所需的程式碼。
[LambdaStartup] public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IDatabaseRepository, DatabaseRepository>(); } }
Lambda 函數可以使用建構函數插入來插入服務,或使用 [FromServices]
屬性插入個別方法中。
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] namespace GetProductHandler; public class Function { private readonly IDatabaseRepository _repo; public Function(IDatabaseRepository repo) { this._repo = repo; } [LambdaFunction] [HttpApi(LambdaHttpMethod.Get, "/product/{id}")] public async Task<Product> FunctionHandler([FromServices] IDatabaseRepository repository, string id) { return await this._repo.GetById(id); } }
Lambda 函數處理常式限制
請注意,處理常式簽章有若干限制。
-
處理常式簽章不能為
unsafe
,且不得使用指標類型,但可以在處理函式方法及其依存項目中使用unsafe
內容。如需詳細資訊,請參閱 Microsoft 文檔網站上的不安全 (C# 參考)。 -
處理常式不會傳送使用
params
關鍵字的參數變數,也不得使用支援參數變數的ArgIterator
作為輸入或傳回參數。 -
處理常式可能不是一般方法,例如 IList<T> Sort<T> (IList<T> 輸入)。
-
不支援具有
async void
簽章的非同步處理常式。
C# Lambda 函數的程式碼最佳實務
遵循下列清單中的準則,在建置 Lambda 函數時使用最佳編碼實務:
-
區隔 Lambda 處理常式與您的核心邏輯。能允許您製作更多可測單位的函式。
-
控制函數部署套件內的相依性。 AWS Lambda 執行環境包含許多程式庫。若要啟用最新的一組功能與安全更新,Lambda 會定期更新這些程式庫。這些更新可能會為您的 Lambda 函數行為帶來細微的變更。若要完全掌控您函式所使用的相依性,請利用部署套件封裝您的所有相依性。
-
最小化依存項目的複雜性。偏好更簡易的框架,其可快速在執行環境啟動時載入。
-
將部署套件最小化至執行時間所必要的套件大小。這能減少您的部署套件被下載與呼叫前解壓縮的時間。對於在 中編寫的函數NET,請避免上傳整個 AWS SDK程式庫作為部署套件的一部分。相反地,選擇性地依賴接取SDK所需元件的模組 (例如 DynamoDB 、Amazon S3 SDK模組和 Lambda 核心程式庫)。
-
請利用執行環境重新使用來改看函式的效能。初始化函數處理常式外部的SDK用戶端和資料庫連線,並在
/tmp
目錄中本機快取靜態資產。由您函式的相同執行個體處理的後續叫用可以重複使用這些資源。這可藉由減少函數執行時間來節省成本。若要避免叫用間洩漏潛在資料,請不要使用執行環境來儲存使用者資料、事件,或其他牽涉安全性的資訊。如果您的函式依賴無法存放在處理常式內記憶體中的可變狀態,請考慮為每個使用者建立個別函式或個別函式版本。
-
使用 Keep-Alive 指令維持持續連線的狀態。Lambda 會隨著時間的推移清除閒置連線。叫用函數時嘗試重複使用閒置連線將導致連線錯誤。若要維護持續連線,請使用與執行階段相關聯的 keep-alive (保持啟用) 指令。如需範例,請參閱在 Node.js 中重複使用 Keep-Alive 的連線。
-
使用環境變數將操作參數傳遞給您的函數。例如,如果您正在寫入到 Amazon S3 儲存貯體,而非對您正在寫入的儲存貯體名稱進行硬式編碼,請將儲存貯體名稱設定為環境變數。
-
避免在 Lambda 函數中使用遞迴調用,其中函數會叫用自己或啟動可能再次叫用函數的程序。這會導致意外的函式呼叫量與升高的成本。如果您看到非預期的叫用量,請將函數預留並行設定為
0
,以便在更新程式碼時將所有叫用限制為函數。 -
請勿在 Lambda 函數程式碼中使用未記錄、非公開APIs。對於 AWS Lambda 受管執行期,Lambda 會定期將安全性和功能更新套用至 Lambda 的內部 APIs。這些內部API更新可能會與回溯不相容,如果您的函數依賴這些非公有 ,則會導致呼叫失敗等意外後果APIs。如需公開可用 的清單,請參閱 API參考APIs。
-
撰寫等冪程式碼。為函數撰寫等冪程式碼可確保採用相同方式來處理重複事件。程式碼應正確驗證事件並正常處理重複的事件。如需詳細資訊,請參閱 How do I make my Lambda function idempotent?
(如何讓 Lambda 函數等冪?)。