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:

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.
  • Ł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:

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:

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

Po zatrzymaniu aplikacji, na przykład za pomocą klawisza Ctrl+C, są wywoływane następujące zdarzenia:

  1. IHostApplicationLifetime.ApplicationStopping
  2. IHostedLifecycleService.StoppingAsync
  3. IHostedService.StopAsync
  4. IHostedLifecycleService.StoppedAsync
  5. 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 Ponadto usługa uwidacznia możliwość oceny środowiska przy użyciu następujących metod rozszerzeń:

Konfiguracja hosta

Konfiguracja hosta służy do konfigurowania właściwości implementacji IHostEnvironment .

Konfiguracja hosta jest dostępna we IHostApplicationBuilder.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;

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

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ść publicznaIHostApplicationBuilder.Configuration umożliwia użytkownikom odczytywanie z istniejącej konfiguracji lub wprowadzanie zmian w istniejącej 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:

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.
    • SIGINT (lub CTRL+C).
    • SIGQUIT (lub CTRL+BREAK w systemie Windows, CTRL+\ w systemie Unix).
    • SIGTERM (wysyłane przez inne aplikacje, takie jak docker stop).
  • 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 zakleszczenia z Environment.Exit bloków oczekujących na wywołanie do ProcessExit. Ponadto, ponieważ obsługa SIGTERM próbowała bezpiecznie zamknąć proces, ConsoleLifetime ustawi ExitCode0wartość , 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 WaitForShutdownprogramie , 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ę:

Hostowanie diagramu sekwencji zamykania.

  1. Kontrolka przepływa od ConsoleLifetime do ApplicationLifetime , aby podnieść ApplicationStopping zdarzenie. Spowoduje to odblokowanie WaitForShutdownAsync kodu wykonawczego Main . W międzyczasie program obsługi sygnałów POSIX zwraca wartość z Cancel = true od czasu obsługi sygnału POSIX.
  2. Main Kod wykonywania rozpoczyna wykonywanie ponownie i nakazuje hostowi StopAsync()polecenie , co z kolei zatrzymuje wszystkie hostowane usługi i zgłasza wszystkie inne zatrzymane zdarzenia.
  3. Na koniec kończy działanie, WaitForShutdown co pozwala na wykonanie dowolnego kodu oczyszczania aplikacji i Main 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 CTL+C lub StopAsync), powiadamia aplikację za pomocą sygnału .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ż