Entrainement
Module
Implémenter la résilience au sein d’un microservice natif Cloud - Training
Ce module vous guide tout au long de l’implémentation de la résilience d’une application de microservices .NET dans un service Kubernetes.
Ce navigateur n’est plus pris en charge.
Effectuez une mise à niveau vers Microsoft Edge pour tirer parti des dernières fonctionnalités, des mises à jour de sécurité et du support technique.
Dans cet article, vous découvrez les différents modèles de configuration et de génération d’un hôte générique .NET disponible dans le package NuGet Microsoft.Extensions.Hosting. L’hôte générique .NET est responsable de la gestion du démarrage et de la durée de vie de l’application. Les modèles de service Worker créent un hôte générique .NET, HostApplicationBuilder. L’hôte générique peut être utilisé avec d’autres types d’applications .NET, comme les applications de console.
Un hôte est un objet qui encapsule les ressources et les fonctionnalités de durée de vie d’une application, par exemple :
IHostedService
Lorsqu’un hôte démarre, il appelle IHostedService.StartAsync sur chaque implémentation de IHostedService inscrite dans la collection de services hébergés du conteneur de services. Dans une application de service Worker, toutes les implémentations IHostedService
qui contiennent des instances BackgroundService ont leurs méthodes BackgroundService.ExecuteAsync appelées.
La principale raison d’inclure toutes les ressources interdépendantes de l’application dans un objet est la gestion de la durée de vie : contrôler le démarrage de l’application et l’arrêt approprié.
L’hôte est généralement configuré, généré et exécuté par du code dans la classe Program
. La méthode Main
:
Les modèles de service Worker .NET génèrent le code suivant pour créer un hôte générique :
using Example.WorkerService;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
IHost host = builder.Build();
host.Run();
Pour plus d’informations sur les services Worker, consultez Services Worker dans .NET.
La méthode CreateApplicationBuilder :
DOTNET_
.Development
.Development
.HostApplicationBuilder.Services constitue une instance de Microsoft.Extensions.DependencyInjection.IServiceCollection. Ces services permettent de créer un IServiceProvider utilisé avec l’injection de dépendances pour résoudre les services inscrits.
Lorsque vous appelez IHostBuilder.Build() ou HostApplicationBuilder.Build(), les services suivants sont automatiquement inscrits :
Si vous générez pour le web ou écrivez une application distribuée, vous devrez peut-être utiliser un autre générateur d’hôte. Tenez compte de la liste suivante de générateurs d’hôte supplémentaires :
IWebHost
. Pour plus d’informations, consultez Hôte web ASP.NET Core.Injectez le service IHostApplicationLifetime dans n’importe quelle classe pour gérer les tâches post-démarrage et d’arrêt approprié. Trois propriétés de l’interface sont des jetons d’annulation utilisés pour inscrire les méthodes du gestionnaire d’événements de démarrage et d’arrêt d’application. L’interface inclut également une méthode StopApplication().
L’exemple suivant est un IHostedService et une implémentation IHostedLifecycleService qui inscrit les événements IHostApplicationLifetime
:
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace AppLifetime.Example;
public sealed class ExampleHostedService : IHostedService, IHostedLifecycleService
{
private readonly ILogger _logger;
public ExampleHostedService(
ILogger<ExampleHostedService> logger,
IHostApplicationLifetime appLifetime)
{
_logger = logger;
appLifetime.ApplicationStarted.Register(OnStarted);
appLifetime.ApplicationStopping.Register(OnStopping);
appLifetime.ApplicationStopped.Register(OnStopped);
}
Task IHostedLifecycleService.StartingAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("1. StartingAsync has been called.");
return Task.CompletedTask;
}
Task IHostedService.StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("2. StartAsync has been called.");
return Task.CompletedTask;
}
Task IHostedLifecycleService.StartedAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("3. StartedAsync has been called.");
return Task.CompletedTask;
}
private void OnStarted()
{
_logger.LogInformation("4. OnStarted has been called.");
}
private void OnStopping()
{
_logger.LogInformation("5. OnStopping has been called.");
}
Task IHostedLifecycleService.StoppingAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("6. StoppingAsync has been called.");
return Task.CompletedTask;
}
Task IHostedService.StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("7. StopAsync has been called.");
return Task.CompletedTask;
}
Task IHostedLifecycleService.StoppedAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("8. StoppedAsync has been called.");
return Task.CompletedTask;
}
private void OnStopped()
{
_logger.LogInformation("9. OnStopped has been called.");
}
}
Le modèle de service Worker peut être modifié pour ajouter l’implémentation ExampleHostedService
:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using AppLifetime.Example;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<ExampleHostedService>();
using IHost host = builder.Build();
await host.RunAsync();
L’application écrit l’exemple de sortie suivant :
// Sample output:
// info: AppLifetime.Example.ExampleHostedService[0]
// 1.StartingAsync has been called.
// info: AppLifetime.Example.ExampleHostedService[0]
// 2.StartAsync has been called.
// info: AppLifetime.Example.ExampleHostedService[0]
// 3.StartedAsync has been called.
// info: AppLifetime.Example.ExampleHostedService[0]
// 4.OnStarted has been called.
// info: Microsoft.Hosting.Lifetime[0]
// Application started. Press Ctrl+C to shut down.
// info: Microsoft.Hosting.Lifetime[0]
// Hosting environment: Production
// info: Microsoft.Hosting.Lifetime[0]
// Content root path: ..\app-lifetime\bin\Debug\net8.0
// info: AppLifetime.Example.ExampleHostedService[0]
// 5.OnStopping has been called.
// info: Microsoft.Hosting.Lifetime[0]
// Application is shutting down...
// info: AppLifetime.Example.ExampleHostedService[0]
// 6.StoppingAsync has been called.
// info: AppLifetime.Example.ExampleHostedService[0]
// 7.StopAsync has been called.
// info: AppLifetime.Example.ExampleHostedService[0]
// 8.StoppedAsync has been called.
// info: AppLifetime.Example.ExampleHostedService[0]
// 9.OnStopped has been called.
La sortie montre l’ordre de tous les divers événements de cycle de vie :
IHostedLifecycleService.StartingAsync
IHostedService.StartAsync
IHostedLifecycleService.StartedAsync
IHostApplicationLifetime.ApplicationStarted
Une fois l’application arrêtée, par exemple avec Ctrl+C, les événements suivants sont déclenchés :
IHostApplicationLifetime.ApplicationStopping
IHostedLifecycleService.StoppingAsync
IHostedService.StopAsync
IHostedLifecycleService.StoppedAsync
IHostApplicationLifetime.ApplicationStopped
L’implémentation de IHostLifetime contrôle quand l’hôte démarre et quand il s’arrête. La dernière implémentation inscrite est utilisée. Microsoft.Extensions.Hosting.Internal.ConsoleLifetime
est l’implémentation de IHostLifetime
par défaut. Pour plus d’informations sur les mécanismes de durée de vie de l’arrêt, consultez Arrêt de l’hôte.
L’interface IHostLifetime
expose une méthode IHostLifetime.WaitForStartAsync appelée au début de IHost.StartAsync
qui attend sa fin avant de continuer. Cela permet de retarder le démarrage jusqu'à ce que celui-ci soit signalé par un événement externe.
En outre, l’interface IHostLifetime
expose une méthode IHostLifetime.StopAsync appelée à partir de IHost.StopAsync
pour indiquer que l’hôte s’arrête et qu’il est temps de s’arrêter.
Injectez le service IHostEnvironment dans une classe pour obtenir des informations sur les paramètres suivants :
En outre, le service IHostEnvironment
ouvre la possibilité d’évaluer l’environnement à l’aide des méthodes d’extension suivantes :
La configuration de l’hôte est utilisée pour configurer les propriétés de l’implémentation IHostEnvironment.
La configuration de l’hôte est disponible dans la propriété HostApplicationBuilderSettings.Configuration et l’implémentation de l’environnement est disponible dans la propriété IHostApplicationBuilder.Environment. Pour configurer l’hôte, accédez à la propriété Configuration
et appelez l’une des méthodes d’extension disponibles.
Examinez l’exemple suivant pour ajouter la configuration d’hôte :
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
HostApplicationBuilderSettings settings = new()
{
Args = args,
Configuration = new ConfigurationManager(),
ContentRootPath = Directory.GetCurrentDirectory(),
};
settings.Configuration.AddJsonFile("hostsettings.json", optional: true);
settings.Configuration.AddEnvironmentVariables(prefix: "PREFIX_");
settings.Configuration.AddCommandLine(args);
HostApplicationBuilder builder = Host.CreateApplicationBuilder(settings);
using IHost host = builder.Build();
// Application code should start here.
await host.RunAsync();
Le code précédent :
PREFIX_
.La configuration d’application est créée en appelant ConfigureAppConfiguration sur un IHostApplicationBuilder. La propriété IHostApplicationBuilder.Configuration publique permet aux consommateurs de lire ou d’apporter des modifications à la configuration existante à l’aide de méthodes d’extension disponibles.
Pour plus d’informations, consultez Configuration dans .NET.
Il existe plusieurs façons d’arrêter un processus hôte. Le plus souvent, un processus hôte peut être arrêté de la manière suivante :
Main
.Le code d’hébergement n’est pas responsable de la gestion de ces scénarios. Le propriétaire du processus doit les traiter de la même façon que n’importe quelle autre application. Il existe plusieurs autres façons d’arrêter un processus de service hébergé :
ConsoleLifetime
est utilisé (UseConsoleLifetime), il écoute les signaux suivants et tente d’arrêter l’hôte correctement.
La logique d’hébergement intégrée gère ces scénarios, en particulier la classe ConsoleLifetime
. ConsoleLifetime
tente de manipuler les signaux d’arrêt SIGINT, SIGQUIT et SIGTERM pour permettre une sortie appropriée de l’application.
Avant .NET 6, il n’existait pas de moyen pour le code .NET de manipuler correctement SIGTERM. Pour contourner cette limitation, ConsoleLifetime
devait s’abonner à System.AppDomain.ProcessExit. Quand ProcessExit
était déclenché, ConsoleLifetime
indiquait à l’hôte d’arrêter et de bloquer le thread ProcessExit
, en attendant que l’hôte s’arrête.
Le gestionnaire de sortie du processus permet d’exécuter le code de nettoyage dans l’application, IHost.StopAsync et le code après HostingAbstractionsHostExtensions.Run dans la méthode Main
.
Toutefois, cette approche posait d’autres problèmes, car SIGTERM n’était pas le seul moyen de déclencher ProcessExit
. SIGTERM est également déclenché lorsque le code de l’application appelle Environment.Exit
. Environment.Exit
n’est pas un moyen approprié d’arrêter un processus dans le modèle d’application Microsoft.Extensions.Hosting
. Il déclenche l’événement ProcessExit
, puis quitte le processus. La fin de la méthode Main
n’est pas exécutée. Les threads d’arrière-plan et de premier plan sont terminés et les blocs finally
ne sont pas exécutés.
Étant donné que ConsoleLifetime
bloquait ProcessExit
en attendant que l’hôte s’arrête, ce comportement conduisait à des interblocages à partir du moment où Environment.Exit
se bloquait également en attendant l’appel à ProcessExit
. En outre, étant donné que la gestion SIGTERM tentait d’arrêter correctement le processus, ConsoleLifetime
définissait le ExitCode sur 0
, ce qui écrasait le code de sortie de l’utilisateur passé à Environment.Exit
.
Dans .NET 6, les signaux POSIX sont pris en charge et manipulés. Le ConsoleLifetime
gère correctement SIGTERM, et ne s’implique plus lorsque Environment.Exit
est invoqué.
Conseil
Pour .NET 6+, ConsoleLifetime
n’a plus de logique pour gérer le scénario Environment.Exit
. Les applications qui appellent Environment.Exit
et doivent effectuer une logique de nettoyage peuvent s’abonner à ProcessExit
elles-mêmes. L’hébergement ne tentera plus d’arrêter correctement l’hôte dans ces scénarios.
Si votre application utilise l’hébergement et que vous souhaitez arrêter correctement l’hôte, vous pouvez appeler IHostApplicationLifetime.StopApplication au lieu de Environment.Exit
.
Le diagramme de séquence suivant montre comment les signaux sont manipulés en interne dans le code d’hébergement. La plupart des utilisateurs n’ont pas besoin de comprendre ce processus. Mais pour les développeurs qui ont besoin d’une compréhension approfondie, un bon visuel peut vous aider à démarrer.
Une fois l’hôte démarré, lorsqu’un utilisateur appelle Run
ou WaitForShutdown
, un gestionnaire est inscrit pour IApplicationLifetime.ApplicationStopping. L’exécution est suspendue dans WaitForShutdown
, en attendant que l’événement ApplicationStopping
soit déclenché. La méthode Main
ne revient pas immédiatement et l’application continue à s’exécuter jusqu’à ce que Run
ou WaitForShutdown
revienne.
Lorsqu’un signal est envoyé au processus, il lance la séquence suivante :
ConsoleLifetime
à ApplicationLifetime
pour déclencher l’événement ApplicationStopping
. Cela signale à WaitForShutdownAsync
de débloquer le code d’exécution Main
. En attendant, le gestionnaire de signal POSIX revient avec Cancel = true
puisque ce signal POSIX a été manipulé.Main
recommence à s’exécuter et indique à l’hôte de StopAsync()
, qui à son tour arrête tous les services hébergés et déclenche tous les autres événements arrêtés.WaitForShutdown
se ferme, ce qui permet à n’importe quel code de nettoyage d’application de s’exécuter et à la méthode Main
de se fermer correctement.Il existe d’autres scénarios courants dans lesquels l’arrêt normal fonctionne dans Kestrel pour les protocoles HTTP/1.1 et HTTP/2, et comment vous pouvez le configurer dans différents environnements avec un équilibreur de charge pour drainer le trafic en douceur. Bien que la configuration du serveur web dépasse le cadre de cet article, vous trouverez plus d’informations sur Options de configuration de la documentation du serveur web ASP.NET Core Kestrel.
Lorsque l’hôte reçoit un signal d’arrêt (par exemple, Ctrl+C ou StopAsync
), il avertit l’application en signalant ApplicationStopping. Vous devriez vous abonner à cet événement si vous avez des opérations de longue durée qui doivent se terminer correctement.
Ensuite, l’hôte appelle IServer.StopAsync avec un délai d’arrêt que vous pouvez configurer (30s par défaut). Kestrel (et Http.Sys) ferment leurs liaisons de port et cessent d’accepter de nouvelles connexions. Ils indiquent également aux connexions actuelles d’arrêter de traiter les nouvelles requêtes. Pour HTTP/2 et HTTP/3, un message préliminaire GOAWAY
est envoyé au client. Pour HTTP/1.1, ils arrêtent la boucle de connexion, car les requêtes sont traitées dans l’ordre. IIS se comporte différemment en rejetant les nouvelles requêtes avec un code d’état 503.
Les requêtes actives ont jusqu’à l’expiration du délai d’arrêt pour se terminer. Si elles sont toutes terminées avant l’expiration du délai, le serveur renvoie le contrôle à l’hôte plus tôt. Si le délai expire, les connexions et requêtes en attente sont interrompues de force, ce qui peut entraîner des erreurs dans les journaux et pour les clients.
Pour garantir une transition fluide des clients vers une nouvelle destination lors de l’utilisation d’un équilibreur de charge, vous pouvez suivre les étapes suivantes :
Commentaires sur .NET
.NET est un projet open source. Sélectionnez un lien pour fournir des commentaires :
Entrainement
Module
Implémenter la résilience au sein d’un microservice natif Cloud - Training
Ce module vous guide tout au long de l’implémentation de la résilience d’une application de microservices .NET dans un service Kubernetes.