.NET Generic Host

In dit artikel leert u meer over de verschillende patronen voor het configureren en bouwen van een .NET Generic Host die beschikbaar is in het NuGet-pakket Microsoft.Extensions.Hosting . De .NET Generic Host is verantwoordelijk voor het opstarten en levensduurbeheer van apps. De werkrolservicesjablonen maken een .NET Generic Host, HostApplicationBuilder. De algemene host kan worden gebruikt met andere typen .NET-toepassingen, zoals Console-apps.

Een host is een object dat de resources en levensduur van een app inkapselt, zoals:

  • Afhankelijkheidsinjectie (DI)
  • Logboekregistratie
  • Configuratie
  • App afsluiten
  • IHostedService Implementaties

Wanneer een host wordt gestart, roept IHostedService.StartAsync deze elke implementatie aan van de registratie in IHostedService de verzameling gehoste services van de servicecontainer. In een werkservice-app hebben alle IHostedService implementaties die exemplaren bevatten BackgroundService hun BackgroundService.ExecuteAsync methoden aangeroepen.

De belangrijkste reden voor het opnemen van alle onderling afhankelijke resources van de app in één object is levensduurbeheer: controle over het opstarten van apps en het correct afsluiten van apps.

Een host instellen

De host wordt doorgaans geconfigureerd, gebouwd en uitgevoerd op code in de Program klasse. De methode Main:

De .NET Worker Service-sjablonen genereren de volgende code om een algemene host te maken:

using Example.WorkerService;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();

IHost host = builder.Build();
host.Run();

Zie Worker Services in .NET voor meer informatie over Worker Services.

Instellingen voor hostbouwer

De methode CreateApplicationBuilder:

  • Hiermee stelt u de hoofdmap van de inhoud in op het pad dat wordt geretourneerd door GetCurrentDirectory().
  • Hostconfiguratie wordt geladen van:
    • Omgevingsvariabelen voorafgegaan door DOTNET_.
    • Opdrachtregelargumenten.
  • De app-configuratie wordt geladen van:
    • appsettings.json.
    • appsettings. {Environment}.json.
    • Secret Manager wanneer de app wordt uitgevoerd in de Development omgeving.
    • Omgevingsvariabelen.
    • Opdrachtregelargumenten.
  • Voegt de volgende logboekregistratieproviders toe:
    • Console
    • Fouten opsporen
    • EventSource
    • EventLog (alleen bij uitvoering in Windows)
  • Hiermee schakelt u bereikvalidatie en afhankelijkheidsvalidatie in wanneer de omgeving zich bevindt Development.

Het HostApplicationBuilder.Services is een Microsoft.Extensions.DependencyInjection.IServiceCollection instantie. Deze services worden gebruikt om een IServiceProvider service te bouwen die wordt gebruikt met afhankelijkheidsinjectie om de geregistreerde services op te lossen.

Door framework geleverde services

Wanneer u een IHostBuilder.Build() van de volgende services aanroept of HostApplicationBuilder.Build(), worden de volgende services automatisch geregistreerd:

IHostApplicationLifetime

Injecteer de IHostApplicationLifetime service in elke klasse om taken na het opstarten en zonder problemen af te sluiten. Drie eigenschappen op de interface zijn annuleringstokens die worden gebruikt om methoden voor het starten en stoppen van apps te registreren. De interface bevat ook een StopApplication() methode.

Het volgende voorbeeld is een IHostedService en IHostedLifecycleService implementatie waarmee gebeurtenissen worden geregistreerd 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.");
    }
}

De sjabloon Worker Service kan worden gewijzigd om de ExampleHostedService implementatie toe te voegen:

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

De toepassing schrijft de volgende voorbeelduitvoer:

// 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.

In de uitvoer ziet u de volgorde van alle verschillende levenscyclus-gebeurtenissen:

  1. IHostedLifecycleService.StartingAsync
  2. IHostedService.StartAsync
  3. IHostedLifecycleService.StartedAsync
  4. IHostApplicationLifetime.ApplicationStarted

Wanneer de toepassing is gestopt, bijvoorbeeld met Ctrl+C, worden de volgende gebeurtenissen gegenereerd:

  1. IHostApplicationLifetime.ApplicationStopping
  2. IHostedLifecycleService.StoppingAsync
  3. IHostedService.StopAsync
  4. IHostedLifecycleService.StoppedAsync
  5. IHostApplicationLifetime.ApplicationStopped

IHostLifetime

De IHostLifetime implementatie bepaalt wanneer de host wordt gestart en wanneer deze stopt. De laatste geregistreerde implementatie wordt gebruikt. Microsoft.Extensions.Hosting.Internal.ConsoleLifetime is de standaard IHostLifetime implementatie. Zie Voor meer informatie over de levensduur van afsluiten, host afsluiten.

De IHostLifetime interface maakt een IHostLifetime.WaitForStartAsync methode beschikbaar, die aan het begin IHost.StartAsync wordt aangeroepen, waarna wordt gewacht totdat deze is voltooid voordat u doorgaat. Dit kan worden gebruikt om het opstarten uit te stellen totdat een externe gebeurtenis wordt gesignaleerd.

Daarnaast maakt de IHostLifetime interface een IHostLifetime.StopAsync methode beschikbaar, die wordt aangeroepen IHost.StopAsync om aan te geven dat de host stopt en het tijd is om af te sluiten.

IHostEnvironment

Injecteer de IHostEnvironment service in een klasse om informatie over de volgende instellingen op te halen:

Daarnaast biedt de IHostEnvironment service de mogelijkheid om de omgeving te evalueren met behulp van deze uitbreidingsmethoden:

Hostconfiguratie

Hostconfiguratie wordt gebruikt om eigenschappen van de IHostEnvironment-implementatie te configureren.

De hostconfiguratie is beschikbaar in IHostApplicationBuilder.Configuration eigenschap en de implementatie van de omgeving is beschikbaar in IHostApplicationBuilder.Environment eigenschap. Als u de host wilt configureren, opent u de Configuration eigenschap en roept u een van de beschikbare extensiemethoden aan.

Bekijk het volgende voorbeeld om hostconfiguratie toe te voegen:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Environment.ContentRootPath = Directory.GetCurrentDirectory();
builder.Configuration.AddJsonFile("hostsettings.json", optional: true);
builder.Configuration.AddEnvironmentVariables(prefix: "PREFIX_");
builder.Configuration.AddCommandLine(args);

using IHost host = builder.Build();

// Application code should start here.

await host.RunAsync();

Met de voorgaande code wordt:

  • Hiermee stelt u de hoofdmap van de inhoud in op het pad dat wordt geretourneerd door GetCurrentDirectory().
  • Hostconfiguratie wordt geladen van:
    • hostsettings.json.
    • Omgevingsvariabelen voorafgegaan door PREFIX_.
    • Opdrachtregelargumenten.

App-configuratie

App-configuratie wordt gemaakt door een aanroep uit te schakelen ConfigureAppConfiguration op een IHostApplicationBuilder. Met de openbareIHostApplicationBuilder.Configuration eigenschap kunnen gebruikers de bestaande configuratie lezen of wijzigen met behulp van beschikbare uitbreidingsmethoden.

Zie Configuratie in .NET voor meer informatie.

Afsluiten van host

Er zijn verschillende manieren waarop een gehost proces wordt gestopt. Meestal kan een gehost proces op de volgende manieren worden gestopt:

De hostingcode is niet verantwoordelijk voor het afhandelen van deze scenario's. De eigenaar van het proces moet ermee omgaan als elke andere app. Er zijn verschillende andere manieren waarop een gehost serviceproces kan worden gestopt:

  • Als ConsoleLifetime deze wordt gebruikt (UseConsoleLifetime), luistert deze naar de volgende signalen en probeert de host correct te stoppen.
    • SIGINT (of CTRL+C).
    • SIGQUIT (of CTRL+BREAK in Windows, CTRL+\ op Unix).
    • SIGTERM (verzonden door andere apps, zoals docker stop).
  • Als de app aanroept Environment.Exit.

De ingebouwde hostinglogica verwerkt deze scenario's, met name de ConsoleLifetime klasse. ConsoleLifetime probeert de signalen 'afsluiten' te verwerken SIGINT, SIGQUIT en SIGTERM om een probleemloze uitgang naar de toepassing mogelijk te maken.

Voor .NET 6 was er geen manier voor .NET-code om SIGTERM correct te verwerken. Als u deze beperking wilt omzeilen, ConsoleLifetime moet u zich abonneren op System.AppDomain.ProcessExit. Wanneer ProcessExit de host is geactiveerd, ConsoleLifetime zou de host worden gesignaleerde om de ProcessExit thread te stoppen en te blokkeren, wachtend totdat de host stopt.

Met de afsluithandler voor processen kan de opschooncode in de toepassing worden uitgevoerd, IHost.StopAsync bijvoorbeeld code na HostingAbstractionsHostExtensions.Run in de Main methode.

Er waren echter andere problemen met deze benadering, omdat SIGTERM niet de enige manier ProcessExit was om op te lossen. SIGTERM wordt ook gegenereerd wanneer app-code aanroept Environment.Exit. Environment.Exit is geen sierlijke manier om een proces in het Microsoft.Extensions.Hosting app-model af te sluiten. Hiermee wordt de ProcessExit gebeurtenis gegenereerd en wordt het proces afgesloten. Het einde van de Main methode wordt niet uitgevoerd. Achtergrond- en voorgrondthreads worden beëindigd en finally blokken worden niet uitgevoerd.

Omdat ConsoleLifetime geblokkeerd ProcessExit terwijl wordt gewacht totdat de host wordt afgesloten, heeft dit gedrag ertoe geleid dat impasses van Environment.Exit ook blokken wachten op de oproep naar ProcessExit. Aangezien de SIGTERM-verwerking het proces correct probeerde af te sluiten, ConsoleLifetime stelt u bovendien het ExitCode in 0op , waardoor de afsluitcode van de gebruiker is doorgegeven aan Environment.Exit.

In .NET 6 worden POSIX-signalen ondersteund en verwerkt. De ConsoleLifetime handles SIGTERM correct en worden niet langer betrokken wanneer Environment.Exit wordt aangeroepen.

Tip

Voor .NET 6+ ConsoleLifetime beschikt u niet meer over logica voor het afhandelen van scenario's Environment.Exit. Apps die aanroepen Environment.Exit en opschoningslogica moeten uitvoeren, kunnen zich abonneren ProcessExit op zichzelf. Hosting probeert de host in deze scenario's niet meer op een goede manier te stoppen.

Als uw toepassing gebruikmaakt van hosting en u de host probleemloos wilt stoppen, kunt IHostApplicationLifetime.StopApplication u bellen in plaats van Environment.Exit.

Proces voor afsluiten hosten

In het volgende sequentiediagram ziet u hoe de signalen intern worden verwerkt in de hostingcode. De meeste gebruikers hoeven dit proces niet te begrijpen. Maar voor ontwikkelaars die een grondige kennis nodig hebben, kan een goede visual u helpen om aan de slag te gaan.

Nadat de host is gestart, wordt een handler geregistreerd wanneer een gebruiker belt Run of WaitForShutdowneen handler voor IApplicationLifetime.ApplicationStopping. De uitvoering is onderbroken en WaitForShutdownwacht tot de ApplicationStopping gebeurtenis is gegenereerd. De Main methode retourneert niet meteen en de app blijft actief totdat Run of WaitForShutdown wordt geretourneerd.

Wanneer een signaal naar het proces wordt verzonden, wordt de volgende volgorde gestart:

Volgdiagram voor afsluiten hosten.

  1. Het besturingselement stroomt van ConsoleLifetime waaruit ApplicationLifetime de ApplicationStopping gebeurtenis moet worden gegenereerd. Dit geeft aan WaitForShutdownAsync dat de Main uitvoeringscode wordt gedeblokkeerd. In de tussentijd retourneert de POSIX-signaalhandler met Cancel = true sinds het POSIX-signaal is verwerkt.
  2. De Main uitvoeringscode wordt opnieuw uitgevoerd en vertelt de host aan StopAsync(), waardoor alle gehoste services op zijn beurt worden gestopt en andere gestopte gebeurtenissen worden gegenereerd.
  3. Ten slotte WaitForShutdown sluit u af, zodat elke toepassing code kan opschonen en de Main methode correct kan worden afgesloten.

Afsluiten van host in webserverscenario's

Er zijn verschillende andere veelvoorkomende scenario's waarin het probleemloos afsluiten werkt in Kestrel voor zowel HTTP/1.1- als HTTP/2-protocollen en hoe u deze in verschillende omgevingen kunt configureren met een load balancer om het verkeer soepel te laten verlopen. Hoewel de webserverconfiguratie buiten het bereik van dit artikel valt, vindt u meer informatie over opties configureren voor de documentatie van de ASP.NET Core Kestrel-webserver .

Wanneer de host een afsluitsignaal ontvangt (bijvoorbeeld CTL+C ofStopAsync), wordt de toepassing op de hoogte gehouden door te signaleren.ApplicationStopping U moet zich abonneren op deze gebeurtenis als u langlopende bewerkingen hebt die correct moeten worden voltooid.

Vervolgens roept de host IServer.StopAsync aan met een time-out voor afsluiten die u kunt configureren (standaard 30s). Kestrel (en Http.Sys) sluiten hun poortbindingen en stoppen met het accepteren van nieuwe verbindingen. Ze geven ook aan dat de huidige verbindingen stoppen met het verwerken van nieuwe aanvragen. Voor HTTP/2 en HTTP/3 wordt een voorlopig GOAWAY bericht verzonden naar de client. Voor HTTP/1.1 stoppen ze de verbindingslus omdat aanvragen in volgorde worden verwerkt. IIS gedraagt zich anders door nieuwe aanvragen met een 503-statuscode te weigeren.

De actieve aanvragen hebben totdat de time-out voor afsluiten is voltooid. Als ze allemaal zijn voltooid vóór de time-out, retourneert de server de besturing eerder naar de host. Als de time-out verloopt, worden de in behandeling zijnde verbindingen en aanvragen geforceerd afgebroken, wat fouten in de logboeken en de clients kan veroorzaken.

Overwegingen voor load balancer

Volg deze stappen om een soepele overgang van clients naar een nieuwe bestemming te garanderen wanneer u met een load balancer werkt:

  • Breng het nieuwe exemplaar naar boven en begin met het verdelen van verkeer naar het exemplaar (mogelijk hebt u al verschillende exemplaren voor schaalaanpassingsdoeleinden).
  • Schakel het oude exemplaar in de load balancer-configuratie uit of verwijder deze zodat er geen nieuw verkeer meer wordt ontvangen.
  • Signaler het oude exemplaar dat moet worden afgesloten.
  • Wacht totdat het leeg is of er een time-out optreedt.

Zie ook