Implementace úloh na pozadí v mikroslužbách s IHostedService a třídou BackgroundService

Úlohy na pozadí a naplánované úlohy můžete potřebovat použít v jakékoli aplikaci bez ohledu na to, jestli se řídí vzorem architektury mikroslužeb. Rozdíl při použití architektury mikroslužeb spočívá v tom, že úlohu na pozadí můžete implementovat v samostatném procesu nebo kontejneru pro hostování, abyste ji mohli podle potřeby škálovat dolů nebo nahoru.

Z obecného hlediska jsme v .NET nazvali tento typ úloh Hostované služby, protože se jedná o služby nebo logiku, které hostíte v rámci hostitele, aplikace nebo mikroslužby. Všimněte si, že v tomto případě hostovaná služba jednoduše znamená třídu s logikou úlohy na pozadí.

Od verze .NET Core 2.0 poskytuje rozhraní nové rozhraní s názvem pomáhá IHostedService snadno implementovat hostované služby. Základní myšlenkou je, že můžete zaregistrovat několik úloh na pozadí (hostovaných služeb), které běží na pozadí, zatímco je váš webový hostitel nebo hostitel spuštěný, jak je znázorněno na obrázku 6–26.

Diagram porovnávající ASP.NET Core IWebHost a .NET Core IHost

Obrázek 6–26. Použití služby IHostedService ve webovém hostiteli vs. hostiteli

ASP.NET Core Podpora procesů na pozadí ve webových aplikacích pro verze 1.x a 2.x IWebHost .NET Core 2.1 a novější verze podporují IHost procesy na pozadí pomocí prostých konzolových aplikací. Všimněte si rozdílu mezi WebHost a Host .

(Základní třída implementující ) v WebHost ASP.NET Core 2.0 je artefakt infrastruktury, který slouží k poskytování funkcí serveru HTTP pro váš proces, například při implementaci webové aplikace MVC nebo služby IWebHost webového rozhraní API. Poskytuje všechny nové vlastnosti infrastruktury v ASP.NET Core, což vám umožní použít injektáž závislostí, vložit middleware do kanálu požadavku a podobně. Používá WebHost je velmi stejný pro úlohy na IHostedServices pozadí.

(Základní Host třída implementující IHost ) byla zavedena v .NET Core 2.1. Objekt v podstatě umožňuje mít podobnou infrastrukturu, jakou máte (injektáž závislostí, hostované služby atd.), ale v tomto případě chcete jako hostitele mít jednoduchý a jednodušší proces bez nic, co by souviset s MVC, webovým rozhraním Host WebHost API nebo funkcemi serveru HTTP.

Proto můžete zvolit a buď vytvořit specializovaný hostitelský proces s pro zpracování hostovaných služeb a nic jiného, například mikroslužbu, která byla provedena pouze pro hostování , nebo můžete případně rozšířit existující ASP.NET Core , například existující webové rozhraní ASP.NET Core API nebo IHost IHostedServices aplikaci WebHost MVC.

Každý přístup má výhody a nevýhody v závislosti na potřebách vaší firmy a škálovatelnosti. Dolní řádek je v podstatě to, že pokud vaše úlohy na pozadí nemají nic společného s HTTP ( IWebHost ), měli byste použít IHost .

Registrace hostovaných služeb ve webovém hostiteli nebo hostiteli

Pojďme přejít k podrobnostem rozhraní, protože jeho použití je v souboru nebo v poměrně IHostedService WebHost Host podobné.

SignalR je jedním z příkladů artefaktu využívajícího hostované služby, ale můžete ho také použít k mnohem jednodušším věcem, jako jsou:

  • Úloha na pozadí dotazující databázi, která hledá změny.
  • Naplánovaná úloha pravidelně aktualizuje mezipaměť.
  • Implementace QueueBackgroundWorkItem, která umožňuje spuštění úlohy ve vlákně na pozadí.
  • Zpracování zpráv z fronty zpráv na pozadí webové aplikace při sdílení běžných služeb, jako je ILogger .
  • Úloha na pozadí spuštěná pomocí Task.Run() .

Jakoukoli z těchto akcí můžete v podstatě změnit na úlohu na pozadí, která implementuje IHostedService .

Způsob, jakým do nebo přidáte jeden nebo více, je jejich registrace prostřednictvím metody rozšíření v ASP.NET Core (nebo v IHostedServices WebHost Host AddHostedService WebHost Host .NET Core 2.1 a vyšší). V podstatě musíte zaregistrovat hostované služby v rámci známé metody třídy , jako v následujícím kódu z ConfigureServices() Startup typického ASP.NET WebHost.

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    //Other DI registrations;

    // Register Hosted Services
    services.AddHostedService<GracePeriodManagerService>();
    services.AddHostedService<MyHostedServiceB>();
    services.AddHostedService<MyHostedServiceC>();
    //...
}

V tomto kódu je hostovaná služba skutečným kódem z podnikové mikroslužby Ordering v eShopOnContainers, zatímco ostatní dva GracePeriodManagerService jsou jenom dvě další ukázky.

Provádění úloh na pozadí se koordinuje s životností aplikace (hostitel nebo IHostedService mikroslužba v této souvislosti). Úlohy zaregistrujete při spuštění aplikace a máte možnost provést při vypnutí aplikace nějaké elegantní akce nebo vyčištění.

Bez použití IHostedService byste mohli vždy spustit vlákno na pozadí, aby bylo možné spustit jakoukoli úlohu. Rozdíl je přesně v době vypnutí aplikace, kdy by toto vlákno bylo jednoduše ukončeno, aniž by bylo možné spustit elegantní akce čištění.

Rozhraní IHostedService

Při registraci bude rozhraní .NET volat metody a vašeho typu během spuštění IHostedService StartAsync() a zastavení StopAsync() IHostedService aplikace. Další podrobnosti najdete v tématu Rozhraní IHostedService.

Jak si dovedete představit, můžete vytvořit více implementací IHostedService a zaregistrovat je v metodě do kontejneru DI, jak ConfigureService() je znázorněno dříve. Všechny tyto hostované služby budou spuštěny a zastaveny společně s aplikací nebo mikroslužbu.

Jako vývojář zodpovídáte za zpracování akce zastavení služeb, když hostitel aktivuje StopAsync() metodu .

Implementace třídy IHostedService s vlastní hostovanou třídou služby odvozenou ze základní třídy BackgroundService

Mohli byste pokračovat a vytvořit vlastní hostovanou třídu služby od začátku a implementovat , jak to musíte udělat při použití IHostedService .NET Core 2.0 a novější.

Vzhledem k tomu, že většina úloh na pozadí bude mít podobné potřeby s ohledem na správu tokenů zrušení a další typické operace, existuje praktická abstraktní základní třída, ze které můžete odvodit pojmenovanou (k dispozici od BackgroundService verze .NET Core 2.1).

Tato třída poskytuje hlavní práci potřebnou k nastavení úlohy na pozadí.

Další kód je abstraktní základní třída BackgroundService implementovaná v .NET.

// Copyright (c) .NET Foundation. Licensed under the Apache License, Version 2.0.
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
    private Task _executingTask;
    private readonly CancellationTokenSource _stoppingCts =
                                                   new CancellationTokenSource();

    protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
        // Store the task we're executing
        _executingTask = ExecuteAsync(_stoppingCts.Token);

        // If the task is completed then return it,
        // this will bubble cancellation and failure to the caller
        if (_executingTask.IsCompleted)
        {
            return _executingTask;
        }

        // Otherwise it's running
        return Task.CompletedTask;
    }

    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
        // Stop called without start
        if (_executingTask == null)
        {
            return;
        }

        try
        {
            // Signal cancellation to the executing method
            _stoppingCts.Cancel();
        }
        finally
        {
            // Wait until the task completes or the stop token triggers
            await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
                                                          cancellationToken));
        }

    }

    public virtual void Dispose()
    {
        _stoppingCts.Cancel();
    }
}

Při odvozování z předchozí abstraktní základní třídy stačí díky této zděděné implementaci implementovat metodu ve vlastní třídě hostované služby, jako v následujícím zjednodušeném kódu z aplikace eShopOnContainers, který v případě potřeby dotazuje databázi a publikuje události integrace do ExecuteAsync() sběrnice událostí.

public class GracePeriodManagerService : BackgroundService
{
    private readonly ILogger<GracePeriodManagerService> _logger;
    private readonly OrderingBackgroundSettings _settings;

    private readonly IEventBus _eventBus;

    public GracePeriodManagerService(IOptions<OrderingBackgroundSettings> settings,
                                     IEventBus eventBus,
                                     ILogger<GracePeriodManagerService> logger)
    {
        // Constructor's parameters validations...
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogDebug($"GracePeriodManagerService is starting.");

        stoppingToken.Register(() =>
            _logger.LogDebug($" GracePeriod background task is stopping."));

        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogDebug($"GracePeriod task doing background work.");

            // This eShopOnContainers method is querying a database table
            // and publishing events into the Event Bus (RabbitMQ / ServiceBus)
            CheckConfirmedGracePeriodOrders();

            await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
        }

        _logger.LogDebug($"GracePeriod background task is stopping.");
    }

    .../...
}

V tomto konkrétním případě pro eShopOnContainers spouští metodu aplikace, která dotazuje tabulku databáze a hledá objednávky s konkrétním stavem a při použití změn publikuje události integrace prostřednictvím sběrnice událostí (pod ní může být rabbitMQ nebo Azure Service Bus).

Místo toho byste samozřejmě mohli spustit jakoukoli jinou úlohu na podnikovém pozadí.

Ve výchozím nastavení je token zrušení nastavený s časovým limitem 5 sekund, i když při sestavování pomocí rozšíření můžete změnit hodnotu WebHost UseShutdownTimeout IWebHostBuilder . To znamená, že se očekává, že se naše služba zruší během 5 sekund, jinak se náhleji zruší.

Následující kód by tento čas změnil na 10 sekund.

WebHost.CreateDefaultBuilder(args)
    .UseShutdownTimeout(TimeSpan.FromSeconds(10))
    ...

Diagram souhrnných tříd

Následující obrázek ukazuje vizuální souhrn tříd a rozhraní, které jsou součástí implementace IHostedServices.

Diagram znázorňující, že IWebHost a IHost mohou hostovat mnoho služeb

Obrázek 6–27. Diagram tříd znázorňující více tříd a rozhraní souvisejících s IHostedService

Diagram tříd: IWebHost a IHost mohou hostovat mnoho služeb, které dědí z BackgroundService, který implementuje IHostedService.

Důležité informace a informace o nasazení

Je důležité si uvědomit, že způsob nasazení ASP.NET Core WebHost nebo .NET Host může mít vliv na konečné řešení. Pokud například nasadíte soubor ve službě IIS nebo běžný Azure App Service, můžete hostitele vypnout kvůli recyklaci WebHost fondu aplikací. Pokud ale nasazujete hostitele jako kontejner do orchestrátoru, jako je Kubernetes, můžete řídit zajištěný počet živých instancí hostitele. Kromě toho byste mohli zvážit další přístupy v cloudu, zejména pro tyto scénáře, jako je Azure Functions. A konečně, pokud potřebujete, aby služba běžela pořád a nasazujete na Windows Server, můžete použít službu Windows Service.

Ale i v případě nasazení do fondu aplikací existují scénáře, jako je naplnění nebo vyprazdnění mezipaměti aplikace v paměti, které by stále WebHost bylo možné použít.

Rozhraní poskytuje pohodlný způsob, jak spustit úlohy na pozadí ve webové aplikaci IHostedService ASP.NET Core (v .NET Core 2.0 a novějších verzích) nebo v jakémkoli procesu nebo hostiteli (počínaje .NET Core 2.1 a IHost ). Jeho hlavní výhodou je příležitost, kterou získáte s elegantním zrušením, abyste vyčistěli kód úloh na pozadí při vypnutí samotného hostitele.

Další zdroje informací