Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
In questo articolo vengono illustrati i vari modelli per la configurazione e la compilazione di un host generico .NET disponibile nel pacchetto NuGet Microsoft.Extensions.Hosting. L'host .NET generico è responsabile della gestione dell'avvio e della durata delle app. I Worker Service templates creano un host generico .NET, HostApplicationBuilder. L'host generico può essere usato con altri tipi di applicazioni .NET, ad esempio app console.
Un host è un oggetto che incapsula le risorse e la durata di un'app, ad esempio:
- Inserimento di dipendenze (DI)
- Registrazione
- Impostazione
- Arresto dell'app
-
IHostedServiceImplementazioni
Quando un host si avvia, chiama IHostedService.StartAsync per ogni implementazione di IHostedService registrata nella raccolta dei servizi ospitati nel contenitore dei servizi. Nell'applicazione di servizio per i worker, tutte le implementazioni IHostedService che contengono istanze BackgroundService hanno i loro metodi BackgroundService.ExecuteAsync chiamati.
Il motivo principale per cui tutte le risorse interdipendenti dell'app sono incluse in un unico oggetto è la gestione del ciclo di vita, vale a dire il controllo sull'avvio dell'app e sull'arresto normale.
Opzioni del costruttore dell'host
.NET offre due approcci per la configurazione e la creazione di un host generico:
IHostApplicationBuilder (
Host.CreateApplicationBuilder): introdotto in .NET 6, questo approccio usa uno stile di configurazione lineare basato su proprietà. I servizi, la configurazione e la registrazione vengono configurati accedendo direttamente alle proprietà nell'oggetto builder , ad esempiobuilder.Services,builder.Configuration. Questo approccio è consigliato per i nuovi progetti ed è l'impostazione predefinita nei modelli .NET correnti.IHostBuilder (
Host.CreateDefaultBuilder): questo è l'approccio tradizionale basato sul callback in cui la configurazione viene eseguita tramite metodi di estensione concatenati (ad esempio,ConfigureServices,ConfigureAppConfiguration). Sebbene sia completamente supportato, questo approccio legacy funziona meglio per mantenere la compatibilità con le codebase esistenti.
Entrambi gli approcci forniscono le stesse funzionalità di base e i comportamenti predefiniti. Scegliere IHostApplicationBuilder per i nuovi progetti per allinearsi ai modelli .NET moderni e al codice di configurazione più semplice. Usare IHostBuilder quando si mantengono applicazioni esistenti o quando le librerie di terze parti richiedono il modello basato sul callback.
Configurare un host
L'host è in genere configurato, compilato ed eseguito da codice nella classe Program. Il metodo Main:
- Chiama un metodo CreateApplicationBuilder per creare e configurare un oggetto generatore.
- Richiama Build() per creare un'istanza IHost.
- Richiama il metodo Run o RunAsync nell'oggetto host.
I modelli di servizio del ruolo di lavoro .NET generano il codice seguente per creare un host generico:
using Example.WorkerService;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
IHost host = builder.Build();
host.Run();
Per altre informazioni sui servizi del ruolo di lavoro, vedere Servizi del ruolo di lavoro in .NET.
Impostazioni del costruttore host
Il metodo CreateApplicationBuilder:
- Imposta la radice del contenuto sul percorso restituito da GetCurrentDirectory().
- Carica la configurazione dell'host da:
- Le variabili di ambiente con prefisso
DOTNET_. - Argomenti della riga di comando.
- Le variabili di ambiente con prefisso
- Carica la configurazione dell'app da:
- appsettings.json.
- appsettings. {Environment}.json.
- Secret Manager quando l'applicazione viene eseguita nell'ambiente
Development. - variabili di ambiente.
- Argomenti della riga di comando.
- Aggiunge i provider di log seguenti:
- Console
- Correzione errori di programma
- FonteEvento
- EventLog (solo quando è in esecuzione su Windows)
- Abilita la convalida dell'ambito e la convalida delle dipendenze quando l'ambiente è
Development.
HostApplicationBuilder.Services è un'istanza Microsoft.Extensions.DependencyInjection.IServiceCollection. Questi servizi vengono usati per costruire un IServiceProvider che viene usato con l'iniezione delle dipendenze per risolvere i servizi registrati.
Servizi forniti dal framework
Quando si chiama IHostBuilder.Build() o HostApplicationBuilder.Build(), i servizi seguenti vengono registrati automaticamente:
Generatori host aggiuntivi basati su scenari
Se si compila per il Web o si scrive un'applicazione distribuita, potrebbe essere necessario usare un generatore host diverso. Considerare l'elenco seguente di generatori di host aggiuntivi:
- DistributedApplicationBuilder: generatore per la creazione di app distribuite. Per altre informazioni, vedere Aspirare.
- WebApplicationBuilder: generatore per applicazioni Web e servizi. Per altre informazioni, vedere ASP.NET Core.
-
WebHostBuilder: generatore per
IWebHost. Per altre informazioni, vedere Host Web ASP.NET Core.
IHostApplicationLifetime
Inserire il servizio IHostApplicationLifetime in qualsiasi classe per gestire le attività post-avvio e di chiusura controllata. Tre proprietà nell'interfaccia sono token di annullamento utilizzati per registrare i metodi del gestore degli eventi di avvio e arresto dell'app. L'interfaccia include anche un metodo StopApplication().
L'esempio seguente è un'implementazione IHostedService e IHostedLifecycleService che registra gli eventi 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.");
}
}
È possibile modificare il modello di servizio Worker per aggiungere l'implementazione 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'applicazione scriverà l'output di esempio seguente:
// 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.
L'output mostra l'ordine di tutti i vari eventi del ciclo di vita:
IHostedLifecycleService.StartingAsyncIHostedService.StartAsyncIHostedLifecycleService.StartedAsyncIHostApplicationLifetime.ApplicationStarted
Quando l'applicazione viene arrestata, ad esempio con CTRL+C, vengono generati gli eventi seguenti:
IHostApplicationLifetime.ApplicationStoppingIHostedLifecycleService.StoppingAsyncIHostedService.StopAsyncIHostedLifecycleService.StoppedAsyncIHostApplicationLifetime.ApplicationStopped
IHostLifetime
L'implementazione IHostLifetime controlla quando l'host si avvia e quando si arresta. Viene usata l'ultima implementazione registrata.
Microsoft.Extensions.Hosting.Internal.ConsoleLifetime è l'implementazione IHostLifetime predefinita. Per altre informazioni sui meccanismi di durata dell'arresto, vedere Arresto dell'host.
L'interfaccia IHostLifetime espone un metodo IHostLifetime.WaitForStartAsync, chiamato all'inizio di IHost.StartAsync che attenderà il completamento prima di continuare. Questo è utile per ritardare l'avvio fino al segnale trasmesso da un evento esterno.
Inoltre, l'interfaccia IHostLifetime espone un metodo IHostLifetime.StopAsync, chiamato da IHost.StopAsync per indicare che l'host si sta fermando ed è tempo di spegnere.
IHostEnvironment
Inserire il servizio IHostEnvironment in una classe per ottenere informazioni sulle impostazioni seguenti:
- IHostEnvironment.ApplicationName
- IHostEnvironment.ContentRootFileProvider
- IHostEnvironment.ContentRootPath
- IHostEnvironment.EnvironmentName
Inoltre, il servizio IHostEnvironment espone la possibilità di valutare l'ambiente con l'aiuto di questi metodi di estensione:
- HostEnvironmentEnvExtensions.IsDevelopment
- HostEnvironmentEnvExtensions.IsEnvironment
- HostEnvironmentEnvExtensions.IsProduction
- HostEnvironmentEnvExtensions.IsStaging
Configurazione dell'host
La configurazione host viene usata per configurare le proprietà dell'implementazione di IHostEnvironment.
La configurazione host è disponibile nella proprietà HostApplicationBuilderSettings.Configuration mentre l'implementazione dell'ambiente è disponibile nella proprietà IHostApplicationBuilder.Environment. Per configurare l'host, accedere alla proprietà Configuration e richiamare uno dei metodi di estensione disponibili.
Per aggiungere la configurazione host, considerare l'esempio seguente:
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();
Il codice precedente:
- Imposta la radice del contenuto sul percorso restituito da GetCurrentDirectory().
- Carica la configurazione dell'host da:
- hostsettings.json.
- Le variabili di ambiente con prefisso
PREFIX_. - Argomenti della riga di comando.
Configurazione dell'app
È possibile accedere alla configurazione dell'app tramite la proprietà pubblica IHostApplicationBuilder.Configuration . Questa proprietà consente ai consumer di leggere o apportare modifiche alla configurazione esistente usando i metodi di estensione disponibili.
Per altre informazioni, vedi Configurazione in .NET.
Spegnimento dell'host
Esistono diversi modi in cui un processo ospitato viene arrestato. In genere, un processo ospitato può essere arrestato nei modi seguenti:
- Se qualcuno non richiama Run o HostingAbstractionsHostExtensions.WaitForShutdown e l'app viene chiusa normalmente con il completamento di
Main. - Se l'applicazione va in crash.
- Se l'app viene terminata forzatamente utilizzando SIGKILL (o CTRL+Z).
Il codice di hosting non è responsabile della gestione di tali scenari. Il proprietario del processo deve gestirli come qualsiasi altra app. Esistono diversi altri modi in cui è possibile arrestare un processo del servizio ospitato:
- Se viene usato
ConsoleLifetime(UseConsoleLifetime), ascolta i segnali seguenti e tenta di arrestare l'host in modo corretto. - Se l'app chiama Environment.Exit.
La logica di hosting integrata gestisce scenari di questo tipo, in particolare la classe ConsoleLifetime.
ConsoleLifetime tenta di gestire i segnali di “arresto” SIGINT, SIGQUIT e SIGTERM per consentire un'uscita corretta all'applicazione.
Prima di .NET 6, il codice .NET non era in grado di gestire correttamente SIGTERM. Per ovviare a questa limitazione, ConsoleLifetime sottoscriverebbe System.AppDomain.ProcessExit. Quando ProcessExit veniva generato, ConsoleLifetime avrebbe segnalato all'host di fermare e bloccare il thread ProcessExit, aspettando che l'host si fermasse.
Il gestore di uscita del processo consentirebbe l'esecuzione del codice di pulizia nell'applicazione, ad esempio IHost.StopAsync e il codice successivo a HostingAbstractionsHostExtensions.Run nel metodo Main.
Tuttavia, ci sono stati altri problemi con questo approccio perché SIGTERM non era l'unico modo in cui ProcessExit veniva sollevato. SIGTERM viene generato anche quando il codice applicativo richiama Environment.Exit.
Environment.Exit non è un modo normale per arrestare un processo nel modello Microsoft.Extensions.Hosting dell’app. Esso genera l'evento ProcessExit e quindi esce dal processo. Il termine del metodo Main non viene eseguito. I thread di background e di primo piano vengono terminati e i blocchi finallynon vengono eseguiti.
Poiché ConsoleLifetime ha bloccato ProcessExit in attesa dell'arresto dell'host, tale comportamento ha portato a dei deadlock dovuti anche ai blocchi Environment.Exit in attesa della chiamata a ProcessExit. Inoltre, poiché la gestione SIGTERM tentava di arrestare il processo in modo ordinato, ConsoleLifetime impostava ExitCode su 0, che ha sovrascritto il codice di uscita dell'utente passato a Environment.Exit.
In .NET 6 i segnali POSIX vengono supportati e gestiti.
ConsoleLifetime gestisce SIGTERM normalmente e non viene più coinvolto quando Environment.Exit viene richiamato.
Suggerimento
Per .NET 6+, ConsoleLifetime non ha più logica per gestire lo scenario Environment.Exit. Le app che richiamano Environment.Exit e devono eseguire la logica di pulizia possono sottoscrivere se stesse a ProcessExit. In questi scenari, il servizio di hosting non tenterà più di arrestare in modo pulito l'host.
Se l'applicazione utilizza l'hosting e si desidera arrestare correttamente l'host, è possibile richiamare IHostApplicationLifetime.StopApplication invece di Environment.Exit.
Processo di spegnimento dell'hosting
Il diagramma di sequenza seguente mostra come i segnali vengono gestiti internamente nel codice di hosting. La maggior parte degli utenti non ha necessità di comprendere questo processo. Tuttavia, per gli sviluppatori che necessitano di una comprensione approfondita, avere una buona visuale della cosa può essere utile per iniziare.
Dopo l'avvio dell'host, quando un utente richiama Run o WaitForShutdown, un gestore viene registrato per IApplicationLifetime.ApplicationStopping. L'esecuzione viene sospesa in WaitForShutdown, in attesa della generazione dell'evento ApplicationStopping. Il metodo Main non restituisce immediatamente e l'app rimane in esecuzione fino a quando non viene restituito Run o WaitForShutdown.
Quando un segnale viene inviato al processo, avvia la sequenza seguente:
- Il controllo passa da
ConsoleLifetimeaApplicationLifetimeper generare l'eventoApplicationStopping. Ciò segnala aWaitForShutdownAsyncdi sbloccare il codice di esecuzioneMain. Nel frattempo, il gestore del segnale POSIX restituisceCancel = truepoiché il segnale POSIX è stato gestito. - Il codice di esecuzione
Mainavvia nuovamente l'esecuzione e indica all'host diStopAsync(), che a sua volta arresta tutti i servizi ospitati e genera qualsiasi altro evento arrestato. - Infine,
WaitForShutdownesce, consentendo l'esecuzione di qualsiasi codice di pulizia dell'applicazione e la chiusura normale del metodoMain.
Arresto dell'host in scenari di server Web
Esistono vari altri scenari comuni in cui l'arresto normale funziona in Kestrel per entrambi i protocolli HTTP/1.1 e HTTP/2: come configurarlo in ambienti diversi con un servizio di bilanciamento del carico per svuotare il traffico senza problemi. Sebbene la configurazione del server Web esuli dall'ambito di questo articolo, è possibile trovare altre informazioni sulla documentazione relativa alle opzioni di configurazione del server Web Kestrel di ASP.NET Core.
Quando l'host riceve un segnale di arresto (ad esempio, CTRL+C o StopAsync), notifica all'applicazione segnalando ApplicationStopping. Dovresti iscriverti a questo evento se hai operazioni di lunga durata che devono essere completate senza intoppi.
Successivamente, l’host chiama IServer.StopAsync con un timeout di arresto configurabile (impostazione predefinita di 30s). Kestrel (e Http.Sys) chiudono le associazioni di porte e interrompono l'accettazione di nuove connessioni. Indicano anche alle connessioni correnti di interrompere l'elaborazione di nuove richieste. Per HTTP/2 e HTTP/3, viene inviato un messaggio preliminare GOAWAY al client. Per HTTP/1.1 arrestano il ciclo di connessione perché le richieste vengono elaborate in ordine. IIS si comporta in modo diverso, rifiutando le nuove richieste con un codice di stato 503.
Le richieste attive devono essere completate entro il tempo limite di arresto. Se sono tutte completate prima del timeout, il server restituisce prima il controllo all'host. Se il timeout scade, le connessioni e le richieste in sospeso vengono interrotte forzatamente, causando errori nei log e ai client.
Considerazioni sul bilanciamento del carico
Per garantire una transizione uniforme dei client a una nuova destinazione quando si usa un servizio di bilanciamento del carico, è possibile seguire questa procedura:
- Avvia la nuova istanza e inizia il bilanciamento del traffico verso di essa (potresti già avere diverse istanze per scopi di scalabilità).
- Disabilitare o rimuovere l'istanza precedente nella configurazione del servizio di bilanciamento del carico in modo da arrestare la ricezione di nuovo traffico.
- Segnalare la chiusura della vecchia istanza.
- Attendere che si svuoti o si esaurisca.
Vedi anche
- Iniezione delle dipendenze in .NET
- Registrazione in .NET
- Configurazione in .NET
- Servizi ruolo di lavoro in .NET
- Host Web ASP.NET Core
- Configurazione del server Web Kestrel di ASP.NET Core
- I bug dell’host generico devono essere creati nel repository github.com/dotnet/runtime.