AWS Lambda 上的 .NET 工作負載

模組 3

模組 3:AWS Lambda 上的 .NET

 學習模組

請注意,您可以按照此處提供的範例執行操作,但這並非強制要求。

AWS Lambda 支援多個 .NET 版本,這些版本可以在 x86_64 和 Arm64 (Graviton2) 架構上,而且您可以自由選擇架構。您的程式碼和部署程序不會變更。

Lambda 屬於無伺服器服務,您僅需按實際用量付費。如果您的 Lambda 函數需要每天運行幾次,您只需按實際用量支付費用。您可以根據需求擴展,並且 Lambda 函數可以同時啟動千個執行個體!

 完成時間

60 分鐘 

定價

如上所述,您只需按實際使用量付費。您支付的金額是根據您的 Lambda 函數執行的時間長短 (四捨五入到最接近的毫秒) 以及您分配給該函數的記憶體量計算得出的。

請記住,價格是根據分配的記憶體量計算的,而不是調用期間使用的記憶體量。因此,這讓透過測試函數來評估其在調用期間使用的最大記憶體量成為了一項有意義的任務。將分配的記憶體控制在必要的最低數量有助於降低使用 Lambda 函數的成本。有關更多資訊,請參閱此頁面的 AWS Lambda 定價。

請注意,如果您的 Lambda 函數使用 S3、Kinesis 等其他服務,這些服務也可能會產生費用。

.NET 支援的版本

在 Lambda 平台上執行 .NET 二進位檔案有多種方法。最常見、也是首要考慮的方法,是使用 AWS 提供的受管執行期系統。這是最簡單、最方便的方法,而且可提供最佳性能。如果您不希望或無法使用受管執行期系統,還有另外兩種方法可供選擇:自訂執行期系統或容器映像。這兩種方法都有各自的優點和缺點,下文將詳細討論。

受管執行期系統

AWS Lambda 服務提供各種熱門的執行期系統供您執行程式碼。AWS 會不斷更新 .NET 執行期系統使其一直處於最新狀態,並根據需要利用 Microsoft 提供的最新版本進行修補。作為開發人員,在管理程式碼使用的執行期系統時,除了在熟悉的.csproj 檔案中指定要使用的版本外,無需執行任何操作。

目前,AWS 僅為長期支持 (LTS) 版本的 .NET 執行期系統提供受管執行期系統,截至本文撰寫之時,為 .NET 3.1 和 .NET 6。.NET 受管的執行期系統適用於 x86_64 和 arm64 架構,並可在 Amazon Linux 2 上執行。如果您想使用非 AWS 提供的 .NET 版本,則可以建置自己的自訂執行期系統或建立符合您需求的容器映像。

如果您對 .NET 以外的領域感興趣,那麼了解 Lambda 服務也為其他語言 (Node.js、Python、Ruby、Java 和 Go) 提供受管執行期系統也是有好處的。有關受管執行期系統清單和所支援語言版本的完整詳細資訊,請參閱此頁的「可用執行期系統」

自訂執行期系統

自訂執行期系統是您自己建置和捆綁的執行期系統。建置和捆綁自訂執行期系統有以下幾種原因。最常見的原因是您想要使用 Lambda 服務提供的 .NET 版本,但其未作為受管執行期系統提供。另一個不太常見的原因是,您希望精確控制執行期系統的次要版本和補丁版本。

建立自訂執行期系統非常簡單。您要做的就是:

將 --self-contained true 傳遞給 build 命令。

這可以直接透過 dotnet build 來完成。您也可以透過帶有以下參數的 aws-lambda-tools-defaults.json 檔案來完成:

"msbuild-parameters": "--self-contained true"

就是這麼簡單,捆綁.NET Lambda 函數時的一個簡單編譯器標誌。現在,要部署的套裝軟體將包含您的程式碼以及您選擇的 .NET 執行期系統中所需的檔案。

您現在可以按需修補執行期系統和對其進行更新。更新執行期系統需要重新部署函數,因為函數程式碼和執行期系統是打包在一起的。

與受管執行期系統相比,部署的套裝軟體要大得多,因為它包含所有必要的執行期系統檔案。這會對冷開機時間產生不利影響 (稍後會詳細介紹)。為了幫助減小套裝軟體,可以考慮使用 .NET 編譯功能 TrimmingReadyToRun。不過在使用前,請先閱讀有關這些功能的文檔。

您可以使用在 Linux 上執行的任何版本的 .NET 建立自訂執行期系統。常見的使用案例是使用 .NET 的「當前」或預覽版本部署函數。

使用自訂執行期系統時,您可以使用社區提供的各種語言。甚至可以像其他人一樣建置自己的自訂執行期系統,來執行 Erlang 和 COBOL 等語言。

容器映像

除了受管執行期系統和自訂執行期系統,AWS Lambda 服務還可以將程式碼打包到容器映像中並將此映像部署到 Lambda 服務。該選項適合那些有時間建置程式碼並將其部署到容器的團隊,也適合那些需要對執行程式碼的作業系統和環境加強控制的團隊。最高可支援 10GB 大小的映像。

AWS 為 .NET 和 .NET 核心提供了各種基礎映像。https://gallery.ecr.aws/lambda/dotnet,該頁面的內容將説明您快速入門。

另一種方法是專門為您的函數建立自訂映像。這是一個進階的使用案例,需要您根據需求來編輯 Dockerfile。本課程未介紹這種方法,但是如果您想了解,可以查看以下存儲庫中的 Dockerfiles – https://github.com/aws/aws-lambda-dotnet/tree/master/LambdaRuntimeDockerfiles/Images

請注意,由於上傳檔較大,使用容器更新 Lambda 函數是最慢的。在這三種方法中,容器的冷開機也是最差的。本模組稍後將對此進行詳細介紹。

選擇適合您的執行期系統

如果您希望啟動性能最佳、易於部署、易於上手,並願意使用 .NET 的 LTS 版本,請選擇受管 .NET 執行期系統。

容器映像是個不錯的選擇,它可以讓您使用 AWS 為各種 .NET 版本建立的映像。或者,您可以選擇自己的容器映像,然後調整執行程式碼的作業系統和環境。容器映像還適用於已經廣泛使用容器的組織。

如果您對 .NET 及其執行期系統庫的版本有非常具體的要求,並且想要自己控制這些要求,請考慮使用自訂執行期系統。但請記住,維護和修補執行期系統需由您自己負責。如果 Microsoft 發佈了安全更新,您需要及時了解,並相應地更新您的自訂執行期系統。從性能的角度來看,自訂執行期系統是三種執行期系統中速度最慢的。

Lambda 函數啟動後,受管執行期、容器映像和自訂執行期系統的性能將非常相似。

適用於 .NET 的 AWS SDK

如果您一直在開發使用 AWS 服務的 .NET 應用程式,那麼您可能已經使用了適用於 .NET 的 AWS SDK。SDK 可讓 .NET 開發人員輕鬆地以一致且熟悉的方式調用 AWS 服務。SDK 會隨著服務的發佈或更新始終保持最新狀態。可從 NuGet 下載 SDK。

像許多與 AWS 相關的內容一樣,SDK 被分為多個較小的套裝軟體,每個套裝軟體處理一項服務。

例如,如果您想從 .NET 應用程式訪問 S3 儲存貯體,可以使用 AWSSDK.S3 NuGet 套裝軟體。或者,如果您想從 .NET 應用程式訪問 DynamoDB,可以使用 AWSSDK.DynamoDBv2 NuGet 套裝軟體。

不過,您只需添加自己需要的 NuGet 套裝軟體。透過將 SDK 拆分成較小的套裝軟體,可以縮小自己的部署套件。

如果您的 Lambda 函數處理常式需要從其他 AWS 服務接收事件,請查找與特定事件相關的 NuGet 套裝軟體,其中包含用於處理事件的相關類型。這些套裝軟體遵循 AWSSDK.Lambda.[SERVICE]Events 命名模式。

例如,如果您的 Lambda 函數:

由傳入的 S3 事件觸發,請使用 AWSSDK.Lambda.S3Events 套裝軟體

由傳入的 Kinesis 事件觸發,請使用 AWSSDK.Lambda.KinesisEvents 套裝軟體

由傳入的 SNS 通知觸發,請使用 AWSSDK.Lambda.SNSEvents 套裝軟體

由傳入的 SQS 訊息觸發,請使用 AWSSDK.Lambda.SQSEvents 套裝軟體

使用 SDK 與 AWS 服務進行交互非常簡單。在您的項目中添加對 NuGet 套裝軟體的引用,然後像使用任何其他 .NET 庫一樣調用該服務。

使用來自 Lambda 函數的 SDK 對您的使用方式沒有影響。

請記住,您不一定需要向專案中添加 AWS SDK NuGet 套裝軟體。例如,如果您的 Lambda 函式呼叫 AWS RDS SQL 伺服器,則您只需使用實體框架訪問資料庫即可。無需額外的 AWS 專用庫。但是,如果您想從 Secrets Manager 中檢索資料庫的使用者名稱/密碼,則需要添加 AWSSDK.SecretsManager NuGet 套裝軟體。

關於許可權的說明

一般而言,您應該使用執行任務所需的最低許可權級別。 在整個課程中,我們將鼓勵您並向您展示如何做到這一點。

但是,為了簡化本課程的教學,我們建議您使用具有 AdministratorAccess 政策的 AWS 使用者。此政策允許您建立部署 Lambda 函數時所需的角色。不學習課程時,您應該從您的 AWS 使用者中刪除此政策。

Hello World Style .NET Lambda 函數

正如您在之前的模組中所見,建立、部署和調用 .NET Lambda 函數非常簡單。在本節中,您將執行同樣的操作,但速度要慢一些,我將解釋每個步驟如何操作。將會討論生成的程式碼和設定檔案。

建立函數

您必須安裝所需的工具才能繼續執行此操作,有關如何執行此操作的更多詳細資訊,請參閱模組 3。

如果您現在不想跳轉到該模組,請參閱這裡的簡單提醒。

安裝 .NET Lambda 函數範本:

dotnet new -i Amazon.Lambda.Templates

安裝用於部署和管理 Lambda 函數的 .NET 工具:

dotnet tool install -g Amazon.Lambda.Tools

現在您已經安裝了範本,可以建立新函數了

從命令列執行:

dotnet new lambda.EmptyFunction -n HelloEmptyFunction

這將建立一個名為「HelloEmptyFunction」的新目錄。其中還有兩個目錄,src 和 test。顧名思義,src 目錄包含函數的程式碼,test 目錄則包含函數的單元測試。導航到這些目錄時,您會發現它們還分別包含另一個目錄。這些子目錄中包含函數的程式碼檔和單元測試檔案。

HelloEmptyFunction
    ├───src
    │   └───HelloEmptyFunction
    │           aws-lambda-tools-defaults.json // The default configuration file
    │           Function.cs // The code for the function
    │           HelloEmptyFunction.csproj // Standard C# project file
    │           Readme.md // A readme file
    │
    └───test
        └───HelloEmptyFunction.Tests
                FunctionTest.cs // The unit tests for the function
                HelloEmptyFunction.Tests.csproj // Standard C# project file

我們先來看看 Function.cs 檔案。

using Amazon.Lambda.Core;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace HelloEmptyFunction;

public class Function
{
    
    /// <summary>
    /// A simple function that takes a string and does a ToUpper
    /// </summary>
    /// <param name="input"></param>
    /// <param name="context"></param>
    /// <returns></returns>
    public string FunctionHandler(string input, ILambdaContext context)
    {
        return input.ToUpper();
    }
}

有幾行需要對注釋做一些解釋:

第 4 行,允許將 JSON 輸入轉換為 .NET 類。

第 17 行,會將函數的 JSON 輸入轉換為字串。

第 17 行,一個 ILambdaContext 物件作為參數傳入,它可以用於記錄、確定函數的名稱、函數執行了多長時間以及其他資訊。

如您所見,程式碼非常簡單,任何使用過 C# 的人應該都不陌生。

儘管這裡的 FunctionHandler 方法是同步的,但 Lambda 函數可以像任何其他 .NET 方法一樣,也可以是非同步的。您需要做的就是將 FunctionHandler 變更為

public async Task<string> FunctionHandler(..)

我們來看看 aws-lambda-tools-defaults.json 檔案:

{
  "Information": [
    "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.",
    "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.",
    "dotnet lambda help",
    "All the command line options for the Lambda command can be specified in this file."
  ],
  "profile": "",
  "region": "",
  "configuration": "Release",
  "function-runtime": "dotnet6",
  "function-memory-size": 256,
  "function-timeout": 30,
  "function-handler": "HelloEmptyFunction::HelloEmptyFunction.Function::FunctionHandler"
}

第 10 行,具體說明該函數應在發佈配置中建置。

第 11 行,向 Lambda 服務指定了要使用的執行期系統。

第 12 行,指定要分配給函數的記憶體量,在本例中為 256MB。

第 13 行,指定函數的超時時間,在本例中為 30 秒。最多允許超時 15 分鐘。

第 14 行,指定函數處理常式。調用此函數時,Lambda 服務將調用此方法。

函數處理常式由三部分組成:

"AssemblyName::Namespace.ClassName::MethodName"

稍後,您將使用該檔案中的配置建置此函數並將其部署到 AWS Lambda 服務。

但首先,我們來看看測試項目及其 HelloEmptyFunction.Tests.cs 檔案:

using Xunit;
using Amazon.Lambda.Core;
using Amazon.Lambda.TestUtilities;

namespace HelloEmptyFunction.Tests;

public class FunctionTest
{
    [Fact]
    public void TestToUpperFunction()
    {

        // Invoke the lambda function and confirm the string was upper cased.
        var function = new Function();
        var context = new TestLambdaContext();
        var upperCase = function.FunctionHandler("hello world", context);

        Assert.Equal("HELLO WORLD", upperCase);
    }
}

這裡的程式碼相對比較簡單,使用了 xUnit 測試框架。您可以像測試任何其他方法一樣測試您的 Lambda 函數。

第 14 行,建立了一個函數類的新執行個體。

第 15 行,建立了一個 TestLambdaContext 類的新執行個體,該執行個體將在下一行傳遞給 Lambda 函數。

第 16 行,調用函數上的 FunctionHandler 方法,傳入字串「hello world」和上下文。它將回應存儲在 upperCase 變數中。

第 18 行,斷言 upperCase 變數等於「HELLO WORLD」。

您可以從命令列執行該測試,也可以在您最喜歡的 IDE 內執行該測試。還可以對 Lambda 函數執行其他類型的測試,如果您有興趣了解更多相關資訊,請參閱之後的模組,在其中您將學習測試和調試 Lambda 函數的各種方法。

部署函數

現在您擁有一個 Lambda 函數,而且可能已經執行了單元測試,那麼就可以將 Lambda 函數部署到 AWS Lambda 服務了。

在命令列中,導航到包含 HelloEmptyFunction.csproj 檔的目錄並執行以下命令:

dotnet lambda deploy-function HelloEmptyFunction  

您會看到包含以下內容輸出 (為了清晰起見,我對其進行了刪減):

... dotnet publish --output "C:\dev\Lambda_Course_Samples\HelloEmptyFunction\src\HelloEmptyFunction\bin\Release\net6.0\publish" --configuration "Release" --framework "net6.0" /p:GenerateRuntimeConfigurationFiles=true --runtime linux-x64 --self-contained false
Zipping publish folder C:\dev\Lambda_Course_Samples\HelloEmptyFunction\src\HelloEmptyFunction\bin\Release\net6.0\publish to C:\dev\Lambda_Course_Samples\HelloEmptyFunction\src\HelloEmptyFunction\bin\Release\net6.0\HelloEmptyFunction.zip
... zipping: Amazon.Lambda.Core.dll
... zipping: Amazon.Lambda.Serialization.SystemTextJson.dll
... zipping: HelloEmptyFunction.deps.json
... zipping: HelloEmptyFunction.dll
... zipping: HelloEmptyFunction.pdb
... zipping: HelloEmptyFunction.runtimeconfig.json
Created publish archive (C:\dev\Lambda_Course_Samples\HelloEmptyFunction\src\HelloEmptyFunction\bin\Release\net6.0\HelloEmptyFunction.zip).

第 1 行,編譯並發佈項目。請注意,執行期系統為 linux-x64,自包含標誌為 false (這意味著該函數將在 Lambda 服務上使用受管的 .NET 執行期系統,而不是自訂執行期系統)。

第 2 行,將已發佈的專案壓縮為 zip 檔案。

第 3-8 行,顯示正在壓縮的檔案。

第 9 行,確認已建立 zip 檔案。

接下來,系統將詢問您「選擇為您的程式碼提供 AWS 憑證的 IAM 角色:」,您可能會看到之前建立的角色清單,但清單底部將顯示「**建立新 IAM 角色***」選項,在該選項旁邊輸入該數位。

系統將要求您「輸入新 IAM 角色的名稱:」。輸入「HelloEmptyFunctionRole」。

然後,系統將要求您「選擇要附加到新角色的 IAM 政策並授予許可權」,並顯示政策清單。看起來類似下列內容,但您的顯示內容可能更長:

1) AWSLambdaReplicator (Grants Lambda Replicator necessary permissions to replicate functions ...)
2) AWSLambdaDynamoDBExecutionRole (Provides list and read access to DynamoDB streams and writ ...)
3) AWSLambdaExecute (Provides Put, Get access to S3 and full access to CloudWatch Logs.)
4) AWSLambdaSQSQueueExecutionRole (Provides receive message, delete message, and read attribu ...)
5) AWSLambdaKinesisExecutionRole (Provides list and read access to Kinesis streams and write  ...)
6) AWSLambdaBasicExecutionRole (Provides write permissions to CloudWatch Logs.)
7) AWSLambdaInvocation-DynamoDB (Provides read access to DynamoDB Streams.)
8) AWSLambdaVPCAccessExecutionRole (Provides minimum permissions for a Lambda function to exe ...)
9) AWSLambdaRole (Default policy for AWS Lambda service role.)
10) AWSLambdaENIManagementAccess (Provides minimum permissions for a Lambda function to manage ...)
11) AWSLambdaMSKExecutionRole (Provides permissions required to access MSK Cluster within a VP ...)
12) AWSLambda_ReadOnlyAccess (Grants read-only access to AWS Lambda service, AWS Lambda consol ...)
13) AWSLambda_FullAccess (Grants full access to AWS Lambda service, AWS Lambda console feature ...) 

選擇「AWSLambdaBasicExecutionRole」,它位於列表第 6 位。

片刻之後,您會看到:

Waiting for new IAM Role to propagate to AWS regions
...............  Done
New Lambda function created

現在您可以調用該函數了。

調用函數

命令列

您可以使用 dotnet lambda tooling 從您選擇的 Shell 調用函數:

dotnet lambda invoke-function HelloEmptyFunction --payload "Invoking a Lambda function"

對於像上面這樣的簡單 Lambda 函數,沒有要跳脫的 JSON,但是當您想傳遞需要解序列化的 JSON 時,則跳脫有效負載將根據您使用的 shell 而有所不同。

您將看到如下所示的輸出:

Amazon Lambda Tools for .NET Core applications (5.4.1)
Project Home: https://github.com/aws/aws-extensions-for-dotnet-cli, https://github.com/aws/aws-lambda-dotnet

Payload:
"INVOKING A LAMBDA FUNCTION"

Log Tail:
START RequestId: 3d43c8be-8eca-48a1-9e51-96d9c84947b2 Version: $LATEST
END RequestId: 3d43c8be-8eca-48a1-9e51-96d9c84947b2
REPORT RequestId: 3d43c8be-8eca-48a1-9e51-96d9c84947b2  Duration: 244.83 ms      Billed Duration: 245 ms                                                                                                                  
Memory Size: 256 MB      Max Memory Used: 68 MB      Init Duration: 314.32 ms

輸出「Payload:」是 Lambda 函數的回應。

請注意日誌尾包含有關 Lambda 函數調用的有用資訊,例如它執行的時間以及使用多少記憶體。對於這樣的簡單函數,244.83 毫秒可能看起來很多,但這是第一次調用該函數,這意味著需要執行更多的工作,後續調用將更快。如需詳細資訊,請參閱冷啟動部分。

讓我們對程式碼進行一個小改變,以新增一些我們自己的日誌陳述式。

在 FunctionHandler 方法中,在返回陳述式上方新增以下內容:

context.Logger.LogInformation("Input: " + input);

使用下列方式重新部署:

dotnet lambda deploy-function HelloEmptyFunction

這次將沒有關於角色或權限的問題。

部署函數後,您可以再次調用它。

dotnet lambda invoke-function HelloEmptyFunction --payload "Invoking a Lambda function" 

這次,輸出將包含一個額外的日誌陳述式。

Payload:
"INVOKING A LAMBDA FUNCTION"

Log Tail:
START RequestId: 7f77a371-c183-494f-bb44-883fe0c57471 Version: $LATEST
2022-06-03T15:36:20.238Z        7f77a371-c183-494f-bb44-883fe0c57471    info    Input: Invoking a Lambda function
END RequestId: 7f77a371-c183-494f-bb44-883fe0c57471
REPORT RequestId: 7f77a371-c183-494f-bb44-883fe0c57471  Duration: 457.22 ms     Billed Duration: 458 ms                                                                                                       
Memory Size: 256 MB      Max Memory Used: 62 MB  Init Duration: 262.12 ms

在第 6 行上,是日誌陳述式。Lambda 函數日誌也會寫入 CloudWatch 日誌 (前提是您授予 Lambda 函數許可來執行此操作)。

AWS Console

調用該函數的另一種方法是從 AWS Console 進行調用。

登入 AWS Console,然後選取要調用的 Lambda 函數。

按一下「測試」分頁。

向下滾動到事件 JSON 部分,然後放入 "Invoking a Lambda function",包括引號。

然後按一下「測試」按鈕。

您將看到類似以下內容的輸出。

請注意,日誌輸出也可見。

採用 JSON 承載的 .NET Lambda 函數

上一個例子是一個簡單的函數,它採用一個字串並返回一個字串,它是一個開始使用的好例子。

但是您可能會想將 JSON 承載傳送到 Lambda 函數。事實上,如果另一個 AWS 服務調用您的 Lambda 函數,它將傳送 JSON 承載。這些 JSON 承載通常非常複雜,但 NuGet 將提供承載的模型。例如,如果您使用 Lambda 函數處理 Kinesis 事件,則 Amazon.lambda.KinesisEvents 套件具有 KinesisEvent 模型。S3 事件,SQS 事件等也是如此。

您現在不需要使用其中一個模型,而是調用一個新的 Lambda 函數,該函數代表一個人的承載。

{
  "FirstName": "Alan",
  "LastName": "Adams"
}

解序列化 JSON 承載的適當 C# 類是:

public class Person 
{
    public string FirstName { get; init; }
    public string LastName { get; init; }  
}

建立函數

與之前一樣,使用下列命令建立新函數:

dotnet new lambda.EmptyFunction -n HelloPersonFunction

更改函數

瀏覽至 HelloPersonFunction /src/HelloPersonFunction 中的 Function.cs 檔案。

將 FunctionHandler 方法的代碼更改為如下所示:
public string FunctionHandler(Person input, ILambdaContext context)
{
    return $"Hello, {input.FirstName} {input.LastName}";
}
在檔案底部,新增一個新類別:
public class Person 
{
    public string FirstName { get; init; }
    public string LastName { get; init; }  
}

這與您幾分鐘前使用的命令相同:

這與您幾分鐘前使用的命令相同:
dotnet lambda deploy-function HelloPersonFunction

調用函數

命令列
 

現在,您的 Lambda 函數可以接受 JSON 承載,但是您如何調用它取決於您使用的 Shell,因為 JSON 在每個命令介面中跳脫的方式可能不同。

如果您使用的是 PowerShell 或 bash,請使用:

dotnet lambda invoke-function HelloPersonFunction --payload '{ \"FirstName\": \"Alan\", \"LastName\": \"Adams\" }'
但是,如果您使用的是 Windows 命令提示字元,請使用:
dotnet lambda invoke-function HelloPersonFunction --payload "{ \"FirstName\": \"Alan\", \"LastName\": \"Adams\" }"
這只是一個簡單的例子。但是,當然,JSON 可以根據需要更加複雜。
AWS Console
 
調用該函數的另一種方法是從 AWS Console 進行調用。

登入 AWS Console,然後選取要調用的 Lambda 函數。
按一下「測試」分頁。
向下滾動到事件 JSON 部分並輸入:
{
  "FirstName": "Alan",
  "LastName": "Adams"
}
然後按一下「測試」按鈕。

您將看到類似以下內容的輸出。
過去的幾個例子相當簡單,Lambda 函數沒有利用任何其他 AWS 服務,但它們是開始使用並熟悉 AWS Lambda 服務、編寫 Lambda 函數、部署編譯的套件和調用函數的好方法。

在下一節中,您將看到如何部署回應 HTTP 請求的 Lambda 函數。

作為 Lambda 函數建立和執行 Web API 應用程式

在上一個範例中,Lambda 函數都是透過指令列或 AWS Console 中 Lambda 服務的測試功能中調用的。

但是您也可以透過 HTTP 請求調用 Lambda 函數,這是一個非常常見的用例。

適用於 .NET 的 AWS 工具提供了一些範本,您可以使用這些範本來建立託管 Web API 應用程式的簡單 Lambda 函數。

最熟悉的可能是 serverless.AspNetCoreWebAPI 範本,這會建立一個可以透過 HTTP 請求調用的簡單 Web API 應用程式。專案範本包含 CloudFormation 組態範本,該範本會建立一個 API 閘道,以將 HTTP 要求轉寄給 Lambda 函數。

部署到 AWS Lambda 時,API 閘道會將 HTTP 請求轉換為 API 閘道事件,並將此 JSON 傳送到 Lambda 函數。當 Lambda 函數部署到 Lambda 服務時,沒有 Kestrel 伺服器在函數中執行。

但是當您在本地執行它時,Kestrel Web 伺服器就會啟動,這使您可以非常輕鬆地編寫程式碼並在您熟悉的任何 Web API 應用程式中進行測試。您甚至可以透過行偵錯執行正常行。這是兩者都能兼顧的好方法!

建立函數

從命令行執行下列命令:
dotnet new serverless.AspNetCoreWebAPI -n HelloAspNetCoreWebAPI
這會建立一個名為 HelloAspNetCoreWebAPI 的新目錄。其中還有兩個目錄,src 和 test。顧名思義,src 目錄包含函數的程式碼,test 目錄則包含函數的單元測試。導航到這些目錄時,您會發現它們還分別包含另一個目錄。這些子目錄中包含函數的程式碼檔和單元測試檔案。
├───src
│   └───AspNetCoreWebAPI
│       │   appsettings.Development.json
│       │   appsettings.json
│       │   AspNetCoreWebAPI.csproj
│       │   aws-lambda-tools-defaults.json // basic Lambda function config, and points to serverless.template file for deployment
│       │   LambdaEntryPoint.cs // Contains the function handler method, this handles the incoming JSON payload
│       │   LocalEntryPoint.cs // Equivalent to Program.cs when running locally, starts Kestrel (only locally)
│       │   Readme.md  
│       │   serverless.template // CloudFormation template for deployment
│       │   Startup.cs // Familiar Startup.cs, can use dependency injection, read config, etc.
│       │
│       └───Controllers
│               ValuesController.cs // Familiar API controller
│
└───test
    └───AspNetCoreWebAPI.Tests
        │   appsettings.json
        │   AspNetCoreWebAPI.Tests.csproj
        │   ValuesControllerTests.cs // Unit test for ValuesController
        │
        └───SampleRequests
                ValuesController-Get.json // JSON representing an APIGatewayProxyRequest, used by the unit test

部署函數

基於 serverless.* 範本的函數比基於 lambda.* 範本部署函數要複雜一點。

在嘗試部署無伺服器函數之前,您需要 S3 儲存貯體。部署工具將使用此儲存貯體來儲存 CloudFormation 堆疊。

您可以使用現有的 S3 儲存貯體,如果沒有,請使用下方說明建立。
建立 S3 儲存貯體
 
如果您使用 us-east-1 區域,則可以使用下列命令來建立儲存貯體:
aws s3api create-bucket --bucket your-unique-bucket-name1234 
或者如果您使用其他區域
aws s3api create-bucket --bucket your-unique-bucket-name1234 --create-bucket-configuration LocationConstraint=REGION
我在 us-east-1 中建立了一個名為 named lambda-course-2022 的儲存貯體。
aws s3api create-bucket --bucket lambda-course-2022 
返回部署函數
 
現在您擁有 S3,您可以執行部署命令:
dotnet lambda deploy-serverless
系統會提示您輸入 CloudFormation 堆疊名稱。
Enter CloudFormation Stack Name: (CloudFormation stack name for an AWS Serverless application)
輸入 AspNetCoreWebAPI

然後,系統會要求您輸入 S3 儲存貯體名稱。使用您之前建立的儲存貯體的名稱,或您要用於此目的的現有儲存貯體的名稱。

輸入之後,開始建置和部署程序。

這會比使用 lambda.* 的專案範例花費更長時間,因為需要建立和連接更多的基礎架構。

輸出將分為兩個不同的部分。

頂端部分將與您之前部署函數時所看到的相似,即專案的發佈和壓縮,但這次輸出將上傳到 S3。
..snip
... zipping: AspNetCoreWebAPI.runtimeconfig.json
... zipping: aws-lambda-tools-defaults.json
Created publish archive (C:\Users\someuser\AppData\Local\Temp\AspNetCoreFunction-CodeUri-Or-ImageUri-637907144179228995.zip).
Lambda project successfully packaged: C:\Users\ someuser\AppData\Local\Temp\AspNetCoreFunction-CodeUri-Or-ImageUri-637907144179228995.zip
Uploading to S3. (Bucket: lambda-course-2022 Key: AspNetCoreWebAPI/AspNetCoreFunction-CodeUri-Or-ImageUri-637907144179228995-637907144208759417.zip)
... Progress: 100%
之後,將建立 CloudFormation 堆疊,並部署所有所需的資源。
Uploading to S3. (Bucket: lambda-course-2022 Key: AspNetCoreWebAPI/AspNetCoreWebAPI-serverless-637907144211067892.template)
... Progress: 100%
Found existing stack: False
CloudFormation change set created
... Waiting for change set to be reviewed
Created CloudFormation stack AspNetCoreWebAPI

Timestamp            Logical Resource Id                      Status
-------------------- ---------------------------------------- ---------------------------------------- 
6/10/2022 09:53 AM   AspNetCoreWebAPI                         CREATE_IN_PROGRESS
6/10/2022 09:53 AM   AspNetCoreFunctionRole                   CREATE_IN_PROGRESS
6/10/2022 09:53 AM   AspNetCoreFunctionRole                   CREATE_IN_PROGRESS
6/10/2022 09:54 AM   AspNetCoreFunctionRole                   CREATE_COMPLETE
6/10/2022 09:54 AM   AspNetCoreFunction                       CREATE_IN_PROGRESS
6/10/2022 09:54 AM   AspNetCoreFunction                       CREATE_IN_PROGRESS
6/10/2022 09:54 AM   AspNetCoreFunction                       CREATE_COMPLETE
6/10/2022 09:54 AM   ServerlessRestApi                        CREATE_IN_PROGRESS
6/10/2022 09:54 AM   ServerlessRestApi                        CREATE_IN_PROGRESS
6/10/2022 09:54 AM   ServerlessRestApi                        CREATE_COMPLETE
6/10/2022 09:54 AM   ServerlessRestApiDeploymentcfb7a37fc3    CREATE_IN_PROGRESS
6/10/2022 09:54 AM   AspNetCoreFunctionProxyResourcePermissionProd CREATE_IN_PROGRESS
6/10/2022 09:54 AM   AspNetCoreFunctionRootResourcePermissionProd CREATE_IN_PROGRESS
6/10/2022 09:54 AM   AspNetCoreFunctionProxyResourcePermissionProd CREATE_IN_PROGRESS
6/10/2022 09:54 AM   AspNetCoreFunctionRootResourcePermissionProd CREATE_IN_PROGRESS
6/10/2022 09:54 AM   ServerlessRestApiDeploymentcfb7a37fc3    CREATE_IN_PROGRESS
6/10/2022 09:54 AM   ServerlessRestApiDeploymentcfb7a37fc3    CREATE_COMPLETE
6/10/2022 09:54 AM   ServerlessRestApiProdStage               CREATE_IN_PROGRESS
6/10/2022 09:54 AM   ServerlessRestApiProdStage               CREATE_IN_PROGRESS
6/10/2022 09:54 AM   ServerlessRestApiProdStage               CREATE_COMPLETE
6/10/2022 09:54 AM   AspNetCoreFunctionProxyResourcePermissionProd CREATE_COMPLETE
6/10/2022 09:54 AM   AspNetCoreFunctionRootResourcePermissionProd CREATE_COMPLETE
6/10/2022 09:54 AM   AspNetCoreWebAPI                         CREATE_COMPLETE
Stack finished updating with status: CREATE_COMPLETE

Output Name                    Value
------------------------------ --------------------------------------------------
ApiURL                         https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/Prod/

底部就是您可以用來調用 API 的公開 URL。

調用函數

如果在瀏覽器或 Fiddler/Postman 等中開啟該 URL,您會看到一個回應,例如「歡迎在 AWS Lambda 上執行 ASP.NET Core」。

然後嘗試打開 https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/Prod/api/values,您將調用值控制器 GET 方法,就像在普通 Web API 應用程式中一樣。

請注意,當使用 API Gateway 時,閘道會自行設定 29 秒的逾時。如果您的 Lambda 函數執行超過此時間,您將不會收到回應。

如果您有興趣,有幾種方法可以檢閱已建立的資源。

若要檢閱建立的 AWS 資源,您可以使用:
aws cloudformation describe-stack-resources --stack-name AspNetCoreWebAPI
這將為您提供有關堆疊中每個資源的詳細資訊。

如果您想要更簡潔的輸出,請使用:
aws cloudformation describe-stack-resources --stack-name AspNetCoreWebAPI --query 'StackResources[].[{ResourceType:ResourceType, LogicalResourceId:LogicalResourceId, PhysicalResourceId:PhysicalResourceId}]'
另一種方法是開啟 AWS Console 並導覽至 Lambda 服務。在螢幕左側,選擇應用程式 (而不是函數)。


您將看到您建立的應用程式。選取它,然後捲動到開啟的頁面底部。您將看到此處建立的所有資源。
您現在看到了 Lambda 函數的三個例子,第一個是一個簡單的 Hello World,採用字串,第二個也非常簡單,但是採用了 JSON 物件,而第三個則更複雜,但仍然很容易建立和部署。

使用這些範例,您可以建立和部署自己的 Lambda 函數。您可能已經了解了一些關於如何調用 .NET Lambda 函數的知識。這就是下一節的主題。

函數 URL – API 閘道的替代方案

如果您只需要一個 Lambda 函數來回應簡單的 HTTP 請求,則應考慮使用 Lambda 函數 URL。

它們允許您將 HTTPS 端點指派給 Lambda 函數。然後,您可以透過向 HTTPS 端點提出請求來調用 Lambda 函數。如需詳細資訊,請參閱此部落格文章和這些文件

清理您建立的資源

要刪除 Lambda 函數,請執行:
dotnet lambda delete-function HelloEmptyFunction  
dotnet lambda delete-function HelloPersonFunction

請注意,上述命令不會刪除您建立的角色。

若要刪除託管 Web API 應用程式的 Lambda 函數,以及所有相關資源,請執行:

dotnet lambda delete-serverless AspNetCoreWebAPI

如何調用 .NET Lambda 函數

如您可以從上面的示例中看到,您可以使用簡單的字串,JSON 物件和 HTTP 請求調用 .NET Lambda 函數。Lambda 函數也可以被其他服務調用,例如 S3 (發生檔案變更時),Kinesis (當事件到達時),DynamoDB (在表格上發生變更時),SMS (當消息到達時), Step Functions 等。

Lambda 函數如何處理所有這些不同的調用方式?

在幕後,當 Lambda 服務執行函數處理常式並傳遞 JSON 輸入時,會調用這些 Lambda 函數。如果您查看 aws-lambda-tools-defaults.json,您可以看到指定的 "function-handler":。對於 .NET Lambda 函數,處理常式包含 "AssemblyName::Namespace.ClassName::MethodName"。

Lambda 函數也可以透過傳遞串流來調用,但這是一種不常見的情況,請參閱處理串流頁面了解更多資訊。

每個 Lambda 函數都有一個函數處理常式。

除了 JSON 輸入之外,Lambda 函數處理常式還可以採用一個可選的 ILambdaContext 物件。這可讓您存取目前調用的相關資訊,例如剩餘的時間、函數名稱和版本。您也可以透過 ILambdaContext 物件寫入日誌訊息。

所有事件都是 JSON

AWS 服務非常容易調用 .NET Lambda 函數的原因是這些服務會發出 JSON,如上所述,.NET Lambda 函數接受 JSON 輸入。不同服務引起的事件都會產生不同形狀的 JSON,但 AWS Lambda 事件 NuGet 套件包含所有相關物件類型,以便將 JSON 序列化為物件供您使用。

如需可用 Lambda 套件的清單,請參閱 https://www.nuget.org/packages?packagetype=&sortby=relevance&q=Amazon.Lambda&prerel=True,您必須在這些結果中搜尋您感興趣的事件類型。

例如,如果您想要觸發 Lambda 函數來回應 S3 儲存貯體上的檔案變更,則需要建立一個 Lambda 函數,該函數接受類型 S3Event 的物件。然後,您將 Amazon.Lambda.S3Events 套件新增到您的專案中。然後將函數處理常式方法更改為:

public async string FunctionHandler(S3Event s3Event, ILambdaContext context)
{
    ...
}

這就是處理 S3 事件所需的,您可以以程式設計方式檢查事件,查看在檔案上執行了什麼操作,它在哪個儲存貯體中等。Amazon.Lambda.S3Events 允許您處理事件,而不是使用 S3 本身,如果您想與 S3 服務互動,則需要將 AWSSDK.S3 NuGet 套件新增到您的專案中。稍後的模組將介紹 AWS 服務調用 Lambda 函數的主題。

對於其他類型的事件,則會使用相同的模式,新增 NuGet 套件,將參數更改為函數處理常式,然後您可以使用事件物件。

以下是您可能用來處理來自其他服務的事件的一些常見套件 -

https://www.nuget.org/packages/Amazon.Lambda.SNSEvents

https://www.nuget.org/packages/Amazon.Lambda.DynamoDBEvents

https://www.nuget.org/packages/Amazon.Lambda.CloudWatchEvents

https://www.nuget.org/packages/Amazon.Lambda.KinesisEvents

https://www.nuget.org/packages/Amazon.Lambda.APIGatewayEvents

調用 Lambda 函數時,您不限於使用 AWS 定義的事件類型。您可以自己建立任何類型的事件,請記住 Lambda 函數可以接收您傳送的任何 JSON

序列化運作方式

您會記住 Lambda 專案範本有兩個大類別,以「lambda.」開頭的類別,以及以「serverless.」開頭的類別。

在「lambda.」範本的情況下,Function.cs 檔案的頂部附近有一個組件屬性,該屬性負責將傳入事件解序列化到函數處理常式中的 .NET 類型。在 .csproj 檔案中,有一個引用到 Amazon.Lambda.Serialization.SystemTextJson 套件。
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
您不需要採取其他行動。

對於「serverless.」範本,它的工作方式有些不同。

函數處理常式是在 serverless.template 檔案中指定的。如果您要部署 serverless.AspNetCoreWebAPI 應用程式,請在 Resources.AspNetCoreFunction.Properties.Handler 下尋找值。此類型專案的處理程式將以此格式結尾:Assembly::Namespace.LambdaEntryPoint::FunctionHandlerAsync。

LambdaEntryPoint 類將位於您的專案中,它沿用具有 FunctionHandlerAsync 方法的類別。

函數處理常式可設定為處理四種不同的事件類型:API Gateway REST API、API Gateway HTTP API 承載版本 1.0、API Gateway HTTP API 承載版本 2.0 以及 Application Load Balancer。

透過變更 LambdaEntryPoint 沿用的類別,您可以變更函數處理常式處理的 JSON 事件類型。

儘管 Lambda 函數看起來正在使用您定義的 JSON 回應您發送的 HTTP 請求,但情況並非如此。實際上,您的 HTTP 請求是由閘道或負載平衡器處理,然後建立一個 JSON 事件,該事件傳送到您的函數處理常式。此 JSON 事件將包含最初包含在 HTTP 請求中的資料,包括源 IP 地址和請求標頭的所有內容。

並行

使用 Lambda 函數時,有兩種並行需要考慮:保留並行和佈建並行。

AWS 帳戶對並行 Lambda 執行數目有預設最大限制。截至本文時,該限制為 1,000。

當您為函數指定保留並行時,您確保該函數將能夠達到指定的同時執行數量。例如,如果您的函數的保留並行為 200,則您確保該函數將能夠達到 200 個函數同時執行。請注意,這會為其他函數保留 800 個並行執行 (1000-200=800)。

當您指定佈建並行性時,您會初始化 Lambda 執行環境的指定數目。當它們初始化後,Lambda 函數將能夠立即回應請求,避免「冷啟動」問題。但是,使用佈建並行需要支付費用。

如需詳細資訊,請參閱管理 Lambda 保留並行性管理 Lambda 佈建並行。

冷啟動和溫啟動

在調用 Lambda 函數之前,必須初始化執行環境,此操作 Lambda 服務會代您完成。您的來源程式碼是從 AWS 受管的 S3 儲存貯體 (適用於使用受管理執行期和自訂執行期的函數),或從 Elastic Container Registry (適用於使用容器映像的函數) 下載的。

當您的函數第一次執行時,您的程式碼需要被 JITed,並執行初始化程式碼 (例如您的建構模組)。這增加了冷啟動時間。

如果您的函數定期被調用,它將保持「溫暖」,即將維持執行環境。後續調用該函數不會受到冷啟動時間的影響。「溫啟動」比「冷啟動」更快。

如果您的函數在一段時間內未調用 (Lambda 服務未指定確切的時間),則會移除執行環境。下一次調用該函數將再次導致冷啟動。

如果您上傳函數代碼的新版本,則下次調用函數將導致冷啟動。

對於在 Lambda 上執行 .NET,受管理的執行期、自訂執行期以及託管容器的三個選項都有不同的冷啟動設定檔。最慢的是容器,稍快的是自訂執行期,最快的是受管理的執行期。如果可能,在執行 .NET Lambda 函數時,您應始終選擇受管理的執行期。

您會發現冷啟動在測試或開發環境中發生更頻繁,而不是在生產環境中。在 AWS 分析中,低於 1% 的調用中發生冷啟動。

如果您在生產中有一個 Lambda 函數不常使用,但需要快速回應請求,並且想避免冷啟動,則可以使用佈建並行,或使用機制經常「ping」函數以保持函數不被移除。

如果您對有關最佳化 Lambda 函數的更多資訊感興趣,可以在 AWS Lambda 開發人員指南中閱讀有關冷啟動、溫啟動和佈建並行的資訊,或查看 Lambda 操作:效能最佳化部落格系列 James Beswick - 第 1 部分第 2 部分第 3 部分

調整 .NET 7 之前的 .NET 版本並準備執行

如果您選擇在 .NET 7 之前的 .NET 版本使用 Lambda 自訂執行期,您可以使用一些 .NET 功能來減少冷啟動時間。

PublishTrimmed 將透過從套件中修剪不必要的程式庫來減少您部署的套件的整體大小。

PublishReadyToRun 將提前對您的程式碼進行編譯,透過減少所需的即時編譯量來進行提前編譯。但它會增加您部署的套件的大小。

為了獲得最佳效能,您必須在使用這些選項時測試您的函數。

您可以從 .csproj 檔案啟用 PublishTrimmed 和 PublishReadyToRun。

<PublishTrimmed>true</PublishTrimmed>
<PublishReadyToRun>true</PublishReadyToRun>

結語

此模組已向您介紹如何建立、部署和調用 .NET Lambda 函數。它提供 AWS Lambda 上各種 .NET 執行方式的概觀,並解釋了您選擇某種方式而非另一種方式的原因。此模組還解釋了關於「冷啟動」和最佳化的概念。


.NET 7 的原生前期編譯

NET 7 於 2022 年 11 月發布,支持原生的前期 (AOT) 編譯。AWS Lambda 的 .NET 範本工具可讓您建置和部署 .NET 7 自訂執行期函數。測試中,與 .NET 6 管理執行期相比,.NET 7 Lambda 函數的冷啟動速度快 86%。
 
若要利用此新功能,您需要將 .NET Lambda 專案範本更新為 6.7.0 或更新版本,並將 .NET 的 AWS 擴充功能更新為 5.6.1 或更新版本。
 
若要執行更新,請執行下列步驟:
dotnet new -i "Amazon.Lambda.Templates::*"dotnet tool update -g Amazon.Lambda.Tools
在撰寫本文時,Lambda AOT 函數有兩個模板:lambda.NativeAOT 和 serverless.NativeAOT。您還需要安裝並執行 Docker。

知識檢查

您現在已完成模組 2「使用 AWS Lambda 的適用於 .NET 開發的工具」。以下測試可讓您檢查到目前為止學習到的內容。

1.Lambda 服務提供哪些版本的 .NET 受管理執行期?(選擇兩個)

a. .NET Core 5

b. .NET 6

c. .NET 7

d. .NET Core 3.1

e. .NET Framework 4.8

2.冷啟動是什麼意思?(選擇一項)

a.啟動 Lambda 執行環境和函數初始化程式碼所需的時間。

b.使用 AWS S3 Glacier 儲存的 Lambda 函數。

c.將程式碼部署到 Lambda 服務所需的時間。

d.更新函數所需的時間

3.您如何將 AWS .NET 軟體開發套件與 .NET Lambda 函數一起使用?

a.在專案檔案中新增 SDK 套件的參考。

b.您不需要一起使用,它包括 Lambda 函數範本

c.您不需要使用,IDE 的工具套件包括該函數

d.透過 AWS Console 將 SDK 新增至 Lambda 服務

4.當您建立新的 Lambda.EmptyFunction 專案時,指定函數組態的檔案名稱是什麼?

a. serverless.template

b. lambda.csproj

c. aws-lambda-tools-defaults.json

5.以下哪種方法是調用 Lambda 函數的方法?

a.使用 dotnet lambda tooling 的命令列

b.HTTPS 請求

c.從其他 AWS 服務調用

d.以上都是

答案:1-bd,2-a,3-a,4-c,5-d

結語

此模組已向您介紹如何建立、部署和調用 .NET Lambda 函數。它提供 AWS Lambda 上各種 .NET 執行方式的概觀,並解釋了您選擇某種方式而非另一種方式的原因。此模組還解釋了關於「冷啟動」和最佳化的概念。

本頁對您是否有幫助?

與其他 AWS 服務搭配使用