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.
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.
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 WebHost
uitbreiden, 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 IHostedService
kunt 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.
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
Een geplande taak maken in ASP.NET Core/Standard 2.0
https://blog.maartenballiauw.be/post/2017/08/01/building-a-scheduled-cache-updater-in-aspnet-core-2.htmlIHostedService implementeren in ASP.NET Core 2.0
https://www.stevejgordon.co.uk/asp-net-core-2-ihostedserviceGenericHost-voorbeeld met ASP.NET Core 2.1
https://github.com/aspnet/Hosting/tree/release/2.1/samples/GenericHostSample