Allmän .NET-värd
I den här artikeln får du lära dig mer om de olika mönstren för att konfigurera och skapa en .NET Generic Host som är tillgänglig i NuGet-paketet Microsoft.Extensions.Hosting . .NET Generic Host ansvarar för appstart och livslängdshantering. Arbetstjänstmallarna skapar en allmän .NET-värd, HostApplicationBuilder. Den allmänna värden kan användas med andra typer av .NET-program, till exempel konsolappar.
En värd är ett objekt som kapslar in en apps resurser och livslängdsfunktioner, till exempel:
- Beroendeinmatning (DI)
- Loggning
- Konfiguration
- Appavstängning
IHostedService
Implementeringar
När en värd startar anropas IHostedService.StartAsync varje implementering av IHostedService registrerad i tjänstcontainerns samling värdbaserade tjänster. I en arbetstjänstapp anropas BackgroundService.ExecuteAsync alla IHostedService
implementeringar som innehåller BackgroundService instanser.
Den främsta orsaken till att inkludera alla appens beroende resurser i ett objekt är livslängdshantering: kontroll över appstart och graciös avstängning.
Konfigurera en värd
Värden är vanligtvis konfigurerad, byggd och kör med kod i Program
klassen. Metoden Main
:
- Anropar en CreateApplicationBuilder metod för att skapa och konfigurera ett builder-objekt.
- Anrop Build() för att skapa en IHost instans.
- Anrop Run eller RunAsync metod för värdobjektet.
Mallarna för .NET Worker Service genererar följande kod för att skapa en allmän värd:
using Example.WorkerService;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
IHost host = builder.Build();
host.Run();
Mer information om Worker Services finns i Worker Services i .NET.
Inställningar för värdbyggare
Metoden CreateApplicationBuilder :
- Anger innehållsroten till sökvägen som returneras av GetCurrentDirectory().
- Läser in värdkonfiguration från :
- Miljövariabler med prefixet
DOTNET_
. - Kommandoradsargument.
- Miljövariabler med prefixet
- Läser in appkonfiguration från:
- appsettings.json.
- appsettings. {Environment}.json.
- Secret Manager när appen körs i
Development
miljön. - Miljövariabler.
- Kommandoradsargument.
- Lägger till följande loggningsproviders:
- Konsol
- Felsöka
- EventSource
- EventLog (endast när du kör på Windows)
- Aktiverar omfångsverifiering och beroendeverifiering när miljön är
Development
.
HostApplicationBuilder.Services Är en Microsoft.Extensions.DependencyInjection.IServiceCollection instans. Dessa tjänster används för att skapa en IServiceProvider som används med beroendeinmatning för att lösa de registrerade tjänsterna.
Ramverksbaserade tjänster
När du anropar antingen IHostBuilder.Build() eller HostApplicationBuilder.Build()registreras följande tjänster automatiskt:
Ytterligare scenariobaserade värdbyggare
Om du skapar för webben eller skriver ett distribuerat program kan du behöva använda en annan värdbyggare. Överväg följande lista över ytterligare värdbyggare:
- DistributedApplicationBuilder: En byggare för att skapa distribuerade appar. Mer information finns i .NET Aspire.
- WebApplicationBuilder: En byggare för webbprogram och tjänster. Mer information finns i ASP.NET Core.
- WebHostBuilder: En byggare för
IWebHost
. Mer information finns i ASP.NET Core-webbvärd.
IHostApplicationLifetime
IHostApplicationLifetime Mata in tjänsten i valfri klass för att hantera uppgifter efter start och graciös avstängning. Tre egenskaper i gränssnittet är annulleringstoken som används för att registrera metoder för att starta och stoppa apphändelser. Gränssnittet innehåller också en StopApplication() metod.
Följande exempel är en IHostedService implementering som IHostedLifecycleService registrerar IHostApplicationLifetime
händelser:
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.");
}
}
Arbetstjänstmallen kan ändras för att lägga till implementeringen 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();
Programmet skulle skriva följande exempelutdata:
// 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.
Utdata visar ordningen på alla olika livscykelhändelser:
IHostedLifecycleService.StartingAsync
IHostedService.StartAsync
IHostedLifecycleService.StartedAsync
IHostApplicationLifetime.ApplicationStarted
När programmet stoppas, till exempel med Ctrl+C, utlöses följande händelser:
IHostApplicationLifetime.ApplicationStopping
IHostedLifecycleService.StoppingAsync
IHostedService.StopAsync
IHostedLifecycleService.StoppedAsync
IHostApplicationLifetime.ApplicationStopped
IHostLifetime
Implementeringen IHostLifetime styr när värden startar och när den stoppas. Den senast registrerade implementeringen används. Microsoft.Extensions.Hosting.Internal.ConsoleLifetime
är standardimplementeringen IHostLifetime
. Mer information om livslängdsmekaniken för avstängning finns i Avstängning av värd.
Gränssnittet IHostLifetime
exponerar en IHostLifetime.WaitForStartAsync metod som anropas i början av IHost.StartAsync
vilken väntar tills den är klar innan den fortsätter. Detta kan användas för att fördröja starten tills den signaleras av en extern händelse.
Dessutom IHostLifetime
exponerar gränssnittet en IHostLifetime.StopAsync metod som anropas från IHost.StopAsync
för att indikera att värden stoppas och att det är dags att stänga av.
IHostEnvironment
Mata in tjänsten IHostEnvironment i en klass för att få information om följande inställningar:
- IHostEnvironment.ApplicationName
- IHostEnvironment.ContentRootFileProvider
- IHostEnvironment.ContentRootPath
- IHostEnvironment.EnvironmentName
Dessutom IHostEnvironment
visar tjänsten möjligheten att utvärdera miljön med hjälp av dessa tilläggsmetoder:
- HostingEnvironmentExtensions.IsDevelopment
- HostingEnvironmentExtensions.IsEnvironment
- HostingEnvironmentExtensions.IsProduction
- HostingEnvironmentExtensions.IsStaging
Värdkonfiguration
Värdkonfiguration används för att konfigurera egenskaperna för IHostEnvironment-implementeringen .
Värdkonfigurationen är tillgänglig i HostApplicationBuilderSettings.Configuration egenskapen och miljöimplementeringen är tillgänglig i IHostApplicationBuilder.Environment egenskapen. Om du vill konfigurera värden öppnar du Configuration
egenskapen och anropar någon av de tillgängliga tilläggsmetoderna.
Tänk på följande exempel för att lägga till värdkonfiguration:
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();
Koden ovan:
- Anger innehållsroten till sökvägen som returneras av GetCurrentDirectory().
- Läser in värdkonfiguration från:
- hostsettings.json.
- Miljövariabler med prefixet
PREFIX_
. - Kommandoradsargument.
Appkonfiguration
Appkonfigurationen skapas genom att anropa ConfigureAppConfiguration på en IHostApplicationBuilder. Med den offentligaIHostApplicationBuilder.Configuration egenskapen kan användarna läsa från eller göra ändringar i den befintliga konfigurationen med hjälp av tillgängliga tilläggsmetoder.
Mer information finns i Konfiguration i .NET.
Avstängning av värd
Det finns flera sätt på vilka en värdbaserad process stoppas. Oftast kan en värdbaserad process stoppas på följande sätt:
- Om någon inte anropar Run eller HostingAbstractionsHostExtensions.WaitForShutdown och appen avslutas normalt med
Main
slutförande. - Om appen kraschar.
- Om appen stängs av med hjälp av SIGKILL (eller CTRL+Z).
Värdkoden ansvarar inte för att hantera dessa scenarier. Processens ägare måste hantera dem på samma sätt som andra appar. Det finns flera andra sätt på vilka en värdbaserad tjänstprocess kan stoppas:
- Om
ConsoleLifetime
används (UseConsoleLifetime) lyssnar den efter följande signaler och försöker stoppa värden på ett smidigt sätt. - Om appen anropar Environment.Exit.
Den inbyggda värdlogiken hanterar dessa scenarier, särskilt ConsoleLifetime
klassen. ConsoleLifetime
försöker hantera "avstängningssignalerna" SIGINT, SIGQUIT och SIGTERM för att möjliggöra en korrekt avslutning av programmet.
Före .NET 6 fanns det inget sätt för .NET-kod att hantera SIGTERM korrekt. Om du vill kringgå den här begränsningen ConsoleLifetime
prenumererar du på System.AppDomain.ProcessExit. När ProcessExit
aktiverades ConsoleLifetime
signalerades värden att stoppa och blockera tråden ProcessExit
i väntan på att värden skulle stoppas.
Processavslutshanteraren skulle göra det möjligt för rensningskoden i programmet att köras, IHost.StopAsync till exempel och koda efter HostingAbstractionsHostExtensions.Run i Main
-metoden.
Det fanns dock andra problem med den här metoden eftersom SIGTERM inte var det enda sättet ProcessExit
som togs upp. SIGTERM aktiveras också när appkod anropar Environment.Exit
. Environment.Exit
är inte ett smidigt sätt att stänga av en process i Microsoft.Extensions.Hosting
appmodellen. Händelsen genereras ProcessExit
och processen avslutas. Slutet av Main
metoden körs inte. Bakgrunds- och förgrundstrådar avslutas och finally
block körs inte .
Eftersom ConsoleLifetime
det blockerades ProcessExit
i väntan på att värden skulle stängas av ledde det här beteendet till dödlägen från Environment.Exit
block som väntade på anropet till ProcessExit
. Eftersom SIGTERM-hanteringen försökte stänga processen på ett korrekt sätt skulle ConsoleLifetime
den dessutom ange ExitCode till 0
, som klonade användarens slutkod som skickades till Environment.Exit
.
I .NET 6 stöds och hanteras POSIX-signaler . Hanterar ConsoleLifetime
SIGTERM på ett korrekt sätt och engageras inte längre när Environment.Exit
anropas.
Dricks
För .NET 6+ ConsoleLifetime
har inte längre logik för att hantera scenariot Environment.Exit
. Appar som anropar Environment.Exit
och behöver utföra rensningslogik kan prenumerera ProcessExit
på sig själva. Värdtjänster försöker inte längre att på ett smidigt sätt stoppa värden i dessa scenarier.
Om ditt program använder värd och du vill stoppa värden på ett smidigt sätt kan du anropa IHostApplicationLifetime.StopApplication i stället Environment.Exit
för .
Värdavstängningsprocess
Följande sekvensdiagram visar hur signalerna hanteras internt i värdkoden. De flesta användare behöver inte förstå den här processen. Men för utvecklare som behöver en djup förståelse kan ett bra visuellt objekt hjälpa dig att komma igång.
När värden har startats, när en användare anropar Run
eller WaitForShutdown
, registreras en hanterare för IApplicationLifetime.ApplicationStopping. Körningen pausas i WaitForShutdown
och väntar ApplicationStopping
på att händelsen ska aktiveras. Metoden Main
returnerar inte direkt och appen fortsätter att köras tills Run
eller WaitForShutdown
returnerar.
När en signal skickas till processen initieras följande sekvens:
- Kontrollen flödar från
ConsoleLifetime
till förApplicationLifetime
att skapaApplicationStopping
händelsen. Detta signalerarWaitForShutdownAsync
att avblockera körningskodenMain
. Under tiden returnerar POSIX-signalhanteraren medCancel = true
eftersom POSIX-signalen har hanterats. - Körningskoden
Main
börjar köras igen och instruerar värden tillStopAsync()
, vilket i sin tur stoppar alla värdbaserade tjänster och genererar andra stoppade händelser. WaitForShutdown
Slutligen avslutas, vilket gör att all programrensningskod kan köras ochMain
att metoden avslutas korrekt.
Värdavstängning i webbserverscenarier
Det finns olika andra vanliga scenarier där graciös avstängning fungerar i Kestrel för både HTTP/1.1- och HTTP/2-protokoll, och hur du kan konfigurera den i olika miljöer med en lastbalanserare för att tömma trafiken smidigt. Även om webbserverkonfigurationen ligger utanför omfånget för den här artikeln kan du hitta mer information om konfigurera alternativ för ASP.NET Core Kestrel-webbserverdokumentation .
När värden tar emot en avstängningssignal (till exempel CTL+C eller StopAsync
), meddelar den programmet genom att signalera .ApplicationStopping Du bör prenumerera på den här händelsen om du har några långvariga åtgärder som måste slutföras korrekt.
Därefter anropar IServer.StopAsync värden med en tidsgräns för avstängning som du kan konfigurera (standard 30-talet). Kestrel (och Http.Sys) stänger sina portbindningar och slutar acceptera nya anslutningar. De uppmanar också de aktuella anslutningarna att sluta bearbeta nya begäranden. För HTTP/2 och HTTP/3 skickas ett preliminärt GOAWAY
meddelande till klienten. För HTTP/1.1 stoppar de anslutningsloopen eftersom begäranden bearbetas i ordning. IIS fungerar annorlunda genom att avvisa nya begäranden med statuskoden 503.
De aktiva begärandena har tills tidsgränsen för avstängningen har slutförts. Om alla är klara före tidsgränsen returnerar servern kontrollen till värden tidigare. Om tidsgränsen upphör att gälla avbryts väntande anslutningar och begäranden med kraft, vilket kan orsaka fel i loggarna och till klienterna.
Överväganden för lastbalanserare
För att säkerställa en smidig övergång av klienter till ett nytt mål när du arbetar med en lastbalanserare kan du följa dessa steg:
- Ta upp den nya instansen och börja balansera trafik till den (du kanske redan har flera instanser i skalningssyfte).
- Inaktivera eller ta bort den gamla instansen i lastbalanserarens konfiguration så att den slutar ta emot ny trafik.
- Signalera att den gamla instansen ska stängas av.
- Vänta tills den töms eller överskrider tidsgränsen.
Se även
- Beroendeinmatning i .NET
- Loggning i .NET
- Konfiguration i .NET
- Worker Services i .NET
- ASP.NET Core Web Host
- ASP.NET Core Kestrel-webbserverkonfiguration
- Allmänna värdbuggar bör skapas på den github.com/dotnet/runtime lagringsplatsen