Implementera bakgrundsuppgifter i mikrotjänster med IHostedService och klassen BackgroundService
Dricks
Det här innehållet är ett utdrag från eBook, .NET Microservices Architecture for Containerized .NET Applications, tillgängligt på .NET Docs eller som en kostnadsfri nedladdningsbar PDF som kan läsas offline.
Bakgrundsaktiviteter och schemalagda jobb kan behöva användas i alla program, oavsett om de följer arkitekturmönstret för mikrotjänster eller inte. Skillnaden när du använder en arkitektur för mikrotjänster är att du kan implementera bakgrundsaktiviteten i en separat process/container för värdtjänster så att du kan skala ned den/upp baserat på dina behov.
Från en allmän synvinkel i .NET kallade vi den här typen av uppgifter värdbaserade tjänster, eftersom de är tjänster/logik som du är värd för i din värd/program/mikrotjänst. Observera att i det här fallet innebär den värdbaserade tjänsten helt enkelt en klass med bakgrundsaktivitetslogik.
Sedan .NET Core 2.0 tillhandahåller ramverket ett nytt gränssnitt med namnet IHostedService som hjälper dig att enkelt implementera värdbaserade tjänster. Den grundläggande tanken är att du kan registrera flera bakgrundsaktiviteter (värdbaserade tjänster) som körs i bakgrunden medan webbvärden eller värden körs, enligt bilden 6–26.
Bild 6-26. Använda IHostedService i en WebHost jämfört med en värd
ASP.NET Core 1.x och 2.x stöd IWebHost
för bakgrundsprocesser i webbappar. .NET Core 2.1 och senare versioner stöder IHost
bakgrundsprocesser med vanliga konsolappar. Observera skillnaden mellan WebHost
och Host
.
En WebHost
(basklass som implementerar IWebHost
) i ASP.NET Core 2.0 är den infrastrukturartefakt som du använder för att tillhandahålla HTTP-serverfunktioner i processen, till exempel när du implementerar en MVC-webbapp eller webb-API-tjänst. Det ger all den nya infrastrukturens godhet i ASP.NET Core, så att du kan använda beroendeinmatning, infoga mellanprogram i pipelinen för begäran och liknande. Använder WebHost
samma sak IHostedServices
för bakgrundsaktiviteter.
En Host
(implementering av IHost
basklass ) introducerades i .NET Core 2.1. I grund och botten Host
kan du ha en liknande infrastruktur än vad du har med WebHost
(beroendeinmatning, värdbaserade tjänster osv.), men i det här fallet vill du bara ha en enkel och lättare process som värd, utan något relaterat till MVC, webb-API eller HTTP-serverfunktioner.
Därför kan du välja och antingen skapa en specialiserad värdprocess med IHost
för att hantera värdbaserade tjänster och inget annat, en sådan mikrotjänst som görs bara för att vara värd IHostedServices
för , eller så kan du också utöka en befintlig ASP.NET Core WebHost
, till exempel en befintlig ASP.NET Core Web API eller MVC-app.
Varje metod har fördelar och nackdelar beroende på dina affärs- och skalbarhetsbehov. Den nedersta raden är i princip att om dina bakgrundsuppgifter inte har något att göra med HTTP (IWebHost
) bör du använda IHost
.
Registrera värdbaserade tjänster i webvärden eller värden
Nu ska vi öka detaljnivån ytterligare i IHostedService
gränssnittet eftersom dess användning är ganska lik i en WebHost
eller i en Host
.
SignalR är ett exempel på en artefakt som använder värdbaserade tjänster, men du kan också använda den för mycket enklare saker som:
- En bakgrundsaktivitet söker efter ändringar i en databas.
- En schemalagd uppgift som uppdaterar vissa cacheminnen med jämna mellanrum.
- En implementering av QueueBackgroundWorkItem som gör att en uppgift kan köras i en bakgrundstråd.
- Bearbeta meddelanden från en meddelandekö i bakgrunden av en webbapp samtidigt som du delar vanliga tjänster som
ILogger
. - En bakgrundsaktivitet började med
Task.Run()
.
Du kan i princip avlasta någon av dessa åtgärder till en bakgrundsaktivitet som implementerar IHostedService
.
Hur du lägger till en eller flera IHostedServices
i din WebHost
eller Host
är genom att registrera dem via AddHostedService tilläggsmetoden i en ASP.NET Core WebHost
(eller i en Host
i .NET Core 2.1 och senare). I grund och botten måste du registrera värdbaserade tjänster inom programstart i Program.cs.
//Other DI registrations;
// Register Hosted Services
builder.Services.AddHostedService<GracePeriodManagerService>();
builder.Services.AddHostedService<MyHostedServiceB>();
builder.Services.AddHostedService<MyHostedServiceC>();
//...
I den koden är den GracePeriodManagerService
värdbaserade tjänsten verklig kod från mikrotjänsten Ordering Business i eShopOnContainers, medan de andra två bara är två ytterligare exempel.
Körningen IHostedService
av bakgrundsaktiviteten samordnas med programmets livslängd (värd eller mikrotjänst för den delen). Du registrerar uppgifter när programmet startar och du har möjlighet att utföra vissa åtgärder eller rensa upp när programmet stängs av.
Utan att använda IHostedService
kan du alltid starta en bakgrundstråd för att köra en uppgift. Skillnaden är exakt vid appens avstängningstid när den tråden helt enkelt skulle avlivas utan att ha möjlighet att köra graciösa rensningsåtgärder.
Gränssnittet IHostedService
När du registrerar en IHostedService
anropar StartAsync()
.NET metoderna och StopAsync()
av din IHostedService
typ under programstart respektive stopp. Mer information finns i IHostedService-gränssnittet.
Som du kan föreställa dig kan du skapa flera implementeringar av IHostedService och registrera var och en av dem i Program.cs, som du visade tidigare. Alla dessa värdbaserade tjänster startas och stoppas tillsammans med programmet/mikrotjänsten.
Som utvecklare ansvarar du för att hantera stoppåtgärden för dina tjänster när StopAsync()
metoden utlöses av värden.
Implementera IHostedService med en anpassad värdbaserad tjänstklass som härleds från backgroundservice-basklassen
Du kan gå vidare och skapa din anpassade värdbaserade tjänstklass från grunden och implementera IHostedService
, som du behöver göra när du använder .NET Core 2.0 och senare.
Men eftersom de flesta bakgrundsaktiviteter har liknande behov när det gäller hantering av annulleringstoken och andra typiska åtgärder finns det en praktisk abstrakt basklass som du kan härleda från, med namnet BackgroundService
(tillgänglig sedan .NET Core 2.1).
Den här klassen innehåller det huvudsakliga arbete som krävs för att konfigurera bakgrundsaktiviteten.
Nästa kod är den abstrakta BackgroundService-basklassen som implementeras i .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();
}
}
När du härleder från den tidigare abstrakta basklassen, tack vare den ärvda implementeringen, behöver du bara implementera ExecuteAsync()
metoden i din egen anpassade värdbaserade tjänstklass, som i följande förenklade kod från eShopOnContainers som avsöker en databas och publicerar integrationshändelser i Event Bus när det behövs.
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.");
}
.../...
}
I det här specifika fallet för eShopOnContainers kör den en programmetod som kör frågor mot en databastabell och letar efter order med ett specifikt tillstånd och när ändringar tillämpas publicerar den integreringshändelser via händelsebussen (under den kan den använda RabbitMQ eller Azure Service Bus).
Du kan naturligtvis köra andra uppgifter för företagsbakgrund i stället.
Som standard anges annulleringstoken med en tidsgräns på 5 sekunder, även om du kan ändra det värdet när du skapar ditt WebHost
med tillägget UseShutdownTimeout
IWebHostBuilder
för . Det innebär att vår tjänst förväntas avbrytas inom 5 sekunder, annars blir den mer plötsligt avbruten.
Följande kod skulle ändra den tiden till 10 sekunder.
WebHost.CreateDefaultBuilder(args)
.UseShutdownTimeout(TimeSpan.FromSeconds(10))
...
Sammanfattningsklassdiagram
Följande bild visar en visuell sammanfattning av de klasser och gränssnitt som ingår i implementeringen av IHostedServices.
Bild 6-27. Klassdiagram som visar flera klasser och gränssnitt relaterade till IHostedService
Klassdiagram: IWebHost och IHost kan vara värdar för många tjänster som ärver från BackgroundService, som implementerar IHostedService.
Distributionsöverväganden och takeaways
Observera att det sätt på vilket du distribuerar din ASP.NET Core WebHost
eller .NET Host
kan påverka den slutliga lösningen. Om du till exempel distribuerar din WebHost
på IIS eller en vanlig Azure App Service kan värden stängas av på grund av att apppoolen återvinns. Men om du distribuerar värden som en container till en orkestrerare som Kubernetes kan du styra det säkra antalet liveinstanser av värden. Dessutom kan du överväga andra metoder i molnet som är särskilt gjorda för dessa scenarier, till exempel Azure Functions. Om du behöver att tjänsten körs hela tiden och distribueras på en Windows Server kan du använda en Windows-tjänst.
Men även för en WebHost
distribuerad till en apppool finns det scenarier som repopulering eller tömning av programmets minnesinterna cacheminne som fortfarande skulle vara tillämpligt.
Gränssnittet IHostedService
ger ett bekvämt sätt att starta bakgrundsaktiviteter i ett ASP.NET Core-webbprogram (i .NET Core 2.0 och senare versioner) eller i valfri process/värd (med början i .NET Core 2.1 med IHost
). Den största fördelen är den möjlighet du får med den graciösa annulleringen för att rensa koden för dina bakgrundsuppgifter när själva värden stängs av.
Ytterligare resurser
Skapa en schemalagd aktivitet i ASP.NET Core/Standard 2.0
https://blog.maartenballiauw.be/post/2017/08/01/building-a-scheduled-cache-updater-in-aspnet-core-2.htmlImplementera IHostedService i ASP.NET Core 2.0
https://www.stevejgordon.co.uk/asp-net-core-2-ihostedserviceGenericHost-exempel med ASP.NET Core 2.1
https://github.com/aspnet/Hosting/tree/release/2.1/samples/GenericHostSample