AWS Lambda annonce la prise en charge de .NET 6 pour les développeurs qui créent des applications sans serveur,
ils peuvent profiter de nouvelles fonctionnalités telles que l'amélioration de la journalisation

AWS Lambda prend désormais en charge .NET 6 à la fois en tant que runtime géré et image de base de conteneur. Les développeurs qui créent des applications sans serveur dans Lambda avec .NET 6 peuvent profiter de nouvelles fonctionnalités telles que l'amélioration de la journalisation, la simplification des définitions de fonctions à l'aide d'instructions de haut niveau et l'amélioration des performances à l'aide de générateurs de sources. L'utilisation de .NET 6 permet également de profiter des nouvelles fonctionnalités du langage .NET et des optimisations de performances

.NET est le Framework de développement de Microsoft pour les applications d'entreprises. L’équipe de développement de .NET a annoncé le 8 novembre la sortie de .NET 6. « Bienvenue à .NET 6. La version d'aujourd'hui est le résultat d'un peu plus d'un an d'efforts de la part de l'équipe .NET et de la communauté. .NET 6 est la première version qui prend en charge de manière native les processeurs Apple Silicon (Arm64) et a également été améliorée pour Windows Arm64 », a déclaré Richard Lander, responsable de programme au sein de l'équipe .NET Core.

Nom : aws.jpg
Affichages : 15965
Taille : 9,5 Ko

« .NET 6 est vraiment le .NET le plus rapide à ce jour », déclare Alex Yakunin, Directeur technique chez ServiceTitan, une plateforme de technologie logicielle. Selon Yakunin, .NET 6 apporte jusqu'à 40 % de gain de vitesse par rapport .NET 5. .NET 6 RC1 a été testé et est pris en charge par la Preview 4 de Visual Studio 2022. Dans la version 6 de .NET, les diagnostics du cloud ont été améliorés avec dotnet monitor et OpenTelemetry. La prise en charge de WebAssembly est plus performante. De nouvelles API ont été ajoutées, pour HTTP/3, le traitement de JSON, les mathématiques et la manipulation directe de la mémoire.

Pour créer et déployer des fonctions Lambda à l'aide de .NET 6, utilisez la CLI .NET Core, AWS Toolkit for Visual Studio ou AWS Serverless Application Model (AWS SAM). En outre, vous pouvez également utiliser l'image de base .NET 6 fournie par AWS pour créer et déployer des fonctions .NET 6 à l'aide d'une image de conteneur. Pour migrer les fonctions Lambda existantes exécutant des versions antérieures de .NET, consultez l'article de blog lié ci-dessus.

.NET 6 est la dernière version de support à long terme (LTS) de .NET et sera supportée pour la sécurité et les corrections de bogues jusqu'en novembre 2024. AWS appliquera automatiquement les mises à jour du moteur d'exécution géré .NET 6 et de l'image de base .NET 6 fournie par AWS, dès qu'elles seront disponibles.
.NET 6 offre de nombreuses nouvelles fonctionnalités aux développeurs .NET, notamment la prise en charge de C# 10 et F# 6. En plus de ces fonctionnalités de .NET 6, voici, ci-dessous, les nouvelles fonctionnalités ajoutées à l'expérience Lambda de .NET. Elles peuvent être utilisées pour améliorer les diagnostics et les performances et utiliser de nouveaux modèles de codage.

Amélioration de la journalisation

La consignation dans les fonctions Lambda .NET a été améliorée pour .NET 6, ce qui permet une meilleure traçabilité et un meilleur contrôle de ce qui est consigné. Si l’utilisateur préfère le style de journalisation des exécutions gérées de .NET précédentes, la variable d'environnement AWS_LAMBDA_HANDLER_LOG_FORMAT doit être définie sur Unformatted.

ID de la demande

L'une des fonctionnalités les plus fréquemment demandées pour le précédent moteur d'exécution Lambda .NET est l'ajout de l'ID de la demande Lambda aux journaux pour une meilleure traçabilité. Cette fonctionnalité est disponible dans le moteur d'exécution .NET 6, ce qui rend le format de journalisation .NET similaire à celui des autres moteurs d'exécution Lambda.

Niveaux de journalisation

La journalisation de .NET 6 utilise des niveaux de journalisation. L'ILambdaLogger est accessible à partir de l'ILambdaContext et dispose des nouvelles API de journalisation suivantes :

  • LogCritical(string message) ;
  • LogError(string message) ;
  • LogWarning(string message) ;
  • LogInformation(string message) ;
  • LogDebug(string message) ;
  • LogTrace(string message) ;
  • Log(LogLevel level, string message).

Les niveaux des messages de journal sont visibles dans les journaux d'Amazon CloudWatch, comme l'identifiant de la demande. Il est ainsi plus facile de filtrer et de rechercher dans les journaux des types de messages particuliers, tels que les erreurs ou les avertissements.

Les appels Console.WriteLine sont écrits dans les journauxCloudWatch comme un message de niveau info ; les appels Console.Error.WriteLine sont écrits comme un message de niveau erreur. L'exemple suivant montre l'utilisation de messages d'information pour consigner l'objet utilisateur récupéré. Il écrit un message d'avertissement si l'utilisateur n'est pas trouvé :

Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public APIGatewayProxyResponse Get(APIGatewayProxyRequest request, ILambdaContext context)
{
    User user = null;
    try
    {
        var id = request.PathParameters["id"];
 
        context.Logger.LogInformation($"Loading user {id}");
        user = FetchUser(id);
        context.Logger.LogInformation($"User: {user.Name}");
    }
    catch(Exception e)
    {
        context.Logger.LogWarning($"Unable to find user: {e.Message}");
    }
 
    ...
 
}


Lorsque user ne peut pas être récupéré, voici les messages du journal qui en résultent, indiquant le niveau du journal et l'identifiant de la demande :

Nom : AWS123.jpg
Affichages : 1361
Taille : 73,4 Ko

Par défaut, les messages de niveau info ou supérieur sont écrits dans les journaux CloudWatch. Vous pouvez ajuster le niveau écrit dans les journaux CloudWatch à l'aide de la variable d'environnement AWS_LAMBDA_HANDLER_LOG_LEVEL. La valeur de la variable d'environnement est définie par les valeurs de l'enum LogLevel.

Avec ce nouveau filtrage, il est possible d’instrumenter les fonctions Lambda avec une journalisation supplémentaire en utilisant les niveaux de journalisation de débogage et de trace. Cela permet d'activer une journalisation supplémentaire à partir des fonctions Lambda pour le dépannage, sans redéployer un nouveau code.

Utilisation du générateur de sources pour la sérialisation JSON

C# 9 fournit des générateurs de sources, qui permettent de générer du code pendant la compilation. Cela peut réduire l'utilisation des API de réflexion et améliorer le temps de démarrage des applications. .NET 6 a mis à jour la bibliothèque JSON native System.Text.Json pour utiliser les générateurs de sources, ce qui permet d'analyser JSON sans avoir recours aux API de réflexion.

Il est possible de tirer parti de la prise en charge des générateurs de sources de System.Text.Json pour améliorer les performances de démarrage à froid. Pour ce faire, le paquet Amazon.Lambda.Serialization.SystemTextJson qui gère la sérialisation des événements et des réponses Lambda en types .NET est utilisé.

Pour utiliser le générateur de sources, il faut définir une nouvelle classe vide dans le projet qui dérive de System.Text.Json.Serialization.JsonSerializerContext. Cette classe doit être une classe partielle car le générateur de sources ajoute du code à cette classe pour gérer la sérialisation. Sur la classe partielle vide, l’utisateur doit ajouter l'attribut JsonSerializable pour chaque type .NET pour lequel le générateur de sources doit générer le code de sérialisation.

Voici un exemple appelé HttpApiJsonSerializerContext qui enregistre les types d'événements et de réponses de l'API HTTP Amazon API Gateway pour que le code de sérialisation soit généré :

Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
[JsonSerializable(typeof(APIGatewayHttpApiV2ProxyRequest))]
[JsonSerializable(typeof(APIGatewayHttpApiV2ProxyResponse))]
public partial class HttpApiJsonSerializerContext : JsonSerializerContext
{
}

Les fonctions lambda utilisant Amazon.Lambda.Serialization.SystemTextJson utilisent l'attribut Amazon.Lambda.Core.LambdaSerializer pour enregistrer le sérialiseur. Le plus souvent, le type DefaultLambdaJsonSerializer est spécifié. Pour utiliser le générateur de sources, l’utilisateur doit enregistrer SourceGeneratorLambdaJsonSerializer, en passant la sous-classe JsonSerializerContext définie précédemment comme paramètre générique.

Voici un exemple d'enregistrement du sérialiseur à l'aide du type HttpApiJsonSerializerContext :

Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
[assembly: LambdaSerializer(typeof(SourceGeneratorLambdaJsonSerializer<APIGatewayExampleImage.HttpApiJsonSerializerContext>))]

Après ces étapes, Lambda utilise le code de sérialisation JSON généré par la source pour gérer toute la sérialisation des événements et des réponses Lambda. Les appels d'API Reflection ne sont pas utilisés pour la sérialisation, ce qui améliore les performances de démarrage à froid de la fonction Lambda.
Voici, ci-dessous, un exemple complet d'une fonction Lambda basée sur la passerelle API utilisant le générateur de sources.

Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using Amazon.Lambda.Core;
using Amazon.Lambda.APIGatewayEvents;
using Amazon.Lambda.Serialization.SystemTextJson;
 
[assembly: LambdaSerializer(typeof(SourceGeneratorLambdaJsonSerializer<SourceGeneratorExample.HttpApiJsonSerializerContext>))]
 
namespace SourceGeneratorExample;
 
[JsonSerializable(typeof(APIGatewayHttpApiV2ProxyRequest))]
[JsonSerializable(typeof(APIGatewayHttpApiV2ProxyResponse))]
public partial class HttpApiJsonSerializerContext : JsonSerializerContext
{
}
 
 
public class Functions
{
    public APIGatewayProxyResponse Get(APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context)
    {
        context.Logger.LogInformation("Get Request");
 
        var response = new APIGatewayHttpApiV2ProxyResponse
        {
            StatusCode = (int)HttpStatusCode.OK,
            Body = "Hello AWS Serverless",
            Headers = new Dictionary<string, string> { { "Content-Type", "text/plain" } }
        };
 
        return response;
    }
}

Instructions de haut niveau

Le nouveau moteur d'exécution Lambda de .NET 6 prend en charge l'écriture de fonctions Lambda à l'aide de la fonction d'instructions de haut niveau de C# 9. Les instructions de premier niveau vous permettent de supprimer une grande partie du code standard initial d'un projet .NET. Dans un exemple typique de "hello world" :
using System;

Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
namespace Application
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(“Enjoying .NET 6 in AWS Lambda”);
        }
    }
}

Avec les déclarations de haut niveau, il est possible d’écrire cela en une seule ligne, en supprimant les parenthèses, les indentations, les espaces de noms et les déclarations de types :

Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
Console.WriteLine("Enjoying .NET 6 in AWS Lambda");

À un niveau élevé, le compilateur C# génère la méthode Main() de l'assemblage .NET, qui contient le code de premier niveau.

Assemblages exécutables

Avec les déclarations de haut niveau, la méthode Main() a été générée par le compilateur. Cette méthode est différente de la méthode traditionnelle d'écriture des fonctions Lambda .NET. Auparavant, un projet Lambda était une bibliothèque de classes et le gestionnaire de la fonction Lambda était défini sur l'assemblage, le type et le nom de la méthode que le client d'exécution Lambda invoque.

Voici un exemple de chaîne de traitement de fonction Lambda .NET :

Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
LambdaProject::LambdaProject.Function::FunctionHandler

Et voici à quoi pourrait ressembler le code de ce gestionnaire de fonction :

Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System.IO;
using System.Threading.Tasks;
 
using Amazon.Lambda.Core;
using Amazon.Lambda.S3Events;
using Amazon.S3;
 
// 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 LambdaProject
{
    public class Function
    {
        IAmazonS3 _s3Client;
 
        public Function()
        {
            _s3Client = new AmazonS3Client();
        }
 
        public async Task FunctionHandler(S3Event evnt, IlambdaContext context)
        {
            foreach (var record in evnt.Records)
            {
                using var response = await _s3Client.GetObjectAsync(record.S3.Bucket.Name, record.S3.Object.Key);
                using var reader = new StreamReader(response.ResponseStream);
                // Run business logic on the text contexts of the S3 object
            }
        }
    }
}

Grâce à la réflexion, le client d'exécution Lambda .NET utilise la chaîne du gestionnaire de fonction pour identifier la méthode à appeler dans l'assemblage .NET.
Lorsque des instructions de haut niveau sont utilisées, on demande à Lambda d'exécuter l'assemblage, qui exécute les instructions de haut niveau. Pour indiquer que l’on souhaite que Lambda exécute l'assemblage, le gestionnaire de fonction Lambda doit être définit sur le nom de l'assemblage uniquement. Dans l'exemple précédent, la chaîne du gestionnaire de la fonction Lambda .NET est LambdaProject.

L'assemblage .NET contenant la fonction Lambda étant exécuté au démarrage, au lieu du client d'exécution Lambda, le code de fonction doit lancer le client d'exécution Lambda afin que les événements Lambda soient envoyés au code. Pour lancer le client d'exécution Lambda :

  1. Ajoutez le paquet Amazon.Lambda.RuntimeSupport NuGet au projet ;
  2. Dans le fichier qui définit toutes les déclarations de haut niveau, ajoutez à la fin du fichier le code permettant de démarrer le client d'exécution Lambda. Le code exact est présenté à la fin de l'exemple ci-dessous.

Il s'agit d'un exemple complet d'une fonction Lambda C# utilisant des instructions de haut niveau qui traitent les événements Lambda :

Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using Amazon.Lambda.Core;
using Amazon.Lambda.RuntimeSupport;
using Amazon.Lambda.Serialization.SystemTextJson;
using Amazon.Lambda.S3Events;
using Amazon.S3;
 
// Code outside of the handler will be executed during Lambda initialization
var s3Client = new AmazonS3Client();
 
// The function handler that will be called for each Lambda event
var handler = async (S3Event evnt, ILambdaContext context) =>
{
    foreach(var record in evnt.Records)
    {
        using var response = await s3Client.GetObjectAsync(record.S3.Bucket.Name, record.S3.Object.Key);
        using var reader = new StreamReader(response.ResponseStream);
        // Run business logic on the text contexts of the S3 object
    }
};
 
// Build the Lambda runtime client passing in the handler to call for each
// event and the JSON serializer to use for translating Lambda JSON documents
// to .NET types.
await LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer())
        .Build()
        .RunAsync();

API minimales d'ASP.NET Core

Depuis le premier moteur d'exécution Lambda de .NET, il est possible d’exécuter des applications ASP.NET Core en tant que fonctions Lambda à l'aide du paquet NuGet Amazon.Lambda.AspNetCoreServer. .NET 6 introduit un nouveau style d'écriture des applications ASP.NET Core appelé Minimal APIs. Celles-ci tirent parti de la prise en charge des instructions de premier niveau de C# 9, ce qui simplifie l'initialisation d'une application ASP.NET Core et permet de définir une application ASP.NET Core entière dans un seul fichier.

Pour déployer une application ASP.NET Core en utilisant des API minimales pour Lambda :

  1. Ajoutez le paquet NuGet Amazon.Lambda.AspNetCoreServer.Hosting à votre projet ;
  2. Ajoutez un appel à AddAWSLambdaHosting dans votre application lorsque les services sont définis pour l'application. L'argument de AddAWSLambdaHosting est la source de l'événement pour la fonction Lambda. Il peut s'agir d'une API REST ou HTTP Gateway, ou d'un équilibreur de charge d'application.

Lorsque le projet ASP.NET Core est exécuté localement, AddAWSLambdaHosting ne fait rien, permettant au serveur Web normal de Kestrel .NET de gérer l'expérience locale. Lorsqu'il est exécuté dans Lambda, AddAWSLambdaHosting remplace Kestrel par Amazon.Lambda.AspNetCoreServer, ce qui permet à Lambda et à API Gateway d'agir comme serveur Web à la place de Kestrel. Étant donné que les API minimales tirent parti des déclarations de niveau supérieur, AddAWSLambdaHosting lance également le client d'exécution Lambda.

Cet exemple montre une application Minimal API ASP.NET Core. Il existe une ligne spécifique à Lambda appelant AddAWSLambdaHosting qui configure le projet pour la prise en charge de Lambda :

Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using Amazon.S3;
 
var builder = WebApplication.CreateBuilder(args);
 
// Add Swagger/OpenAPI support
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
 
// Add S3 service client to dependency injection container
builder.Services.AddAWSService<IAmazonS3>();
 
// Add AWS Lambda support.
builder.Services.AddAWSLambdaHosting(LambdaEventSource.HttpApi);
 
var app = builder.Build();
 
app.UseSwagger();
app.UseSwaggerUI();
 
// Example GET route
app.MapGet("/document/{name}", async (IAmazonS3 s3Client, string name) =>
{
    using var response = await s3Client.GetObjectAsync(app.Configuration["S3Bucket"], name);
    using var reader = new StreamReader(response.ResponseStream);
    var content = await reader.ReadToEndAsync();
 
    // Run business logic on the text contexts of the S3 object
 
    return content;
});
 
app.Run();

Vous devez déployer en tant qu'assemblage exécutable afin que la chaîne du gestionnaire de fonctions soit définie sur le nom de l'assemblage uniquement. Par exemple, voici comment l'application ASP.NET Core précédente est définie dans AWS CloudFormation :
Code JSON : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
   "AspNetCoreFunction": {
      "Type": "AWS::Serverless::Function",
      "Properties": {
        "Handler": "AspNetCoreMinimalApiExample", // The assembly name only
        "Runtime": "dotnet6"
        "MemorySize": 256,
        "Timeout": 30,
        "Role": null,
        "Policies": [
          "AWSLambda_FullAccess",
          "AmazonS3ReadOnlyAccess"
        ],
        "Events": {
          "ProxyResource": {
            "Type": "HttpApi",
            "Properties": {
              "Path": "/{proxy+}",
              "Method": "ANY"
            }
          },
          "RootResource": {
            "Type": "HttpApi",
            "Properties": {
              "Path": "/",
              "Method": "ANY"
            }
          }
        }
      }
    }
  },

Client d'exécution Lambda open source

Au cours des dernières années, AWS a mis en open source davantage de composants de Lambda pour aider la communauté à contribuer à l'expérience Lambda. Pour .NET, vous pouvez trouver toutes les bibliothèques client AWS dans le dépôt GitHub aws/aws-lambda-dotnet.

Pour .NET 6, le runtime géré utilise désormais le client runtime Lambda open source du dépôt aws/aws-lambda-dotnet. Auparavant, le client d'exécution Lambda open source était utilisé pour les fonctions qui utilisaient l'exécution personnalisée de Lambda ou le support basé sur l'image du conteneur.

Désormais, l'expérience du client d'exécution Lambda est cohérente et transparente dans tous les environnements, qu'il s'agisse du runtime géré, des images de conteneurs ou de l'utilisation du client d'exécution Lambda pour les runtimes personnalisés .NET. Le passage du précédent client d'exécution au client d'exécution open source est transparent, car les fonctions Lambda sont migrées vers .NET 6.

Le client d'exécution Lambda open source présente des caractéristiques de performance différentes de celles du client d'exécution Lambda .NET Core 3.1. Cela s'explique par le fait que le client open source utilise uniquement du code géré, alors que le client .NET Core 3.1 utilise un mélange de code géré et natif. Dans nos tests, les démarrages à froid pour les fonctions de base "Hello, world !" peuvent être légèrement plus rapides dans .NET Core 3.1.

Cependant, pour les fonctions Lambda qui effectuent un travail réel, les tests montrent une amélioration significative du démarrage à froid dans .NET 6. Par exemple, une fonction Lambda .NET 6 qui utilise le SDK AWS .NET pour récupérer un élément de DynamoDB a montré une amélioration des performances de 25 %.
Migration vers .NET 6

Pour migrer les fonctions Lambda .NET existantes vers le nouveau moteur d'exécution .NET 6 :

  1. Ouvrez le fichier csproj ou fsproj. Définissez l'élément TargetFramework sur net6.0 ;
  2. Ouvrez le fichier aws-lambda-tools-defaults.json, s'il existe : ;
    • Définissez le champ function-runtime sur dotnet6 ;
    • Définissez le champ framework sur net6.0. Si vous supprimez le champ, la valeur est déduite du fichier de projet.
  3. S'il existe, ouvrez le fichier serverless.template. Pour toute ressource AWS::Lambda::Function ou AWS::Serverless::Function, définissez la propriété Runtime sur dotnet6 ;
  4. Mettre à jour toutes les références des paquets Amazon.Lambda.* NuGet avec les dernières versions.

Source : AWS

Et vous ?

Quel est votre avis sur le sujet ?

Voir aussi :

Microsoft annonce .NET 6, version la plus rapide à ce jour avec prise en charge des puces Apple Silicon, des containers Windows isolés des processus et une amélioration des E/S de fichiers

.NET 6 apporterait jusqu'à 40 % de gain de vitesse par rapport .NET 5, les tests de mise en cache et de boucles serrées serraient jusqu'à 30 % plus rapide

AWS présente une flopée de nouveaux services de bases de données et de ML, et annonce en Preview version RDS Custom pour Microsoft SQL Server et AWS DMS Fleet Advisor

AWS lance CloudShell, un Shell basé sur le Web pour l'accès en ligne de commande à AWS, qui peut être lancé directement à partir de l'AWS Management Console