.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
:
- Roept een CreateApplicationBuilder methode aan om een opbouwobject te maken en te configureren.
- Aanroepen Build() om een IHost exemplaar te maken.
- Roept Run of RunAsync methode op het hostobject aan.
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.
- Omgevingsvariabelen voorafgegaan door
- 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:
Aanvullende op scenario's gebaseerde hostbouwers
Als u bouwt voor het web of een gedistribueerde toepassing schrijft, moet u mogelijk een andere hostbouwer gebruiken. Bekijk de volgende lijst met extra hostbouwers:
- DistributedApplicationBuilder: Een opbouwfunctie voor het maken van gedistribueerde apps. Zie .NET Aspire voor meer informatie.
- WebApplicationBuilder: Een opbouwfunctie voor webtoepassingen en -services. Zie ASP.NET Core voor meer informatie.
- WebHostBuilder: Een opbouwfunctie voor
IWebHost
. Zie ASP.NET Core-webhost voor meer informatie.
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:
IHostedLifecycleService.StartingAsync
IHostedService.StartAsync
IHostedLifecycleService.StartedAsync
IHostApplicationLifetime.ApplicationStarted
Wanneer de toepassing is gestopt, bijvoorbeeld met Ctrl+C, worden de volgende gebeurtenissen gegenereerd:
IHostApplicationLifetime.ApplicationStopping
IHostedLifecycleService.StoppingAsync
IHostedService.StopAsync
IHostedLifecycleService.StoppedAsync
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:
- IHostEnvironment.ApplicationName
- IHostEnvironment.ContentRootFileProvider
- IHostEnvironment.ContentRootPath
- IHostEnvironment.EnvironmentName
Daarnaast biedt de IHostEnvironment
service de mogelijkheid om de omgeving te evalueren met behulp van deze uitbreidingsmethoden:
- HostingEnvironmentExtensions.IsDevelopment
- HostingEnvironmentExtensions.IsEnvironment
- HostingEnvironmentExtensions.IsProduction
- HostingEnvironmentExtensions.IsStaging
Hostconfiguratie
Hostconfiguratie wordt gebruikt om eigenschappen van de IHostEnvironment-implementatie te configureren.
De hostconfiguratie is beschikbaar in HostApplicationBuilderSettings.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;
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();
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:
- Als iemand niet belt Run of HostingAbstractionsHostExtensions.WaitForShutdown als de app normaal wordt afgesloten met
Main
het voltooien. - Als de app vastloopt.
- Als de app geforceerd wordt afgesloten met SIGKILL (of CTRL+Z).
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. - 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 0
op , 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 WaitForShutdown
een handler voor IApplicationLifetime.ApplicationStopping. De uitvoering is onderbroken en WaitForShutdown
wacht 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:
- Het besturingselement stroomt van
ConsoleLifetime
waaruitApplicationLifetime
deApplicationStopping
gebeurtenis moet worden gegenereerd. Dit geeft aanWaitForShutdownAsync
dat deMain
uitvoeringscode wordt gedeblokkeerd. In de tussentijd retourneert de POSIX-signaalhandler metCancel = true
sinds het POSIX-signaal is verwerkt. - De
Main
uitvoeringscode wordt opnieuw uitgevoerd en vertelt de host aanStopAsync()
, waardoor alle gehoste services op zijn beurt worden gestopt en andere gestopte gebeurtenissen worden gegenereerd. - Ten slotte
WaitForShutdown
sluit u af, zodat elke toepassing code kan opschonen en deMain
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
- Afhankelijkheidsinjectie in .NET
- Logboekregistratie in .NET
- Configuratie in .NET
- Worker Services in .NET
- ASP.NET Core-webhost
- ASP.NET Core Kestrel-webserverconfiguratie
- Algemene hostfouten moeten worden gemaakt in de github.com/dotnet/runtime opslagplaats