Delen via


Achtergrondtaken implementeren in microservices met IHostedService en de klasse BackgroundService

Tip

Deze inhoud is een fragment uit het eBook, .NET Microservices Architecture for Containerized .NET Applications, beschikbaar op .NET Docs of als een gratis downloadbare PDF die offline kan worden gelezen.

.NET Microservices Architecture for Containerized .NET Applications eBook cover thumbnail.

Achtergrondtaken en geplande taken zijn iets wat u mogelijk moet gebruiken in elke toepassing, ongeacht of deze het patroon van de microservicesarchitectuur volgt. Het verschil bij het gebruik van een microservicesarchitectuur is dat u de achtergrondtaak in een afzonderlijk proces/container voor hosting kunt implementeren, zodat u deze omlaag/omhoog kunt schalen op basis van uw behoeften.

Vanuit een algemeen oogpunt hebben we in .NET dit type taken gehoste services genoemd, omdat het services/logica zijn die u host in uw host/toepassing/microservice. In dit geval betekent de gehoste service gewoon een klasse met de achtergrondtaaklogica.

Omdat .NET Core 2.0, biedt het framework een nieuwe interface met de naam IHostedService waarmee u eenvoudig gehoste services kunt implementeren. Het basisidee is dat u meerdere achtergrondtaken (gehoste services) kunt registreren die op de achtergrond worden uitgevoerd terwijl uw webhost of host wordt uitgevoerd, zoals wordt weergegeven in de afbeelding 6-26.

Diagram comparing ASP.NET Core IWebHost and .NET Core IHost.

Afbeelding 6-26. IHostedService gebruiken in een WebHost versus een Host

ASP.NET Core 1.x- en 2.x-ondersteuning IWebHost voor achtergrondprocessen in web-apps. Ondersteuning voor .NET Core 2.1 en latere versies IHost voor achtergrondprocessen met gewone console-apps. Let op het verschil tussen WebHost en Host.

Een WebHost (basisklasse implementeren IWebHost) in ASP.NET Core 2.0 is het infrastructuurartefact dat u gebruikt om HTTP-serverfuncties te bieden aan uw proces, zoals wanneer u een MVC-web-app of web-API-service implementeert. Het biedt alle nieuwe infrastructuurgoedheid in ASP.NET Core, zodat u afhankelijkheidsinjectie kunt gebruiken, middlewares kunt invoegen in de aanvraagpijplijn en vergelijkbaar. Deze WebHost worden op dezelfde wijze IHostedServices gebruikt voor achtergrondtaken.

Er is een Host (basisklasse implementeren IHost) geïntroduceerd in .NET Core 2.1. In principe Host kunt u een vergelijkbare infrastructuur hebben dan wat u hebt WebHost (afhankelijkheidsinjectie, gehoste services, enzovoort), maar in dit geval wilt u gewoon een eenvoudig en lichter proces hebben als host, met niets met betrekking tot MVC-, web-API- of HTTP-serverfuncties.

Daarom kunt u een gespecialiseerd hostproces kiezen en maken voor IHost het afhandelen van de gehoste services en niets anders, zoals een microservice die alleen is gemaakt voor het hosten van de IHostedServices, of u kunt ook een bestaande ASP.NET Core WebHostuitbreiden, zoals een bestaande ASP.NET Core Web-API of MVC-app.

Elke benadering heeft voor- en nadelen, afhankelijk van uw bedrijfs- en schaalbaarheidsbehoeften. De bottom line is in feite dat als uw achtergrondtaken niets te maken hebben met HTTP (IWebHost) die u moet gebruiken IHost.

Gehoste services registreren in uw WebHost of Host

Laten we verder inzoomen op de IHostedService interface, omdat het gebruik ervan vergelijkbaar is in een WebHost of in een Host.

SignalR is een voorbeeld van een artefact met gehoste services, maar u kunt dit ook gebruiken voor veel eenvoudigere zaken zoals:

  • Een achtergrondtaak peilt een database die op zoek is naar wijzigingen.
  • Een geplande taak die regelmatig een cache bijwerkt.
  • Een implementatie van QueueBackgroundWorkItem waarmee een taak kan worden uitgevoerd op een achtergrondthread.
  • Berichten van een berichtenwachtrij op de achtergrond van een web-app verwerken terwijl u algemene services zoals ILogger.
  • Er is een achtergrondtaak gestart met Task.Run().

U kunt deze acties in principe offloaden naar een achtergrondtaak die wordt geïmplementeerd IHostedService.

De manier waarop u een of meer toevoegt aan uw of door IHostedServices ze te registreren via de AddHostedService extensiemethode in een ASP.NET Core WebHost (of in een Host .NET Core 2.1 en Host hoger).WebHost In principe moet u de gehoste services registreren bij het opstarten van de toepassing in Program.cs.

//Other DI registrations;

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

In die code is de GracePeriodManagerService gehoste service echte code van de Order business microservice in eShopOnContainers, terwijl de andere twee slechts twee extra voorbeelden zijn.

De IHostedService uitvoering van de achtergrondtaak wordt gecoördineerd met de levensduur van de toepassing (host of microservice). U registreert taken wanneer de toepassing wordt gestart en u hebt de mogelijkheid om een goede actie uit te voeren of op te schonen wanneer de toepassing wordt afgesloten.

Zonder gebruik IHostedServicekunt u altijd een achtergrondthread starten om een taak uit te voeren. Het verschil is precies op de afsluittijd van de app wanneer die thread gewoon wordt gedood zonder dat u de mogelijkheid hebt om graceful opschoonacties uit te voeren.

De IHostedService-interface

Wanneer u een IHostedService.NET registreert, worden de StartAsync() en StopAsync() methoden van uw type aangeroepen tijdens het starten en stoppen van de IHostedService toepassing. Zie de interface IHostedService voor meer informatie.

Zoals u zich kunt voorstellen, kunt u meerdere implementaties van IHostedService maken en ze registreren in Program.cs, zoals eerder wordt weergegeven. Alle gehoste services worden gestart en gestopt samen met de toepassing/microservice.

Als ontwikkelaar bent u verantwoordelijk voor het afhandelen van de stopactie van uw services wanneer StopAsync() de methode wordt geactiveerd door de host.

IHostedService implementeren met een aangepaste gehoste serviceklasse die is afgeleid van de baseklasse BackgroundService

U kunt uw aangepaste gehoste serviceklasse helemaal zelf maken en de IHostedService, zoals u moet doen wanneer u .NET Core 2.0 en hoger gebruikt, implementeren.

Aangezien de meeste achtergrondtaken echter vergelijkbare behoeften hebben met betrekking tot het beheer van annuleringstokens en andere typische bewerkingen, is er een handige abstracte basisklasse die u kunt afleiden van, benoemd BackgroundService (beschikbaar sinds .NET Core 2.1).

Deze klasse biedt het belangrijkste werk dat nodig is om de achtergrondtaak in te stellen.

De volgende code is de abstracte Base-klasse BackgroundService, zoals geïmplementeerd in .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();
    }
}

Wanneer u afgeleid bent van de vorige abstracte basisklasse, hoeft u alleen de ExecuteAsync() methode te implementeren in uw eigen aangepaste gehoste serviceklasse, zoals in de volgende vereenvoudigde code van eShopOnContainers die een database peilt en integratiegebeurtenissen publiceert in Event Bus wanneer dat nodig is.

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

            try {
                    await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
                }
            catch (TaskCanceledException exception) {
                    _logger.LogCritical(exception, "TaskCanceledException Error", exception.Message);
                }
        }

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

    .../...
}

In dit specifieke geval voor eShopOnContainers voert het een toepassingsmethode uit die een query uitvoert op een databasetabel die op orders zoekt met een specifieke status en bij het toepassen van wijzigingen, worden integratie-gebeurtenissen gepubliceerd via de gebeurtenisbus (eronder kan RabbitMQ of Azure Service Bus worden gebruikt).

U kunt natuurlijk ook elke andere zakelijke achtergrondtaak uitvoeren.

Standaard wordt het annuleringstoken ingesteld met een time-out van 5 seconden, hoewel u deze waarde kunt wijzigen bij het bouwen van uw WebHost met behulp van de UseShutdownTimeout extensie van de IWebHostBuilder. Dit betekent dat onze service naar verwachting binnen 5 seconden wordt geannuleerd, anders wordt deze sneller gedood.

Met de volgende code wordt die tijd gewijzigd in 10 seconden.

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

Overzichtsklassediagram

In de volgende afbeelding ziet u een visueel overzicht van de klassen en interfaces die betrokken zijn bij het implementeren van IHostedServices.

Diagram showing that IWebHost and IHost can host many services.

Afbeelding 6-27. Klassediagram met de meerdere klassen en interfaces met betrekking tot IHostedService

Klassediagram: IWebHost en IHost kunnen veel services hosten, die overnemen van BackgroundService, waarmee IHostedService wordt geïmplementeerd.

Overwegingen en aandachtspunten bij de implementatie

Het is belangrijk te weten dat de manier waarop u uw ASP.NET Core WebHost of .NET Host implementeert, van invloed kan zijn op de uiteindelijke oplossing. Als u bijvoorbeeld uw WebHost on IIS of een reguliere Azure-app Service implementeert, kan uw host worden afgesloten vanwege prullenbakken van de app-pool. Maar als u uw host als een container implementeert in een orchestrator zoals Kubernetes, kunt u het verzekerde aantal live-exemplaren van uw host beheren. Daarnaast kunt u andere benaderingen in de cloud overwegen die speciaal zijn gemaakt voor deze scenario's, zoals Azure Functions. Als u wilt dat de service voortdurend wordt uitgevoerd en op een Windows Server wordt geïmplementeerd, kunt u een Windows-service gebruiken.

Maar zelfs voor een WebHost geïmplementeerde app-pool zijn er scenario's zoals het opnieuw vullen of leegmaken van de cache in het geheugen van de toepassing die nog steeds van toepassing is.

De IHostedService interface biedt een handige manier om achtergrondtaken te starten in een ASP.NET Core-webtoepassing (in .NET Core 2.0 en latere versies) of in een proces/host (beginnend in .NET Core 2.1 met IHost). Het belangrijkste voordeel is de kans dat u krijgt met de graceful annulering om de code van uw achtergrondtaken op te schonen wanneer de host zelf wordt afgesloten.

Aanvullende bronnen