Obecný hostitel .NET

V tomto článku se dozvíte o různých vzorech konfigurace a vytvoření obecného hostitele .NET, který je k dispozici v balíčku NuGet Microsoft.Extensions.Hosting . Obecný hostitel .NET zodpovídá za správu po spuštění a životnosti aplikace. Šablony služby Pracovních procesů vytvářejí obecného hostitele .NET. HostApplicationBuilder Obecný hostitel lze použít s jinými typy aplikací .NET, jako jsou konzolové aplikace.

Hostitel je objekt, který zapouzdřuje prostředky a životnost aplikace, například:

  • Injektáž závislostí (DI)
  • Protokolování
  • Konfigurace
  • Vypnutí aplikace
  • IHostedService Implementace

Když se hostitel spustí, volá IHostedService.StartAsync každou implementaci zaregistrované IHostedService v kolekci hostovaných služeb kontejneru služby. V aplikaci pracovní služby mají všechny IHostedService implementace, které obsahují BackgroundService instance, volané metody BackgroundService.ExecuteAsync .

Hlavním důvodem zahrnutí všech vzájemně závislých prostředků aplikace v jednom objektu je správa životnosti: kontrola nad spuštěním aplikace a řádné vypnutí.

Nastavení hostitele

Hostitel je obvykle nakonfigurovaný, sestavený a spouštěný kódem ve Program třídě. Metoda Main:

Šablony služby pracovních procesů .NET vygenerují následující kód pro vytvoření obecného hostitele:

using Example.WorkerService;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();

IHost host = builder.Build();
host.Run();

Další informace o pracovních službách naleznete v tématu Služby pracovních procesů v .NET.

Nastavení tvůrce hostitelů

Metoda CreateApplicationBuilder:

  • Nastaví kořen obsahu na cestu vrácenou .GetCurrentDirectory()
  • Načte konfiguraci hostitele z:
    • Proměnné prostředí s předponou DOTNET_.
    • Argumenty příkazového řádku
  • Načte konfiguraci aplikace z:
    • appsettings.json.
    • Appsettings. {Environment}.json.
    • Správce tajných kódů při spuštění aplikace v Development prostředí
    • Proměnné prostředí.
    • Argumenty příkazového řádku
  • Přidá následující zprostředkovatele protokolování:
    • Konzola
    • Ladění
    • EventSource
    • EventLog (pouze při spuštění ve Windows)
  • Povolí ověřování oboru a ověřování závislostí, pokud je Developmentprostředí .

Jedná se HostApplicationBuilder.Services o Microsoft.Extensions.DependencyInjection.IServiceCollection instanci. Tyto služby slouží k vytvoření IServiceProvider injektáže závislostí k vyřešení registrovaných služeb.

Služby poskytované architekturou

Při volání nebo IHostBuilder.Build()HostApplicationBuilder.Build()se automaticky zaregistrují následující služby:

IHostApplicationLifetime

IHostApplicationLifetime Vložte službu do libovolné třídy pro zpracování úloh po spuštění a řádném vypnutí. Tři vlastnosti rozhraní jsou tokeny zrušení používané k registraci metod spuštění aplikace a zastavení aplikace obslužné rutiny událostí. Rozhraní také obsahuje metodu StopApplication() .

Následující příklad je IHostedService a IHostedLifecycleService implementace, která registruje IHostApplicationLifetime události:

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.");
    }
}

Šablonu pracovní služby je možné upravit tak, aby se přidala ExampleHostedService implementace:

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

Aplikace by napsala následující ukázkový výstup:

// 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.

Výstup zobrazuje pořadí všech různých událostí životního cyklu:

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

Když je aplikace zastavena, například s Ctrl+C, jsou vyvolány následující události:

  1. IHostApplicationLifetime.ApplicationStopping
  2. IHostedLifecycleService.StoppingAsync
  3. IHostedService.StopAsync
  4. IHostedLifecycleService.StoppedAsync
  5. IHostApplicationLifetime.ApplicationStopped

IHostLifetime

Implementace IHostLifetime řídí, kdy se hostitel spustí a kdy se zastaví. Použije se poslední zaregistrovaná implementace. Microsoft.Extensions.Hosting.Internal.ConsoleLifetime je výchozí IHostLifetime implementace. Další informace o mechanismu životnosti vypnutí najdete v tématu Vypnutí hostitele.

Rozhraní IHostLifetime zveřejňuje metodu IHostLifetime.WaitForStartAsync , která je volána na začátku IHost.StartAsync , která bude čekat na dokončení, než bude pokračovat. Tuto možnost lze použít ke zpoždění spouštění, dokud není signalizovat externí událostí.

Kromě toho rozhraní zveřejňuje metoduIHostLifetime.StopAsync, která je volána z IHost.StopAsync indikující, IHostLifetime že hostitel se zastavuje a je čas vypnout.

IHostEnvironment

IHostEnvironment Vložte službu do třídy, abyste získali informace o následujících nastaveních:

Kromě toho IHostEnvironment služba zpřístupňuje možnost vyhodnotit prostředí pomocí těchto rozšiřujících metod:

Konfigurace hostitele

Konfigurace hostitele slouží ke konfiguraci vlastností implementace IHostEnvironment .

Konfigurace hostitele je k dispozici ve IHostApplicationBuilder.Configuration vlastnosti a implementace prostředí je k dispozici ve IHostApplicationBuilder.Environment vlastnosti. Pokud chcete hostitele nakonfigurovat, získejte přístup k Configuration vlastnosti a volejte některou z dostupných metod rozšíření.

Pokud chcete přidat konfiguraci hostitele, zvažte následující příklad:

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

Předchozí kód:

  • Nastaví kořen obsahu na cestu vrácenou .GetCurrentDirectory()
  • Načte konfiguraci hostitele z:
    • hostsettings.json.
    • Proměnné prostředí s předponou PREFIX_.
    • Argumenty příkazového řádku

Konfigurace aplikace

Konfigurace aplikace se vytvoří voláním ConfigureAppConfiguration na .IHostApplicationBuilder VeřejnáIHostApplicationBuilder.Configuration vlastnost umožňuje uživatelům číst nebo měnit stávající konfiguraci pomocí dostupných metod rozšíření.

Další informace naleznete v tématu Konfigurace v .NET.

Vypnutí hostitele

Hostovaný proces je zastavený několika způsoby. Hostovaný proces je nejčastěji možné zastavit následujícími způsoby:

Kód hostování neodpovídá za zpracování těchto scénářů. Vlastník procesu se s nimi musí vypořádat stejně jako s jakoukoli jinou aplikací. Proces hostované služby se dá zastavit několika dalšími způsoby:

  • Pokud ConsoleLifetime se použije (UseConsoleLifetime), naslouchá následujícím signálům a pokusí se hostiteli elegantně zastavit.
    • SIGINT (nebo CTRL+C).
    • SIGQUIT (nebo CTRL+BREAK ve Windows, CTRL+\ v Unixu).
    • SIGTERM (odesílaný jinými aplikacemi, například docker stop).
  • Pokud aplikace volá Environment.Exit.

Integrovaná logika hostování zpracovává tyto scénáře, konkrétně ConsoleLifetime třídu. ConsoleLifetime se pokusí zpracovat "vypnutí" signály SIGINT, SIGQUIT a SIGTERM, aby umožňovaly řádné ukončení aplikace.

Před .NET 6 neexistuje způsob, jak kód .NET elegantně zpracovat SIGTERM. Pokud chcete toto omezení obejít, ConsoleLifetime přihlaste se k odběru System.AppDomain.ProcessExit. Když ProcessExit byl vyvolán, ConsoleLifetime signalizoval by hostitel, aby zastavil a zablokoval ProcessExit vlákno, a čekal na zastavení hostitele.

Obslužná rutina ukončení procesu by umožňovala spuštění kódu vyčištění v aplikaci – IHost.StopAsync například kód za HostingAbstractionsHostExtensions.Run metodou Main .

Tento přístup ale měl i jiné problémy, protože SIGTERM nebyl jediným způsobem ProcessExit . SIGTERM je vyvolán také při volání Environment.Exitkódu aplikace . Environment.Exit není elegantní způsob vypnutí procesu v Microsoft.Extensions.Hosting modelu aplikace. ProcessExit Vyvolá událost a pak proces ukončí. Konec Main metody se nespustí. Vlákna na pozadí a popředí jsou ukončena a finally bloky se nespustí .

Vzhledem k tomu, ConsoleLifetime že blokované ProcessExit při čekání na vypnutí hostitele, toto chování vedlo k zablokování z Environment.Exit také bloků čekání na volání ProcessExit. Kromě toho, že se zpracování SIGTERM pokoušelo řádně vypnout proces, ConsoleLifetime by nastavil ExitCode na 0, který zahltí ukončovací kód uživatele předanýEnvironment.Exit.

V rozhraní .NET 6 se podporují a zpracovávají signály POSIX. Obslužné ConsoleLifetime rutiny SIGTERM elegantně a při vyvolání se už nezabývá Environment.Exit .

Tip

Pro .NET 6 nebo novější ConsoleLifetime už nemá logiku pro zpracování scénáře Environment.Exit. Aplikace, které volají Environment.Exit a potřebují provádět logiku čištění, se můžou přihlásit k odběru ProcessExit sebe sama. Hostování se už nebude pokoušet o řádné zastavení hostitele v těchto scénářích.

Pokud vaše aplikace používá hostování a chcete elegantně zastavit hostitele, můžete volat IHostApplicationLifetime.StopApplication místo Environment.Exit.

Proces hostování vypnutí

Následující sekvenční diagram ukazuje, jak se signály zpracovávají interně v hostitelském kódu. Většinauživatelůch Ale pro vývojáře, kteří potřebují hluboké porozumění, vám může dobrý vizuál pomoct začít.

Po spuštění hostitele se při volání Run uživatele nebo WaitForShutdownobslužné rutině zaregistruje IApplicationLifetime.ApplicationStopping. Provádění je pozastaveno v WaitForShutdown, čeká ApplicationStopping na vyvolání události. Metoda Main se okamžitě nevrátí a aplikace zůstane spuštěná, dokud Run se nevrátí nebo WaitForShutdown nevrátí.

Když se do procesu odešle signál, zahájí následující sekvenci:

Hostování sekvenčního diagramu vypnutí

  1. Řízení proudí z ConsoleLifetime dosadí ApplicationLifetimeApplicationStopping událost. To signalizuje WaitForShutdownAsync odblokování spouštěcího Main kódu. Mezitím se obslužná rutina signálu POSIX vrátí od Cancel = true doby, kdy byl zpracován signál POSIX.
  2. Spouštěcí Main kód se spustí znovu a řekne hostiteli, kterému StopAsync()se zase zastaví všechny hostované služby a vyvolá všechny ostatní zastavené události.
  3. Nakonec se ukončí, WaitForShutdown což umožní, aby se jakýkoli kód pro vyčištění aplikace spustil, a Main aby metoda mohla elegantně ukončit.

Vypnutí hostitele ve scénářích webového serveru

Existují různé běžné scénáře, kdy řádné vypnutí funguje v Kestrel pro protokoly HTTP/1.1 i HTTP/2 a jak ho můžete nakonfigurovat v různých prostředích pomocí nástroje pro vyrovnávání zatížení, aby se provoz vyprázdnil hladce. I když konfigurace webového serveru přesahuje rozsah tohoto článku, najdete další informace o konfiguraci možností pro dokumentaci k webovému serveru ASP.NET Core Kestrel.

Když hostitel obdrží signál vypnutí (například CTL+C nebo StopAsync), upozorní aplikaci signálem .ApplicationStopping Pokud máte nějaké dlouhotrvající operace, které je potřeba řádně dokončit, měli byste se přihlásit k odběru této události.

Dále volání IServer.StopAsync hostitele s vypršením časového limitu vypnutí, který můžete nakonfigurovat (výchozí 30s). Kestrel (a Http.Sys) zavře své vazby portů a přestane přijímat nová připojení. Sdělí také aktuálním připojením, aby přestala zpracovávat nové požadavky. V případě HTTP/2 a HTTP/3 se klientovi odešle předběžná GOAWAY zpráva. U protokolu HTTP/1.1 zastaví smyčku připojení, protože požadavky se zpracovávají v pořadí. Služba IIS se chová odlišně tím, že odmítne nové požadavky se stavovým kódem 503.

Aktivní požadavky budou mít do vypršení časového limitu vypnutí. Pokud jsou všechny dokončeny před vypršením časového limitu, server vrátí řízení hostiteli dříve. Pokud vyprší časový limit, čekající připojení a požadavky jsou vynuceně přerušeny, což může způsobit chyby v protokolech a klientech.

Důležité informace o nástroji pro vyrovnávání zatížení

Pokud chcete zajistit hladký přechod klientů do nového cíle při práci s nástrojem pro vyrovnávání zatížení, můžete postupovat takto:

  • Vyvolání nové instance a zahájení vyrovnávání provozu (pro účely škálování už můžete mít několik instancí).
  • Zakažte nebo odeberte starou instanci v konfiguraci nástroje pro vyrovnávání zatížení, aby přestal přijímat nový provoz.
  • Signalizovat starou instanci, aby se vypnula.
  • Počkejte, až se vyprázdní nebo vyprší časový limit.

Viz také