將 .NET Lambda 函數代碼編譯為本機運行時格式 - AWS Lambda

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

將 .NET Lambda 函數代碼編譯為本機運行時格式

.NET 8 支持本地 ahead-of-time (AOT)編譯。使用原生 AOT,您可以將 Lambda 函數程式碼編譯為原生執行階段格式,這樣便不需要在執行階段編譯 .NET 程式碼。原生 AOT 編譯可縮短以 .NET 撰寫之 Lambda 函數的冷啟動時間。如需詳細資訊,請參閱 AWS 計算部落格 AWS Lambda上的 .NET 8 執行階段簡介

Lambda 執行時間

若要使用原生 AOT 編譯部署 Lambda 函數建置,請使用受管理的 .NET 8 Lambda 執行階段。這個執行階段支援使用 x86_64 和 arm64 架構。

當您在不使用 AOT 的情況下部署 .NET Lambda 函數時,您的應用程式會先編譯成中繼語言 (IL) 程式碼。在執行階段,Lambda 執行階段中的 just-in-time (JIT) 編譯器會取得 IL 程式碼,並視需要將其編譯成機器程式碼。使用使用原生 AOT 提前編譯的 Lambda 函數,您可以在部署函數時將程式碼編譯成機器程式碼,這樣您就不需要依賴 Lambda 執行階段中的 .NET 執行階段或 SDK 來在程式碼執行之前編譯程式碼。

AOT 的一個限制是,您的應用程式程式碼必須在具有與 .NET 8 執行階段使用的相同 Amazon Linux 2023 (AL2023) 作業系統的環境中進行編譯。.NET Lambda CLI 提供了使用 AL2023 映像檔在碼頭容器中編譯應用程式的功能。

為了避免潛在的跨架構相容性問題,我們強烈建議您在與您為函數設定的相同處理器架構的環境中編譯程式碼。若要深入了解跨架構編譯的限制,請參閱 Microsoft .NET 文件中的交叉編譯

必要條件

Docker

若要使用原生 AOT,您的函數程式碼必須在與 .NET 8 執行階段具有相同 AL2023 作業系統的環境中編譯。以下各節中的 .NET CLI 命令使用泊塢視窗在 AL2023 環境中開發和建置 Lambda 函數。

.NET 8 SDK

原生 AOT 編譯是 .NET 8 的一項功能。您必須在建置電腦上安裝 .NET 8 SDK,而不僅是執行階段。

Amazon.Lambda.Tools

若要建立 Lambda 函數,請使用 Amazon.Lambda.Tools .NET Core Global Tools 延伸模組。若要安裝 Amazon.Lambda.Tools,請執行下列命令:

dotnet tool install -g Amazon.Lambda.Tools

如需有關 Amazon.Lambda.Tools .NET CLI 延伸模組的詳細資訊,請參閱上的 .NET CLI 存放庫的AWS 擴充功能 GitHub。

Amazon.Lambda.Templates

若要產生 Lambda 函數程式碼,請使用Amazon.Lambda.Templates NuGet 套件。若要安裝此範本套件,請執行下列命令:

dotnet new install Amazon.Lambda.Templates

開始使用

.NET 全域 CLI 和 AWS Serverless Application Model (AWS SAM) 都提供使用原生 AOT 建置應用程式的入門範本。若要建置您的第一個原生 AOT Lambda 函數,請按照下列指示中的步驟操作。

初始化和部署原生 AOT 編譯的 Lambda 函數
  1. 使用原生 AOT 範本初始化新專案,然後瀏覽至包含所建立 .cs.csproj 檔案的目錄。在此範例中,我們將函數命名為 NativeAotSample

    dotnet new lambda.NativeAOT -n NativeAotSample cd ./NativeAotSample/src/NativeAotSample

    原生 AOT 範本建立的 Function.cs 檔案包含以下函數程式碼。

    using Amazon.Lambda.Core; using Amazon.Lambda.RuntimeSupport; using Amazon.Lambda.Serialization.SystemTextJson; using System.Text.Json.Serialization; namespace NativeAotSample; public class Function { /// <summary> /// The main entry point for the Lambda function. The main function is called once during the Lambda init phase. It /// initializes the .NET Lambda runtime client passing in the function handler to invoke for each Lambda event and /// the JSON serializer to use for converting Lambda JSON format to the .NET types. /// </summary> private static async Task Main() { Func<string, ILambdaContext, string> handler = FunctionHandler; await LambdaBootstrapBuilder.Create(handler, new SourceGeneratorLambdaJsonSerializer<LambdaFunctionJsonSerializerContext>()) .Build() .RunAsync(); } /// <summary> /// A simple function that takes a string and does a ToUpper. /// /// To use this handler to respond to an AWS event, reference the appropriate package from /// https://github.com/aws/aws-lambda-dotnet#events /// and change the string input parameter to the desired event type. When the event type /// is changed, the handler type registered in the main method needs to be updated and the LambdaFunctionJsonSerializerContext /// defined below will need the JsonSerializable updated. If the return type and event type are different then the /// LambdaFunctionJsonSerializerContext must have two JsonSerializable attributes, one for each type. /// // When using Native AOT extra testing with the deployed Lambda functions is required to ensure // the libraries used in the Lambda function work correctly with Native AOT. If a runtime // error occurs about missing types or methods the most likely solution will be to remove references to trim-unsafe // code or configure trimming options. This sample defaults to partial TrimMode because currently the AWS // SDK for .NET does not support trimming. This will result in a larger executable size, and still does not // guarantee runtime trimming errors won't be hit. /// </summary> /// <param name="input"></param> /// <param name="context"></param> /// <returns></returns> public static string FunctionHandler(string input, ILambdaContext context) { return input.ToUpper(); } } /// <summary> /// This class is used to register the input event and return type for the FunctionHandler method with the System.Text.Json source generator. /// There must be a JsonSerializable attribute for each type used as the input and return type or a runtime error will occur /// from the JSON serializer unable to find the serialization information for unknown types. /// </summary> [JsonSerializable(typeof(string))] public partial class LambdaFunctionJsonSerializerContext : JsonSerializerContext { // By using this partial class derived from JsonSerializerContext, we can generate reflection free JSON Serializer code at compile time // which can deserialize our class and properties. However, we must attribute this class to tell it what types to generate serialization code for. // See https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-source-generation

    原生 AOT 會將應用程式編譯成單一原生二進位檔。該二進位檔的進入點是 static Main 方法。在 static Main 中,系統會引導 Lambda 執行期並設定 FunctionHandler 方法。在執行期引導程序中,原始碼產生的序列化程式是使用 new SourceGeneratorLambdaJsonSerializer<LambdaFunctionJsonSerializerContext>() 加以設定

  2. 若要將應用程式部署到 Lambda,請確定本機環境中的 Docker 正在執行,並執行下列命令。

    dotnet lambda deploy-function

    在幕後,.NET 全域 CLI 會下載 AL2023 Docker 映像檔,並在執行中的容器中編譯您的應用程式程式碼。編譯後的二進位檔會輸出回本機檔案系統,然後再部署至 Lambda。

  3. 執行以下命令來測試函數。以您在部署精靈中為函數選擇的名稱取代 <FUNCTION_NAME>

    dotnet lambda invoke-function <FUNCTION_NAME> --payload "hello world"

    CLI 的回應包括冷啟動 (初始化持續時間) 的效能詳細資訊,以及函數調用的總執行時間。

  4. 若要依照上述步驟刪除您建立的 AWS 資源,請執行下列命令。以您在部署精靈中為函數選擇的名稱取代 <FUNCTION_NAME>。刪除不再使用的 AWS 資源後,您就可以避免向您收取不必要的費用 AWS 帳戶。

    dotnet lambda delete-function <FUNCTION_NAME>

序列化

若要使用原生 AOT 將函數部署至 Lambda,您的函數程式碼必須使用原始碼產生的序列化。原始碼產生器不會使用執行期反射,來收集序列化存取物件屬性所需的中繼資料,而是會產生建置應用程式時編譯的 C# 原始碼檔案。若要正確設定原始碼產生的序列化程式,請確認函數使用的所有輸入和輸出物件及自訂類型都包含在內。例如,接收來自 API Gateway REST API 的事件並傳回自訂 Product 類型的 Lambda 函數,會包含定義如下的序列化程式。

[JsonSerializable(typeof(APIGatewayProxyRequest))] [JsonSerializable(typeof(APIGatewayProxyResponse))] [JsonSerializable(typeof(Product))] public partial class CustomSerializer : JsonSerializerContext { }

裁剪

本機 AOT 會在編譯過程中裁剪應用程式程式碼,以盡可能縮小二進位檔。與舊版 .NET 相比,Lambda 的 .NET 8 提供了改進的裁剪支援。Lambda 執行階段程式庫AWS .NET 開發套件、.NET Lambda 註解和 .NET 8 本身已新增 Support 援。

這些改進提供了消除構建時間修剪警告的可能性,但 .NET 永遠不會完全修剪安全。這表示函數相依的程式庫,可能會在編譯過程中遭到部分裁剪。您可以通過定義TrimmerRootAssemblies.csproj文件的一部分來管理這一點,如以下示例所示。

<ItemGroup> <TrimmerRootAssembly Include="AWSSDK.Core" /> <TrimmerRootAssembly Include="AWSXRayRecorder.Core" /> <TrimmerRootAssembly Include="AWSXRayRecorder.Handlers.AwsSdk" /> <TrimmerRootAssembly Include="Amazon.Lambda.APIGatewayEvents" /> <TrimmerRootAssembly Include="bootstrap" /> <TrimmerRootAssembly Include="Shared" /> </ItemGroup>

請注意,當您收到 trim 警告時,新增產生警告的類別TrimmerRootAssembly可能無法解決問題。修剪警告表示該類正在嘗試訪問某些其他類,直到運行時才能確定。若要避免執行階段錯誤,請將此第二個類別加入TrimmerRootAssembly

若要深入了解如何管理修剪警告,請參閱 Microsoft .NET 文件中的修剪警告簡介

故障診斷

Error: Cross-OS native compilation is not supported (錯誤:不支援跨作業系統原生編譯)。

您的 Amazon.Lambda.Tools .NET Core 全域工具版本太舊。請更新至最新版本再重試。

Docker: image operating system "linux" cannot be used on this platform (Docker:映像檔作業系統 "linux" 不能在此平台上使用)。

系統上的 Docker 設定為使用 Windows 容器。請更換至 Linux 容器以執行原生 AOT 建置環境。

如需有關常見錯誤的詳細資訊,請參閱上的 .NET 存放庫的AWS 原生 Aot。 GitHub