Controlador de funciones de AWS Lambda en C# - AWS Lambda

Controlador de funciones de AWS Lambda en C#

El controlador de la función Lambda es el método del código de la función que procesa eventos. Cuando se invoca una función, Lambda ejecuta el método del controlador. Si el controlador existe o devuelve una respuesta, pasará a estar disponible para gestionar otro evento.

Un controlador de función de Lambda se define como una instancia o un método estático en una clase. Si desea acceder al objeto de Lambda contexto, está disponible definiendo un parámetro de método de tipo iLambdaContext, una interfaz que puede utilizar para acceder a información sobre la invocación actual, como el nombre de la función actual, el límite de memoria, el tiempo de ejecución restante, y el registro.

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

En la sintaxis, tenga en cuenta lo siguiente:

  • inputType: el primer parámetro del controlador es su entrada, que puede estar formada por datos de eventos (publicados por un origen de eventos) o por una entrada personalizada proporcionada por el usuario, como una cadena o cualquier objeto de datos personalizado.

  • returnType: si piensa invocar la función de Lambda de forma síncrona (utilizando el tipo de invocación RequestResponse), puede devolver la salida de la función utilizando cualquiera de los tipos de datos admitidos. Por ejemplo, si utiliza una función de Lambda como backend de aplicaciones móviles, la invocación se realiza de forma síncrona. El tipo de datos de salida se serializará en JSON.

    Si piensa invocar la función de Lambda de forma asíncrona (utilizando el tipo de invocación Event), returnType debería ser void. Por ejemplo, si utiliza AWS Lambda con orígenes de eventos como Amazon S3 o Amazon SNS, estos orígenes de eventos invocan la función de Lambda mediante el tipo de invocación Event.

  • ILambdaContext context: el segundo argumento de la firma del controlador es opcional. Proporciona acceso al objeto context que tiene información sobre la función y la solicitud.

Control de flujos

De forma predeterminada, solo se admite el tipo System.IO.Stream como parámetro de entrada.

Por ejemplo, fíjese en el siguiente código de ejemplo de C#.

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

En el código C# del ejemplo, el primer parámetro del controlador es la entrada al controlador (MyHandler), que puede estar formada por datos de eventos (publicados por un origen de eventos como Amazon S3) o por una entrada personalizada proporcionada por el usuario, tal como Stream (como sucede en este ejemplo) o cualquier objeto de datos personalizado. La salida es de tipo Stream.

Control de tipos de datos estándar

Para todos los demás tipos, que se indican a continuación, tendrá que especificar un serializador.

  • Tipos primitivos de .NET (como string o int).

  • Colecciones y mapas: IList, IEnumerable, IList<T>, Array, IDictionary, IDictionary<TKey, TValue>

  • Tipos POCO (objetos CLR estándar simples, o Plain old CLR objects)

  • Tipos de eventos predefinidos de AWS

  • Para las invocaciones asíncronas, Lambda ignorará el tipo de retorno. El tipo de retorno se puede establecer en void en estos casos.

  • Si está utilizando la programación asíncrona de .NET, el tipo de retorno puede ser Task y Task<T>, y utilizar las palabras clave async y await. Para obtener más información, consulte Uso de async en funciones C# con AWS Lambda.

A menos que los parámetros de entrada y salida de la función sean del tipo System.IO.Stream, deberá serializarlos. AWS Lambda proporciona un serializador predeterminado que se puede aplicar en el nivel de ensamblado o de método de la aplicación, o puede definir uno implementando la interfaz ILambdaSerializer proporcionada por la biblioteca Amazon.Lambda.Core. Para obtener más información, consulte Implementar Lambda funciones de C# con archivos de archivo .zip.

Para añadir el atributo serializador predeterminado a un método, primero añada una dependencia de Amazon.Lambda.Serialization.Json en el archivo .csproj.

<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> <AssemblyName>AssemblyName</AssemblyName> </PropertyGroup> <ItemGroup> <PackageReference Include="Amazon.Lambda.Core" Version="1.2.0" /> <PackageReference Include="Amazon.Lambda.APIGatewayEvents" Version="2.3.0" /> <PackageReference Include="Amazon.Lambda.Serialization.Json" Version="1.8.0" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> </ItemGroup> </Project>

El ejemplo siguiente ilustra la flexibilidad que puede obtenerse especificando el serializador predeterminado de Json.NET en un método y otro elegido por el usuario en otro método:

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); } }
nota

Si utiliza .NET Core 3.1, le recomendamos que utilice el serializador Amazon.Lambda.Serialization.SystemTextJson. Este paquete proporciona una mejora de rendimiento sobre Amazon.Lambda.Serialization.Json.

Signaturas del controlador

Cuando cree funciones de Lambda, debe proporcionar una cadena de controlador que indique a AWS Lambda dónde debe buscar el código que va a invocar. En C#, tiene este formato:

ENSAMBLADO::TIPO::MÉTODO, donde:

  • ENSAMBLADO es el nombre del archivo de ensamblado de .NET para la aplicación. Cuando utilice la CLI de .NET Core para compilar la aplicación, si no ha establecido el nombre del ensamblado con la propiedad AssemblyName en .csproj, el nombre de ENSAMBLADO será el nombre de la carpeta que contiene el archivo .csproj. Para obtener más información, consulte CLI de .NET Core. En este caso, supongamos que el nombre de la carpeta es HelloWorldApp.

  • TIPO es el nombre completo del tipo de controlador, que consta del EspacioDeNombres y del NombreDeLaClase. En este caso Example.Hello.

  • MÉTODO es el nombre del controlador de la función, en este caso, MyHandler.

En definitiva, la signatura tendrá este formato: Ensamblado::EspacioDeNombres.NombreDeLaClase::NombreDelMétodo.

Ahora, considere el siguiente ejemplo:

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

La cadena del controlador sería: HelloWorldApp::Example.Hello::MyHandler.

importante

Si el método especificado en la cadena de controlador está sobrecargado, debe proporcionar la signatura exacta del método que Lambda debe invocar. AWS Lambda rechazará una signatura que de otra forma sería válida si la resolución requeriría seleccionar entre varias signaturas (sobrecargadas).

Serialización de las funciones de Lambda

Para las funciones de Lambda que utilizan tipos de entrada o salida distintos de un objeto Stream, tendrá que añadir una biblioteca de serialización a la aplicación. Puede hacerlo de las siguientes maneras:

  • Utilice el paquete Amazon.Lambda.Serialization.Json de NuGet. Esta biblioteca utiliza JSON.NET para gestionar la serialización.

    nota

    Si utiliza .NET Core 3.1, le recomendamos que utilice el serializador Amazon.Lambda.Serialization.SystemTextJson. Este paquete proporciona una mejora de rendimiento sobre Amazon.Lambda.Serialization.Json.

  • Creando su propia biblioteca de serialización mediante la implementación de la interfaz ILambdaSerializer, que está disponible formando parte de la biblioteca Amazon.Lambda.Core. La interfaz define dos métodos:

    • T Deserialize<T>(Stream requestStream);

      Puede implementar este método para deserializar la carga de solicitud desde la API Invoke en el objeto que se pasa al controlador de la función de Lambda.

    • T Serialize<T>(T response, Stream responseStream);.

      Puede implementar este método para serializar el resultado que devuelve el controlador de la función de Lambda en la carga de respuesta que devuelve la API Invoke.

Puede utilizar cualquier serializador que desee añadiéndolo como una dependencia al archivo MyProject.csproj.

... <ItemGroup> <PackageReference Include="Amazon.Lambda.Core" Version="1.0.0" /> <PackageReference Include="Amazon.Lambda.Serialization.Json" Version="1.3.0" /> </ItemGroup>

Seguidamente, deberá añadirlo a su archivo AssemblyInfo.cs. Por ejemplo, si utiliza el serializador predeterminado de Json.NET, esto es lo que debería añadir:

[assembly:LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]
nota

Puede definir un atributo de serialización personalizado en el nivel de método, que sustituirá al serializador predeterminado especificado en el nivel de ensamblado. Para obtener más información, consulte Control de tipos de datos estándar.

Restricciones del controlador de funciones de Lambda

Tenga en cuenta que existen algunas restricciones que afectan a la firma del controlador.

  • No puede ser unsafe ni utilizar tipos de puntero en la signatura del controlador, aunque el contexto unsafe puede utilizarse dentro del método del controlador y sus dependencias. Para obtener más información, consulte unsafe (Referencia de C#).

  • No puede pasar un número variable de parámetros utilizando la palabra clave params, ni utilizar ArgIterator como parámetro de entrada o de retorno que se utiliza para admitir un número variable de parámetros.

  • El controlador no puede ser un método genérico (por ejemplo, IList<T> Sort<T>(IList<T> input)).

  • No se admiten los controladores asíncronos con la signatura async void.

Uso de async en funciones C# con AWS Lambda

Si sabe que la función de Lambda requerirá un proceso de larga duración, como cargar archivos grandes en Amazon S3 o leer una gran cantidad de registros desde DynamoDB, puede utilizar el patrón async/await. Cuando se utiliza esta firma, Lambda invoca la función de forma síncrona y espera a que devuelva una respuesta o a que se agote el tiempo de espera de ejecución.

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

Si utiliza este patrón, debe tener en cuenta algunas consideraciones:

  • AWS Lambda no admite métodos async void.

  • Si crea una función de Lambda asíncrona sin implementar el operador await, .NET emitirá una advertencia de compilación y se producirá un comportamiento inesperado. Por ejemplo, algunas acciones asíncronas se ejecutarán, mientras que otras no. O algunas acciones asíncronas no se completarán antes de que finalice la invocación de la función.

    public async Task ProcessS3ImageResizeAsync(SimpleS3Event event) // Compiler warning { client.DoAsyncWork(input); }
  • La función de Lambda puede incluir varias llamadas asíncronas, que pueden invocarse en paralelo. Puede utilizar los métodos Task.WhenAll y Task.WhenAny para trabajar con varias tareas. Para utilizar el método Task.WhenAll, pase una lista de las operaciones en una matriz al método. En el ejemplo siguiente, tenga en cuenta que si olvida incluir cualquier operación en la matriz, la llamada puede volver antes de que finalice su operación.

    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); }

    Para utilizar el método Task.WhenAny, también se pasa una lista de las operaciones en una matriz al método. La llamada vuelve tan pronto como finaliza la primera operación, incluso si las demás siguen ejecutándose.

    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); }