

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

# 將 .NET Lambda 函數程式碼編譯為原生執行時期格式
<a name="dotnet-native-aot"></a>

.NET 8 支援原生預先 (AOT) 編譯。使用原生 AOT，您可以將 Lambda 函數程式碼編譯為原生執行階段格式，這樣便不需要在執行階段編譯 .NET 程式碼。原生 AOT 編譯可縮短以 .NET 撰寫之 Lambda 函數的冷啟動時間。如需詳細資訊，請參閱 AWS 運算部落格上的[適用於 AWS Lambda 的 .NET 8 執行時期簡介](https://aws.amazon.com/blogs/compute/introducing-the-net-8-runtime-for-aws-lambda/)。

**Topics**
+ [Lambda 執行時間](#dotnet-native-aot-runtime)
+ [必要條件](#dotnet-native-aot-prerequisites)
+ [開始使用](#dotnet-native-aot-getting-started)
+ [序列化](#dotnet-native-aot-serialization)
+ [裁剪](#dotnet-native-aot-trimming)
+ [疑難排解](#dotnet-native-aot-troubleshooting)

## Lambda 執行時間
<a name="dotnet-native-aot-runtime"></a>

若要使用原生 AOT 編譯部署 Lambda 函數建置，請使用受管 .NET 8 Lambda 執行時期。此執行時期支援同時使用 x86\$164 和 arm64 架構。

不使用 AOT 來部署 .NET Lambda 函數時，您的應用程式會首先編譯成中繼語言 (IL) 程式碼。在執行時期，Lambda 執行時期中的即時 (JIT) 編譯器需使用 IL 程式碼，並根據需要編譯為機器碼。透過使用原生 AOT 預先編譯的 Lambda 函數，可以在部署函數時將程式碼編譯為機器程式碼，因此在執行之前，您不需要依賴 Lambda 執行時期中的 .NET 執行時期或 SDK 來編譯程式碼。

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

為了避免跨架構相容性的潛在問題，強烈建議您在具有您為函數設定之相同處理器架構的環境中編譯程式碼。若要進一步了解跨架構編譯的限制，請參閱 Microsoft .NET 文件中的[跨編譯](https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/cross-compile)。

## 必要條件
<a name="dotnet-native-aot-prerequisites"></a>

**Docker**  
若要使用原生 AOT，編譯函數程式碼時所用環境的 AL2023 作業系統必須與 .NET 8 執行時期相同。以下各節中的 .NET CLI 命令，使用 Docker 在 AL2023 環境中開發和建置 Lambda 函數。

**.NET 8 SDK**  
原生 AOT 編譯是 .NET 8 的一項功能。除了執行時期之外，您也必須在建置機器上安裝 [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)。

**Amazon.Lambda.Tools**  
若要建立 Lambda 函數，請使用 [https://www.nuget.org/packages/Amazon.Lambda.Tools](https://www.nuget.org/packages/Amazon.Lambda.Tools) [.NET Core Global Tools 延伸模組](https://aws.amazon.com/blogs/developer/net-core-global-tools-for-aws/)。若要安裝 Amazon.Lambda.Tools，請執行下列命令：  

```
dotnet tool install -g Amazon.Lambda.Tools
```
如需有關 Amazon.Lambda.Tools .NET CLI 延伸模組的詳細資訊，請參閱 GitHub 上的[適用於 .NET CLI 的 AWS 延伸模組](https://github.com/aws/aws-extensions-for-dotnet-cli)儲存庫。

**Amazon.Lambda.Templates**  
若要產生 Lambda 函數程式碼，請使用 [https://www.nuget.org/packages/Amazon.Lambda.Templates](https://www.nuget.org/packages/Amazon.Lambda.Templates) NuGet 套件。若要安裝此範本套件，請執行下列命令：  

```
dotnet new install Amazon.Lambda.Templates
```

## 開始使用
<a name="dotnet-native-aot-getting-started"></a>

.NET Global 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>()` 加以設定

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

   ```
   dotnet lambda deploy-function
   ```

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

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

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

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

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

   ```
   dotnet lambda delete-function <FUNCTION_NAME>
   ```

## 序列化
<a name="dotnet-native-aot-serialization"></a>

若要使用原生 AOT 將函數部署至 Lambda，您的函數程式碼必須使用[原始碼產生的序列化](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/source-generation-modes?pivots=dotnet-8-0)。原始碼產生器不會使用執行期反射，來收集序列化存取物件屬性所需的中繼資料，而是會產生建置應用程式時編譯的 C\$1 原始碼檔案。若要正確設定原始碼產生的序列化程式，請確認函數使用的所有輸入和輸出物件及自訂類型都包含在內。例如，接收來自 API Gateway REST API 的事件並傳回自訂 `Product` 類型的 Lambda 函數，會包含定義如下的序列化程式。

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

## 裁剪
<a name="dotnet-native-aot-trimming"></a>

本機 AOT 會在編譯過程中裁剪應用程式程式碼，以盡可能縮小二進位檔。與舊版 .NET 相比，適用於 Lambda 的 .NET 8 可提供更好的修剪支援。支援已新增至 [Lambda 執行時期程式庫](https://github.com/aws/aws-lambda-dotnet/pull/1596)、[AWS .NET SDK](https://github.com/aws/aws-sdk-net/pulls?q=is%3Apr+trimming)、[.NET Lambda Annotations](https://github.com/aws/aws-lambda-dotnet/pull/1610)和 .NET 8 本身。

這些改進可能消除建置時間修剪警告，但 .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>
```

請注意，當您收到修剪警告時，將產生警告的類別新增至 `TrimmerRootAssembly` 可能無法解決問題。修剪警告表示類別正在嘗試存取一些其他類別，它們在執行時期之前無法確定。若要避免執行時期錯誤，請將此第二個類別新增至 `TrimmerRootAssembly`。

若要進一步了解如何管理修剪警告，請參閱 Microsoft .NET 文件中的[修剪警告簡介](https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/fixing-warnings)。

## 疑難排解
<a name="dotnet-native-aot-troubleshooting"></a>

**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 建置環境。

如需有關常見錯誤的詳細資訊，請參閱 GitHub 上的[適用於 .NET 的 AWS NativeAOT](https://github.com/awslabs/dotnet-nativeaot-labs#common-errors) 儲存庫。