Host ogólny platformy .NET
W tym artykule przedstawiono różne wzorce konfigurowania i tworzenia hosta ogólnego platformy .NET dostępnego w pakiecie NuGet Microsoft.Extensions.Hosting . Host ogólny platformy .NET jest odpowiedzialny za uruchamianie aplikacji i zarządzanie okresem istnienia. Szablony usługi procesu roboczego tworzą hosta ogólnego platformy .NET. HostApplicationBuilder Host ogólny może być używany z innymi typami aplikacji platformy .NET, takimi jak aplikacje konsolowe.
Host to obiekt, który hermetyzuje zasoby aplikacji i funkcje okresu istnienia, takie jak:
- Wstrzykiwanie zależności (DI)
- Rejestrowanie
- Konfigurowanie
- Zamykanie aplikacji
-
IHostedService
Implementacje
Po uruchomieniu hosta wywołuje IHostedService.StartAsync każdą implementację zarejestrowaną IHostedService w kolekcji hostowanych usług kontenera usługi. W aplikacji usługi procesu roboczego wszystkie IHostedService
implementacje zawierające BackgroundService wystąpienia mają wywoływane BackgroundService.ExecuteAsync metody.
Główną przyczyną włączenia wszystkich współzależnych zasobów aplikacji w jednym obiekcie jest zarządzanie okresem istnienia: kontrola nad uruchamianiem aplikacji i bezproblemowym zamykaniem.
Konfigurowanie hosta
Host jest zwykle skonfigurowany, skompilowany i uruchamiany według kodu w Program
klasie . Metoda Main
:
- Wywołuje metodę do utworzenia CreateApplicationBuilder i skonfigurowania obiektu konstruktora.
- Wywołania Build() w celu utworzenia IHost wystąpienia.
- Wywołuje Run metodę lub RunAsync obiekt hosta.
Szablony usługi procesu roboczego platformy .NET generują następujący kod, aby utworzyć hosta ogólnego:
using Example.WorkerService;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
IHost host = builder.Build();
host.Run();
Aby uzyskać więcej informacji na temat usług roboczych, zobacz Usługi robocze na platformie .NET.
Ustawienia konstruktora hostów
Metoda CreateApplicationBuilder:
- Ustawia katalog główny zawartości na ścieżkę zwróconą przez GetCurrentDirectory().
- Ładuje konfigurację hosta z:
- Zmienne środowiskowe poprzedzone prefiksem
DOTNET_
. - Argumenty wiersza polecenia.
- Zmienne środowiskowe poprzedzone prefiksem
- Ładuje konfigurację aplikacji z:
- appsettings.json.
- appsettings. {Środowisko}.json.
- Menedżer wpisów
Development
tajnych, gdy aplikacja działa w środowisku. - Zmienne środowiskowe.
- Argumenty wiersza polecenia.
- Dodaje następujących dostawców rejestrowania:
- Konsola
- Debugowanie
- EventSource
- EventLog (tylko w przypadku uruchamiania w systemie Windows)
- Włącza walidację zakresu i walidację zależności, gdy środowisko ma wartość
Development
.
Jest HostApplicationBuilder.Services to Microsoft.Extensions.DependencyInjection.IServiceCollection wystąpienie. Te usługi są używane do tworzenia elementu używanego IServiceProvider z iniekcją zależności w celu rozwiązania zarejestrowanych usług.
Usługi dostarczane przez platformę
Po wywołaniu IHostBuilder.Build() metody lub HostApplicationBuilder.Build()następujące usługi są rejestrowane automatycznie:
Dodatkowe konstruktory hostów opartych na scenariuszach
Jeśli tworzysz aplikację internetową lub piszesz aplikację rozproszoną, może być konieczne użycie innego konstruktora hostów. Rozważmy następującą listę dodatkowych konstruktorów hostów:
- DistributedApplicationBuilder: Konstruktor do tworzenia aplikacji rozproszonych. Aby uzyskać więcej informacji, zobacz .NET Aspire.
- WebApplicationBuilder: Konstruktor aplikacji internetowych i usług. Aby uzyskać więcej informacji, zobacz ASP.NET Core.
-
WebHostBuilder: konstruktor dla elementu
IWebHost
. Aby uzyskać więcej informacji, zobacz ASP.NET Core web host.
IHostApplicationLifetime
Wstrzykuj usługę IHostApplicationLifetime do dowolnej klasy, aby obsługiwać zadania po uruchomieniu i bezproblemowym zamykaniu. Trzy właściwości interfejsu to tokeny anulowania używane do rejestrowania metod obsługi uruchamiania aplikacji i zatrzymywania aplikacji. Interfejs zawiera również metodę StopApplication() .
W poniższym przykładzie przedstawiono implementację IHostedService i IHostedLifecycleService rejestrującą IHostApplicationLifetime
zdarzenia:
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.");
}
}
Szablon usługi procesu roboczego można zmodyfikować w celu dodania implementacji 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();
Aplikacja napisze następujące przykładowe dane wyjściowe:
// 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.
Dane wyjściowe pokazują kolejność wszystkich zdarzeń cyklu życia:
IHostedLifecycleService.StartingAsync
IHostedService.StartAsync
IHostedLifecycleService.StartedAsync
IHostApplicationLifetime.ApplicationStarted
Po zatrzymaniu aplikacji, na przykład za pomocą Ctrl+C, są wywoływane następujące zdarzenia:
IHostApplicationLifetime.ApplicationStopping
IHostedLifecycleService.StoppingAsync
IHostedService.StopAsync
IHostedLifecycleService.StoppedAsync
IHostApplicationLifetime.ApplicationStopped
IHostLifetime
Implementacja IHostLifetime steruje, gdy host zostanie uruchomiony i zatrzymany. Używana jest ostatnia zarejestrowana implementacja.
Microsoft.Extensions.Hosting.Internal.ConsoleLifetime
to domyślna IHostLifetime
implementacja. Aby uzyskać więcej informacji na temat mechaniki okresu istnienia zamykania, zobacz Zamykanie hosta.
Interfejs IHostLifetime
uwidacznia metodę IHostLifetime.WaitForStartAsync , która jest wywoływana na początku IHost.StartAsync
, będzie czekać, aż zostanie ukończona przed kontynuowaniem. Może to służyć do opóźniania uruchamiania do momentu zasygnalizowania zdarzenia zewnętrznego.
IHostLifetime
Ponadto interfejs uwidacznia metodęIHostLifetime.StopAsync, która jest wywoływana z IHost.StopAsync
, aby wskazać, że host jest zatrzymany i nadszedł czas, aby go zamknąć.
IHostEnvironment
Wstrzyknąć usługę IHostEnvironment do klasy, aby uzyskać informacje o następujących ustawieniach:
- IHostEnvironment.ApplicationName
- IHostEnvironment.ContentRootFileProvider
- IHostEnvironment.ContentRootPath
- IHostEnvironment.EnvironmentName
IHostEnvironment
Ponadto usługa uwidacznia możliwość oceny środowiska przy użyciu następujących metod rozszerzeń:
- HostingEnvironmentExtensions.IsDevelopment
- HostingEnvironmentExtensions.IsEnvironment
- HostingEnvironmentExtensions.IsProduction
- HostingEnvironmentExtensions.IsStaging
Konfiguracja hosta
Konfiguracja hosta służy do konfigurowania właściwości implementacji IHostEnvironment .
Konfiguracja hosta jest dostępna we HostApplicationBuilderSettings.Configuration właściwości, a implementacja środowiska jest dostępna we IHostApplicationBuilder.Environment właściwości. Aby skonfigurować hosta, uzyskaj dostęp do Configuration
właściwości i wywołaj dowolną z dostępnych metod rozszerzenia.
Aby dodać konfigurację hosta, rozważmy następujący przykład:
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();
Powyższy kod ma następujące działanie:
- Ustawia katalog główny zawartości na ścieżkę zwróconą przez GetCurrentDirectory().
- Ładuje konfigurację hosta z:
- hostsettings.json.
- Zmienne środowiskowe poprzedzone prefiksem
PREFIX_
. - Argumenty wiersza polecenia.
Konfiguracja aplikacji
Konfiguracja aplikacji jest tworzona przez wywołanie ConfigureAppConfiguration metody IHostApplicationBuilder. Właściwość publiczna IHostApplicationBuilder.Configuration umożliwia użytkownikom odczytywanie lub wprowadzanie zmian w pierwotnej konfiguracji przy użyciu dostępnych metod rozszerzeń.
Aby uzyskać więcej informacji, zobacz Konfiguracja na platformie .NET.
Zamykanie hosta
Istnieje kilka sposobów zatrzymania hostowanego procesu. Najczęściej hostowany proces można zatrzymać w następujący sposób:
- Jeśli ktoś nie wywołuje Run lub HostingAbstractionsHostExtensions.WaitForShutdown aplikacja kończy się normalnie z ukończeniem
Main
. - Jeśli aplikacja ulegnie awarii.
- Jeśli aplikacja jest wymuszanie zamknięta przy użyciu SIGKILL (lub Ctrl+Z).
Kod hostingu nie jest odpowiedzialny za obsługę tych scenariuszy. Właściciel procesu musi poradzić sobie z nimi tak samo jak każda inna aplikacja. Istnieje kilka innych sposobów zatrzymania hostowanego procesu usługi:
- Jeśli
ConsoleLifetime
jest używany (UseConsoleLifetime), nasłuchuje następujących sygnałów i próbuje zatrzymać hosta bezpiecznie. - Jeśli aplikacja wywołuje metodę Environment.Exit.
Wbudowana logika hostingu obsługuje te scenariusze, w szczególności klasę ConsoleLifetime
.
ConsoleLifetime
próbuje obsłużyć sygnały "shutdown" SIGINT, SIGQUIT i SIGTERM, aby umożliwić bezproblemowe wyjście z aplikacji.
Przed platformą .NET 6 nie było sposobu, aby kod platformy .NET bezpiecznie obsługiwał SIGTERM. Aby obejść to ograniczenie, ConsoleLifetime
należy zasubskrybować usługę System.AppDomain.ProcessExit. Kiedy ProcessExit
został zgłoszony, ConsoleLifetime
zasygnalizuje, że host zatrzyma i zablokuje ProcessExit
wątek, czekając na zatrzymanie hosta.
Procedura obsługi zakończenia procesu umożliwiłaby uruchomienie kodu oczyszczania w aplikacji — na przykład IHost.StopAsync i kodu po HostingAbstractionsHostExtensions.Run metodzie Main
.
Jednak wystąpiły inne problemy z tym podejściem, ponieważ SIGTERM nie był jedynym sposobem, w jaki ProcessExit
został podniesiony. Funkcja SIGTERM jest również wywoływana, gdy kod aplikacji wywołuje metodę Environment.Exit
.
Environment.Exit
Nie jest to niełatwy sposób zamykania procesu w Microsoft.Extensions.Hosting
modelu aplikacji. Zgłasza ProcessExit
zdarzenie, a następnie kończy proces.
Main
Koniec metody nie jest wykonywany. Wątki tła i pierwszego planu są przerywane, a finally
bloki nie są wykonywane.
Ponieważ ConsoleLifetime
zablokowano ProcessExit
podczas oczekiwania na zamknięcie hosta, to zachowanie doprowadziło również do oczekujących na wywołanie do Environment.Exit
. Ponadto, ponieważ obsługa SIGTERM próbowała bezpiecznie zamknąć proces, ConsoleLifetime
ustawi ExitCode0
wartość , która clobbered kod zakończenia użytkownika został przekazany do Environment.Exit
.
Na platformie .NET 6 sygnały POSIX są obsługiwane i obsługiwane. Obsługa ConsoleLifetime
SIGTERM z wdziękiem i nie jest już zaangażowana, gdy Environment.Exit
jest wywoływana.
Napiwek
W przypadku platformy .NET 6 lub nowszej ConsoleLifetime
nie ma już logiki do obsługi scenariusza Environment.Exit
. Aplikacje, które wywołają Environment.Exit
logikę czyszczenia i muszą wykonywać te czynności, mogą subskrybować ProcessExit
się samodzielnie. Hosting nie będzie już próbował bezpiecznie zatrzymać hosta w tych scenariuszach.
Jeśli aplikacja korzysta z hostingu i chcesz bezpiecznie zatrzymać hosta, możesz wywołać IHostApplicationLifetime.StopApplication metodę zamiast Environment.Exit
.
Proces zamykania hostingu
Na poniższym diagramie sekwencji pokazano, jak sygnały są obsługiwane wewnętrznie w kodzie hostingu. Większość użytkowników nie musi rozumieć tego procesu. Jednak w przypadku deweloperów, którzy potrzebują głębokiego zrozumienia, dobra wizualizacja może pomóc w rozpoczęciu pracy.
Po uruchomieniu hosta, gdy użytkownik wywołuje Run
metodę lub WaitForShutdown
, program obsługi zostanie zarejestrowany dla programu IApplicationLifetime.ApplicationStopping. Wykonanie jest wstrzymane w WaitForShutdown
programie , czekając na ApplicationStopping
podniesienie zdarzenia.
Main
Metoda nie zwraca się od razu, a aplikacja pozostaje uruchomiona do czasu Run
powrotu lub WaitForShutdown
powrotu.
Gdy sygnał jest wysyłany do procesu, inicjuje następującą sekwencję:
- Kontrolka przepływa od
ConsoleLifetime
doApplicationLifetime
, aby podnieśćApplicationStopping
zdarzenie. Spowoduje to odblokowanieWaitForShutdownAsync
kodu wykonawczegoMain
. W międzyczasie program obsługi sygnałów POSIX zwraca wartość zCancel = true
od czasu obsługi sygnału POSIX. -
Main
Kod wykonywania rozpoczyna wykonywanie ponownie i nakazuje hostowiStopAsync()
polecenie , co z kolei zatrzymuje wszystkie hostowane usługi i zgłasza wszystkie inne zatrzymane zdarzenia. - Na koniec kończy działanie,
WaitForShutdown
co pozwala na wykonanie dowolnego kodu oczyszczania aplikacji iMain
bezpieczne zakończenie działania metody.
Zamykanie hosta w scenariuszach serwera internetowego
Istnieją różne inne typowe scenariusze, w których bezpieczne zamykanie działa w usłudze Kestrel dla protokołów HTTP/1.1 i HTTP/2 oraz sposób konfigurowania go w różnych środowiskach z modułem równoważenia obciążenia w celu bezproblemowego opróżniania ruchu. Chociaż konfiguracja serwera internetowego wykracza poza zakres tego artykułu, więcej informacji można znaleźć w artykule Konfigurowanie opcji dla dokumentacji serwera internetowego ASP.NET Core Kestrel.
Gdy host odbiera sygnał zamknięcia (na przykład ctrl+C lub StopAsync
), powiadamia aplikację, sygnalizując ApplicationStopping. Należy zasubskrybować to zdarzenie, jeśli masz długotrwałe operacje, które muszą zostać pomyślnie zakończone.
Następnie host wywołuje IServer.StopAsync limit czasu zamknięcia, który można skonfigurować (domyślnie 30s). Kestrel (i Http.Sys) zamyka powiązania portów i przestaje akceptować nowe połączenia. Informują również bieżące połączenia o zatrzymaniu przetwarzania nowych żądań. W przypadku protokołu HTTP/2 i HTTP/3 do klienta jest wysyłany wstępny GOAWAY
komunikat. W przypadku protokołu HTTP/1.1 zatrzymują pętlę połączenia, ponieważ żądania są przetwarzane w kolejności. Usługi IIS zachowują się inaczej, odrzucając nowe żądania z kodem stanu 503.
Aktywne żądania mają czas do zakończenia limitu czasu zamknięcia. Jeśli wszystkie te elementy zostaną ukończone przed przekroczeniem limitu czasu, serwer zwróci kontrolę do hosta wcześniej. Jeśli limit czasu wygaśnie, oczekujące połączenia i żądania zostaną przerwane siłowo, co może spowodować błędy w dziennikach i klientach.
Zagadnienia dotyczące modułu równoważenia obciążenia
Aby zapewnić bezproblemowe przejście klientów do nowego miejsca docelowego podczas pracy z modułem równoważenia obciążenia, możesz wykonać następujące kroki:
- Uruchom nowe wystąpienie i rozpocznij równoważenie ruchu do niego (być może masz już kilka wystąpień na potrzeby skalowania).
- Wyłącz lub usuń stare wystąpienie w konfiguracji modułu równoważenia obciążenia, aby zatrzymać odbieranie nowego ruchu.
- Zasygnalizuj zamknięcie starego wystąpienia.
- Poczekaj na opróżnienie lub przekroczenie limitu czasu.
Zobacz też
- Wstrzykiwanie zależności na platformie .NET
- Rejestrowanie na platformie .NET
- Konfiguracja na platformie .NET
- Usługi robocze na platformie .NET
- Host internetowy platformy ASP.NET Core
- konfiguracja serwera internetowego ASP.NET Core Kestrel
- Ogólne usterki hosta powinny zostać utworzone w repozytorium github.com/dotnet/runtime