.NET Általános gazdagép

Ebben a cikkben megismerheti a Microsoft.Extensions.Hosting NuGet csomagban elérhető .NET Generic Host konfigurálásának és létrehozásának különböző mintáit. A .NET Generic Host felelős az alkalmazás indításáért és élettartam-kezeléséért. A Worker Service-sablonok létrehoznak egy .NET Generic Hostot, vagyis egy általános gazdagépet. HostApplicationBuilder A Generikus gazdagép más típusú .NET-alkalmazásokkal, például konzol alkalmazásokkal is használható.

A gazdagép olyan objektum, amely az alkalmazás erőforrásait és élettartam-funkcióit foglalja magában, például:

  • Függőséginjektálás (DI)
  • Naplózás
  • Konfiguráció
  • Alkalmazásleállítás
  • IHostedService Megvalósítások

Amikor egy gazdagép elindul, meghívja a szolgáltatástároló üzemeltetett szolgáltatások gyűjteményében regisztrált összes IHostedService.StartAsync implementáció IHostedService metódusát. Egy háttérszolgáltatás alkalmazásban minden IHostedService olyan implementációra, amely BackgroundService példányokat tartalmaz, meghívják a BackgroundService.ExecuteAsync metódusokat.

Az alkalmazás összes egymástól függő erőforrásának egy objektumba való belefogalmazásának fő oka az élettartam-kezelés: az alkalmazásindítás és a kecses leállítás vezérlése.

Hoszt építő beállításai

A .NET két módszert kínál a Generic Host konfigurálásához és létrehozásához:

  • IHostApplicationBuilder (Host.CreateApplicationBuilder): A .NET 6-ban bevezetett megközelítés lineáris, tulajdonságalapú konfigurációs stílust használ. A szolgáltatások, a konfiguráció és a naplózás úgy konfigurálható, hogy közvetlenül hozzáfér a szerkesztőobjektum tulajdonságaihoz (például builder.Services: , builder.Configuration). Ez a megközelítés új projektekhez ajánlott, és az alapértelmezett az aktuális .NET-sablonokban.

  • IHostBuilder (Host.CreateDefaultBuilder): Ez a hagyományos visszahívásalapú megközelítés, ahol a konfiguráció láncolt kiterjesztési módszerekkel történik (például ConfigureServices: ; ConfigureAppConfiguration). Bár teljes mértékben támogatott, ez az örökölt megközelítés a legjobban a meglévő kódbázisokkal való kompatibilitás fenntartásához működik a legjobban.

Mindkét megközelítés ugyanazt az alapvető funkciót és alapértelmezett viselkedést biztosítja. Válasszon IHostApplicationBuilder új projekteket, hogy igazodjanak a modern .NET-mintákhoz és az egyszerűbb konfigurációs kódhoz. Használja a IHostBuilder-t meglévő alkalmazások karbantartásakor, vagy ha a külső könyvtárak igénylik a visszahívási alapú mintát.

Gazdagép beállítása

A gazdagépet általában kód alapján konfigurálják, készítik és futtatják az Program osztályban. A Main módszer:

A .NET Worker szolgáltatás sablonok a következő kódot hozzák létre egy Generikus gazdagép létrehozásához:

using Example.WorkerService;

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

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

A Worker Services szolgáltatással kapcsolatos további információkért lásd : Worker Services in .NET.

Gazdagépszerkesztő beállításai

A CreateApplicationBuilder módszer:

  • A tartalomgyökér beállítása az GetCurrentDirectory() által visszaadott elérési útra.
  • Betölti a gazdagép konfigurációját a következőből:
    • A környezeti változók DOTNET_ előtaggal kezdődnek.
    • Parancssori argumentumok.
  • Betölti az alkalmazáskonfigurációt a következőből:
    • appsettings.json.
    • appsettings. {Environment}.json.
    • Secret Manager, amikor az alkalmazás a Development környezetben fut.
    • Környezeti változók.
    • Parancssori argumentumok.
  • A következő naplózási szolgáltatókat adja hozzá:
    • Konzol
    • Hibakeresés
    • Eseményforrás
    • EventLog (csak Windows rendszeren futtatva)
  • Engedélyezi a hatókör érvényesítését és a függőségek érvényesítését, ha a környezet meg van adva Development.

Ez HostApplicationBuilder.Services egy Microsoft.Extensions.DependencyInjection.IServiceCollection példány. Ezek a szolgáltatások a függőséginjektálással a regisztrált szolgáltatások feloldásához használt szolgáltatások létrehozására IServiceProvider szolgálnak.

Keretrendszer által biztosított szolgáltatások

Ha az IHostBuilder.Build() vagy a HostApplicationBuilder.Build() szolgáltatást hívod, a következő szolgáltatások automatikusan regisztrálódnak:

További forgatókönyv-alapú gazdagépkészítők

Ha a webre építesz vagy elosztott alkalmazást írsz, szükség lehet egy másik tárhelyépítő használatára. Tekintse meg a további gazdagépszerkesztők alábbi listáját:

IHostApplicationLifetime

Injektálja a IHostApplicationLifetime szolgáltatást bármely osztályba az indítás utáni feladatok és a zökkenőmentes leállítás kezeléséhez. A felületen három tulajdonság található, amely leállítási jogkivonatokat használ az alkalmazás indítás és az alkalmazás leállítás eseménykezelő metódusainak regisztrálásához. Az interfész egy metódust StopApplication() is tartalmaz.

Az alábbi példa egy IHostedService eseményregisztrációt regisztráló IHostedLifecycleService és IHostApplicationLifetime implementáció:

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

A Worker Service sablon módosítható a ExampleHostedService megvalósítás hozzáadásához:

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

Az alkalmazás a következő mintakimenetet írja:

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

A kimenet az összes különböző életciklus-esemény sorrendjét jeleníti meg:

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

Amikor az alkalmazás leáll, például a Ctrl+C parancs megadásával, a következő események generálódnak:

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

IHostLifetime

Az IHostLifetime implementáció szabályozza, hogy mikor indul el a gazdagép, és mikor áll le. A rendszer az utolsó regisztrált implementációt használja. Microsoft.Extensions.Hosting.Internal.ConsoleLifetime az alapértelmezett IHostLifetime implementáció. A leállítás működési mechanizmusairól további információt a Host leállítása témakörben talál.

Az IHostLifetime interfész tesz elérhetővé egy IHostLifetime.WaitForStartAsync metódust, amely a IHost.StartAsync kezdetekor meghívódik, és megvárja a befejezést, mielőtt folytatná. Ezzel késleltetheti az indítást, amíg egy külső esemény nem jelzi.

Emellett a IHostLifetime felület egy IHostLifetime.StopAsync metódust is elérhetővé tesz, amelyet a IHost.StopAsync-ből hívnak meg annak jelzésére, hogy a host leáll, és elérkezett az ideje a kikapcsolásnak.

IHostEnvironment

IHostEnvironment A szolgáltatás injektálása egy osztályba a következő beállításokkal kapcsolatos információk lekéréséhez:

A szolgáltatás emellett IHostEnvironment az alábbi bővítménymetódusok segítségével teszi lehetővé a környezet kiértékelését:

Gazdagép konfigurációja

A gazdagépkonfiguráció az IHostEnvironment implementáció tulajdonságainak konfigurálására szolgál.

A gazdagép konfigurációja a HostApplicationBuilderSettings.Configuration tulajdonságban érhető el, a környezet implementációja pedig a IHostApplicationBuilder.Environment tulajdonságban érhető el. A kiszolgáló konfigurálásához lépjen a Configuration tulajdonsághoz, és hívja meg az elérhető kiterjesztési metódusokat.

Gazdagépkonfiguráció hozzáadásához vegye figyelembe a következő példát:

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

A fenti kód a következőket végzi el:

  • A tartalomgyökér beállítása az GetCurrentDirectory() által visszaadott elérési útra.
  • Betölti a gazdagép konfigurációját a következőből:
    • hostsettings.json.
    • A környezeti változók PREFIX_ előtaggal kezdődnek.
    • Parancssori argumentumok.

Alkalmazáskonfiguráció

Az alkalmazáskonfiguráció a nyilvános IHostApplicationBuilder.Configuration tulajdonságon keresztül érhető el. Ez a tulajdonság lehetővé teszi a felhasználók számára a meglévő konfiguráció olvasását vagy módosítását az elérhető bővítménymetelyek használatával.

További információ: Konfiguráció a .NET-ben.

Kiszolgáló leállítása

Számos módja van annak, ahogyan egy üzemeltetett folyamat leállítható. A üzemeltetett folyamatokat leggyakrabban a következő módokon lehet leállítani:

Az üzemeltetési kód nem felelős ezekért a forgatókönyvekért. A folyamat tulajdonosának ugyanúgy kell velük foglalkoznia, mint bármely más alkalmazásnak. A üzemeltetett szolgáltatásfolyamatok számos más módon is leállíthatók:

  • Ha ConsoleLifetime használják (UseConsoleLifetime), a következő jeleket figyeli, és megpróbálja kecsesen leállítani a gazdagépet.
    • SIGINT (vagy Ctrl+C).
    • SIGQUIT (vagy Ctrl+BREAK Windowson, Ctrl+\ Unixon).
    • SIGTERM (amit más alkalmazások küldenek, például docker stop).
  • Ha az alkalmazás hív Environment.Exit.

A beépített üzemeltetési logika kezeli ezeket a forgatókönyveket, különösen az osztályt ConsoleLifetime . ConsoleLifetime próbálja kezelni a SIGINT, SIGQUIT és SIGTERM "leállítási" jelet, hogy lehetővé tegye az alkalmazás sima leállását.

A .NET 6 előtt nem volt mód arra, hogy a .NET-kód kecsesen kezelje a SIGTERM-et. A korlátozás megkerüléséhez a ConsoleLifetime előfizetne a System.AppDomain.ProcessExit szolgáltatásra. Amikor ProcessExit emelkedett, ConsoleLifetime jelezte a gazdagépnek, hogy állítsa le és blokkolja a ProcessExit szálat, várva a gazdagép leállását.

A folyamat kilépési kezelője lehetővé tenné az alkalmazás takarítási kódjának futtatását – például a IHost.StopAsync metódusban lévő HostingAbstractionsHostExtensions.Run és a Main utáni kódot.

Ezzel a megközelítéssel azonban más problémák is felmerültek, mert nem csak a SIGTERM okozta a ProcessExit problémát. SIGTERM akkor is aktiválódik, amikor az alkalmazáskód meghívja a Environment.Exit. Environment.Exit nem egy elegáns módja annak, hogy leállítsuk egy folyamatot a Microsoft.Extensions.Hosting alkalmazásmodellben. Triggerálja a ProcessExit eseményt, majd kilép a folyamatból. A Main metódus vége nem lesz végrehajtva. A háttér- és előtérszálak leállnak, a finally blokkok pedig nem lesznek végrehajtva.

Mivel ConsoleLifetime blokkolva volt, miközben a gazdagép leállására várt, ez a viselkedés holtpontokhoz vezetett abból kifolyólag, hogy ProcessExit szintén blokkolva volt a hívására várva. Emellett, mivel a SIGTERM-kezelés megpróbálta rendesen leállítani a folyamatot, ConsoleLifetime a ExitCode-t 0-re állította, ami felülírta a felhasználónak átadott kilépési kódot .

A .NET 6-ban a POSIX-jelek támogatottak és kezelhetők. A ConsoleLifetime a SIGTERM-et elegánsan kezeli, és nem vesz részt a folyamatban, amikor a Environment.Exit hívásra kerül sor.

Tipp.

A .NET 6+ ConsoleLifetime esetében már nincs logikája a forgatókönyv Environment.Exitkezeléséhez. Azok az alkalmazások, amelyek Environment.Exit-t használnak, és tisztítási logikára van szükségük, maguk is előfizethetnek ProcessExit-re. A tárhelyszolgáltató ezekben a forgatókönyvekben többé nem próbálja meg fokozatosan leállítani a gazdagépet.

Ha az alkalmazás üzemeltetést használ, és elegánsan le szeretné állítani a gazdát, akkor ahelyett a IHostApplicationLifetime.StopApplication hívhatja a Environment.Exit.

Leállítási folyamat üzemeltetése

Az alábbi szekvenciadiagram bemutatja, hogyan kezelik a jeleket belsőleg az üzemeltetési kódban. A felhasználók többségének nem kell megértenie ezt a folyamatot. A mély megértést igénylő fejlesztők számára azonban egy jó vizualizáció segíthet az első lépésekben.

A gazdagép elindítása után, amikor egy felhasználó hívást indít Run vagy WaitForShutdown, egy kezelő lesz regisztrálva IApplicationLifetime.ApplicationStopping-re. A végrehajtás szünetel WaitForShutdown-ben, és vár az ApplicationStopping eseményre. A Main metódus nem tér vissza azonnal, és az alkalmazás addig fut, amíg RunWaitForShutdown vissza nem tér.

Amikor a rendszer jelet küld a folyamatnak, a következő sorrendet indítja el:

Tárhely leállítási folyamat diagramja.

  1. A vezérlés a ConsoleLifetime-ból a ApplicationLifetime-ba áramlik, hogy kiváltsa a ApplicationStopping eseményt. Ez jelzi, hogy WaitForShutdownAsync feloldja a Main végrehajtási kód blokkolását. Amíg a POSIX-jel kezelése befejeződik, a POSIX jelkezelő visszatér Cancel = true.
  2. A Main végrehajtási kód újra végrehajtja és utasítja a gazdagépet StopAsync()-re, amely leállítja az összes üzemeltetett szolgáltatást, és előidézi a többi leállított eseményt.
  3. Végül a WaitForShutdown kilép, lehetővé téve az alkalmazáshoz tartozó tisztító kódok végrehajtását, és hogy a Main metódus kecsesen befejeződjön.

Webszerver leállítása szerverforgatókönyvekben

A Kestrelben számos más gyakori forgatókönyv is létezik a HTTP/1.1 és a HTTP/2 protokollok esetében, és hogyan konfigurálható különböző környezetekben egy terheléselosztóval a forgalom zökkenőmentes kiürítéséhez. Bár a webkiszolgáló konfigurációja meghaladja a jelen cikk hatókörét, további információt a ASP.NET Core Kestrel webkiszolgáló dokumentációjának konfigurálási lehetőségeiről talál.

Amikor a házigép leállítási jelet kap (például Ctrl+C vagy StopAsync), értesíti az alkalmazást a ApplicationStoppingjelzésével. Javasolt feliratkozni erre az eseményre, ha elnyújtott műveletekkel rendelkezik, amelyeknek zökkenőmentesen befejeződjenek.

Ezután a gazdagép meghívja IServer.StopAsync egy konfigurálható leállítási időkorláttal (alapértelmezett 30 másodperc). A Kestrel (és a Http.Sys) zárja be a portkötéseket, és hagyja abba az új kapcsolatok elfogadását. Azt is közlik az aktuális kapcsolatokkal, hogy ne dolgozzanak fel új kéréseket. HTTP/2 és HTTP/3 esetén a rendszer előzetes GOAWAY üzenetet küld az ügyfélnek. HTTP/1.1 esetén leállítja a kapcsolati hurkot, mert a kérések feldolgozása sorrendben történik. Az IIS másképp viselkedik, ha elutasítja az új kéréseket egy 503-at tartalmazó állapotkóddal.

Az aktív kérelmeknek a leállítási időkeret lejártáig van idejük a befejezésre. Ha mindegyik befejeződik az időtúllépés előtt, a kiszolgáló hamarabb visszaadja az irányítást a gazdagépnek. Ha az időtúllépés lejár, a függőben lévő kapcsolatok és kérések erőteljesen megszakadnak, ami hibákat okozhat a naplókban és az ügyfelekben.

A terheléselosztó szempontjai

A terheléselosztóval végzett munka során az ügyfelek zökkenőmentes átvitele új célhelyre az alábbi lépések végrehajtásával biztosítható:

  • Hozza létre az új példányt, és kezdje el a forgalom kiegyensúlyozását (skálázási célokra már lehet, hogy több példánya is van).
  • Tiltsa le vagy távolítsa el a régi példányt a terheléselosztó konfigurációjában, hogy ne kapjon új forgalmat.
  • Jelezzen a régi példánynak, hogy állítsa le.
  • Várja meg, amíg kimerül vagy időtúllép.

Lásd még