AWS Lambda
开发人员指南
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门

C# 中的 AWS Lambda 函数处理程序

创建 Lambda 函数时,需指定一个处理程序以供 AWS Lambda 服务在代表您执行函数时调用。

将 Lambda 函数处理程序定义为某个类中的实例或静态方法。如果您希望访问 Lambda context 对象,可通过定义类型为 ILambdaContext 的方法参数来实现,该参数是一个您可以用来访问有关当前执行的信息(例如,当前函数的名称、内存限制、剩余执行时间和日志记录)的接口。

returnType handler-name(inputType input, ILambdaContext context) { ... }

在该语法中,需要注意以下方面:

  • inputType – 第一个处理程序参数是处理程序的输入,它可以是事件数据(由事件源发布)或您提供的自定义输入(如字符串或任意自定义数据对象)。

  • returnType – 如果打算同步调用 Lambda 函数(使用 RequestResponse 调用类型),则可以使用任何支持的数据类型返回函数输出。例如,如果使用 Lambda 函数作为移动应用程序后端并同步调用它,则输出数据类型会序列化为 JSON。

    如果打算异步调用 Lambda 函数(使用 Event 调用类型),则 returnType 应为 void。例如,将 AWS Lambda 搭配 Amazon S3 或 Amazon SNS 之类的事件源使用时,这些事件源会使用 Event 调用类型调用 Lambda 函数。

处理流

System.IO.Stream 类型在默认情况下可作为输入参数受支持。

例如,考虑以下 C# 示例代码。

using System.IO; namespace Example { public class Hello { public Stream MyHandler(Stream stream) { //function logic } }

在此 C# 示例代码中,第一个处理程序参数是处理程序 (MyHandler) 的输入,它可以是事件数据(由 Amazon S3 之类的事件源发布),也可以是您提供的自定义输入(如本示例中的 Stream)或任何自定义数据对象。输出的类型为 Stream

处理标准数据类型

其他所有类型(如下所列)均需要您指定串行器。

  • Primitive .NET 类型(例如 string 或 int)。

  • 集合和映射 - IList、IEnumerable、IList<T>、Array、IDictionary、IDictionary<TKey 和 TValue>

  • POCO 类型(无格式的旧 CLR 对象)

  • 预定义的 AWS 事件类型

  • Lambda 将忽略异步调用的返回类型。在这种情况下,可能会将返回类型设置为 void。

  • 如果您使用的是 .NET 异步编程,则返回类型可以是 Task 和 Task<T>,并且可使用 asyncawait 关键字。有关更多信息,请参阅在使用 C# 编写的 AWS Lambda 函数中应用 Async

除非函数输入和输出参数的类型为 System.IO.Stream,否则您需要对这些参数执行序列化。AWS Lambda 提供了可在应用程序的程序集或方法级别应用的默认串行器,或者您也可以通过实施由 Amazon.Lambda.Core 库提供的 ILambdaSerializer 接口定义自己的串行器。有关更多信息,请参阅C# 中的 AWS Lambda 部署程序包

要向某个方法添加默认的串行器属性,请先添加对 project.json 文件中 Amazon.Lambda.Serialization.Json 的依赖关系。

{ "version": "1.0.0-*", "dependencies":{ "Microsoft.NETCore.App": { "type": "platform", "version": "1.0.1" }, "Amazon.Lambda.Serialization.Json": "1.3.0" }, "frameworks": { "netcoreapp1.0": { "imports": "dnxcore50" } } }

以下示例说明了您可以分别针对自己选择的各种方法灵活地指定默认 Json.NET 串行器:

public class ProductService{ [LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))] public Product DescribeProduct(DescribeProductRequest request) { return catalogService.DescribeProduct(request.Id); } [LambdaSerializer(typeof(MyJsonSerializer))] public Customer DescribeCustomer(DescribeCustomerRequest request) { return customerService.DescribeCustomer(request.Id); } }

处理程序签名

创建 Lambda 函数时,您必须提供一个处理程序字符串,告知 AWS Lambda 在何处查找要调用的代码。在 C# 中,格式如下:

ASSEMBLY::TYPE::METHOD,其中:

  • ASSEMBLY 是您的应用程序的 .NET 程序集文件的名称。使用 .NET Core CLI 构建应用程序时,如果未使用 project.json 中的 buildOptions.outputName 设置来设置程序集名称,则 ASSEMBLY 名称将为包含 project.json 文件的文件夹的名称。有关更多信息,请参阅.NET Core CLI。在这种情况下,假设文件夹名称为 HelloWorldApp

  • TYPE 是包含 NamespaceClassName 的处理程序类型的全名。在本例中为 Example.Hello

  • METHOD 是函数处理程序的名称,在本例中为 MyHandler

签名最终将是以下格式:Assembly::Namespace.ClassName::MethodName

请考虑以下示例:

using System.IO; namespace Example { public class Hello { public Stream MyHandler(Stream stream) { //function logic } }

处理程序字符串为:HelloWorldApp::Example.Hello::MyHandler

重要

如果在处理程序字符串中指定的方法重载,您必须提供 Lambda 应调用的方法的准确签名。如果该解决方法要求在多个(重载)签名中进行选择,AWS Lambda 将拒绝其他有效签名。

Lambda 函数处理程序限制

请注意,处理程序签名存在一些限制。

  • 处理程序签名不能是unsafe的,且不得使用指针类型,但在处理程序方法及其依赖关系中可以使用unsafe上下文。有关更多信息,请参阅不安全(C# 参考)

  • 处理程序签名不得使用 params 关键字传递可变数量的参数,也不得将用于支持可变数量参数的 ArgIterator 用作输入或返回参数。

  • 处理程序不能是泛型方法(例如 IList<T> Sort<T>(IList<T> input))。

  • 不支持使用签名 async void 的 Async 处理程序。

在使用 C# 编写的 AWS Lambda 函数中应用 Async

如果您知道自己的 Lambda 函数需要长时间的运行过程,例如上传大型文件到 Amazon S3 或者从 DynamoDB 中读取大量记录,则您可以利用 async/await 模式。当您使用此签名时,Lambda 会同步执行函数并等待该函数返回响应或执行超时

public async Task<Response> ProcessS3ImageResizeAsync(SimpleS3Event input) { var response = await client.DoAsyncWork(input); return response; }

使用此模式时,您必须谨记以下几项注意事项:

  • AWS Lambda 不支持 async void 方法。

  • 如果您创建了一个 async Lambda 函数,但未实施 await 运算符,.NET 将发出一个编译器,提醒您注意意外行为。例如,有些 async 操作会执行,而有些不会。或者,有些 async 操作不会在函数执行完成时结束。

    public async Task ProcessS3ImageResizeAsync(SimpleS3Event event) // Compiler warning { client.DoAsyncWork(input); }
  • 您的 Lambda 函数可包含多个可并行调用的 async 调用。您可使用 Task.WhenAllTask.WhenAny 方法来处理多项任务。要使用 Task.WhenAll 方法,您需要将一个操作列表作为阵列传递至该方法。请注意,在以下示例中,如果您忘记在该阵列中包含任何操作,则调用可能会在操作结束之前返回。

    public async Task DoesNotWaitForAllTasks1() { // In Lambda, Console.WriteLine goes to CloudWatch Logs. var task1 = Task.Run(() => Console.WriteLine("Test1")); var task2 = Task.Run(() => Console.WriteLine("Test2")); var task3 = Task.Run(() => Console.WriteLine("Test3")); // Lambda may return before printing "Test2" since we never wait on task2. await Task.WhenAll(task1, task3); }

    要使用 Task.WhenAny 方法,您同样需要将一个操作列表作为阵列传递至该方法。该调用将在第一个操作结束时返回,即使其他操作仍在运行也是如此。

    public async Task DoesNotWaitForAllTasks2() { // In Lambda, Console.WriteLine goes to CloudWatch Logs. var task1 = Task.Run(() => Console.WriteLine("Test1")); var task2 = Task.Run(() => Console.WriteLine("Test2")); var task3 = Task.Run(() => Console.WriteLine("Test3")); // Lambda may return before printing all tests since we're only waiting for one to finish. await Task.WhenAny(task1, task2, task3); }