Implementowanie zadań w tle w mikrousług za pomocą usługi IHostedService i klasy BackgroundService
Napiwek
Ta zawartość jest fragmentem książki eBook, architektury mikrousług platformy .NET dla konteneryzowanych aplikacji platformy .NET dostępnych na platformie .NET Docs lub jako bezpłatnego pliku PDF, który można odczytać w trybie offline.
Zadania w tle i zaplanowane zadania mogą być potrzebne do użycia w dowolnej aplikacji, niezależnie od tego, czy są zgodne ze wzorcem architektury mikrousług. Różnica w przypadku korzystania z architektury mikrousług polega na tym, że można zaimplementować zadanie w tle w osobnym procesie/kontenerze do hostowania, aby można było skalować je w dół/w górę w zależności od potrzeb.
Z ogólnego punktu widzenia na platformie .NET nazwaliśmy tego typu zadaniami Hostowane usługi, ponieważ są to usługi/logika hostowane w ramach hosta/aplikacji/mikrousługi. Należy pamiętać, że w tym przypadku hostowana usługa po prostu oznacza klasę z logiką zadań w tle.
Ponieważ platforma .NET Core 2.0 udostępnia nowy interfejs o nazwie IHostedService ułatwia implementowanie hostowanych usług. Podstawową ideą jest to, że można zarejestrować wiele zadań w tle (hostowanych usług), które są uruchamiane w tle podczas działania hosta internetowego lub hosta, jak pokazano na obrazie 6–26.
Rysunek 6–26. Używanie usługi IHostedService na hoście sieci Web a hosta
ASP.NET Core 1.x i 2.x obsługują IWebHost
procesy w tle w aplikacjach internetowych. Platforma .NET Core 2.1 i nowsze wersje obsługują IHost
procesy w tle z aplikacjami konsoli zwykłych. Zwróć uwagę na różnicę między WebHost
i Host
.
A (implementacja WebHost
klasy IWebHost
bazowej ) w ASP.NET Core 2.0 to artefakt infrastruktury używany do udostępniania funkcji serwera HTTP procesowi, na przykład podczas implementowania aplikacji internetowej MVC lub usługi internetowego interfejsu API. Zapewnia ona całą nową dobroć infrastruktury w ASP.NET Core, umożliwiając korzystanie z wstrzykiwania zależności, wstawiania oprogramowania pośredniczącego w potoku żądania i podobnych. Te WebHost
zastosowania są takie same IHostedServices
w przypadku zadań w tle.
A Host
(implementacja IHost
klasy bazowej ) została wprowadzona w programie .NET Core 2.1. Zasadniczo element umożliwia korzystanie z WebHost
podobnej infrastruktury niż to, Host
co masz (iniekcja zależności, hostowane usługi itp.), ale w tym przypadku po prostu chcesz mieć prosty i lżejszy proces jako host, z niczym związanymi z funkcjami MVC, interfejsu API sieci Web lub serwera HTTP.
W związku z tym można wybrać i utworzyć wyspecjalizowany proces hosta w IHost
celu obsługi hostowanych usług i nic innego, takiej mikrousługi wykonanej tylko do hostowania IHostedServices
, lub możesz alternatywnie rozszerzyć istniejący ASP.NET Core WebHost
, taki jak istniejący ASP.NET Core Web API lub aplikacja MVC.
Każde podejście ma zalety i wady w zależności od potrzeb biznesowych i skalowalności. Najważniejsze jest to, że jeśli zadania w tle nie mają nic wspólnego z protokołem HTTP (IWebHost
), należy użyć polecenia IHost
.
Rejestrowanie hostowanych usług na hoście sieci Web lub hoście
Przejdźmy do szczegółów interfejsu IHostedService
, ponieważ jego użycie jest dość podobne w elemecie WebHost
lub w elemecie Host
.
SignalR to jeden z przykładów artefaktu korzystającego z hostowanych usług, ale można go również użyć w znacznie prostszych sytuacjach, takich jak:
- Zadanie w tle sonduje bazę danych, wyszukując zmiany.
- Zaplanowane zadanie okresowo aktualizuje część pamięci podręcznej.
- Implementacja elementu QueueBackgroundWorkItem, która umożliwia wykonanie zadania w wątku w tle.
- Przetwarzanie komunikatów z kolejki komunikatów w tle aplikacji internetowej podczas udostępniania typowych usług, takich jak
ILogger
. - Zadanie w tle rozpoczęte z
Task.Run()
.
W zasadzie można odciążyć dowolną z tych akcji do zadania w tle, które implementuje IHostedService
element .
Sposób dodawania jednego lub wielu IHostedServices
do elementu WebHost
lub Host
polega na zarejestrowaniu ich za pomocą AddHostedService metody rozszerzenia w środowisku ASP.NET Core WebHost
(lub w programie .NET Core 2.1 lub nowszym Host
). Zasadniczo musisz zarejestrować hostowane usługi w ramach uruchamiania aplikacji w Program.cs.
//Other DI registrations;
// Register Hosted Services
builder.Services.AddHostedService<GracePeriodManagerService>();
builder.Services.AddHostedService<MyHostedServiceB>();
builder.Services.AddHostedService<MyHostedServiceC>();
//...
W tym kodzie hostowana GracePeriodManagerService
usługa jest prawdziwym kodem z mikrousługi biznesowej Ordering w eShopOnContainers, podczas gdy pozostałe dwa są tylko dwoma dodatkowymi przykładami.
Wykonywanie IHostedService
zadania w tle jest koordynowane z okresem istnienia aplikacji (hostem lub mikrousługą). Zadania są rejestrowane podczas uruchamiania aplikacji i masz możliwość wykonania pewnych akcji lub czyszczenia, gdy aplikacja zostanie zamknięta.
Bez użycia polecenia IHostedService
zawsze można uruchomić wątek w tle, aby uruchomić dowolne zadanie. Różnica jest dokładnie w czasie zamykania aplikacji, gdy ten wątek zostanie po prostu zabity bez możliwości uruchamiania bezproblemowych akcji oczyszczania.
Interfejs IHostedService
Podczas rejestrowania elementu IHostedService
platforma .NET wywołuje StartAsync()
metody IHostedService
i StopAsync()
typu podczas uruchamiania i zatrzymywania aplikacji odpowiednio. Aby uzyskać więcej informacji, zobacz Interfejs IHostedService.
Jak można sobie wyobrazić, możesz utworzyć wiele implementacji usługi IHostedService i zarejestrować je w Program.cs, jak pokazano wcześniej. Wszystkie te hostowane usługi zostaną uruchomione i zatrzymane wraz z aplikacją/mikrousługą.
Jako deweloper odpowiadasz za obsługę akcji zatrzymywania usług, gdy StopAsync()
metoda jest wyzwalana przez hosta.
Implementowanie usługi IHostedService przy użyciu niestandardowej klasy usługi hostowanej pochodzącej z klasy bazowej BackgroundService
Możesz utworzyć niestandardową klasę usługi hostowanej od podstaw i zaimplementować klasę IHostedService
, co należy zrobić podczas korzystania z platformy .NET Core 2.0 lub nowszej.
Jednak ponieważ większość zadań w tle będzie mieć podobne potrzeby w odniesieniu do zarządzania tokenami anulowania i innych typowych operacji, istnieje wygodna abstrakcyjna klasa bazowa, z której można korzystać, o nazwie BackgroundService
(dostępna od wersji .NET Core 2.1).
Ta klasa zapewnia główną pracę wymaganą do skonfigurowania zadania w tle.
Następny kod jest abstrakcyjną klasą bazową BackgroundService zaimplementowaną na platformie .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();
}
}
Podczas wyprowadzania z poprzedniej abstrakcyjnej klasy bazowej, dzięki tej odziedziczonej implementacji wystarczy zaimplementować ExecuteAsync()
metodę we własnej niestandardowej klasie usługi hostowanej, jak w poniższym uproszczonym kodzie z modułu eShopOnContainers, który sonduje bazę danych i publikuje zdarzenia integracji w magistrali zdarzeń w razie potrzeby.
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.");
}
.../...
}
W tym konkretnym przypadku w przypadku aplikacji eShopOnContainers wykonuje ona metodę aplikacji, która wykonuje zapytanie względem tabeli bazy danych, wyszukując zamówienia o określonym stanie i podczas stosowania zmian, publikuje zdarzenia integracji za pośrednictwem magistrali zdarzeń (poniżej może być używany program RabbitMQ lub Azure Service Bus).
Oczywiście można uruchomić dowolne inne zadanie w tle biznesowym, zamiast tego.
Domyślnie token anulowania jest ustawiany z limitem czasu 5 sekund, chociaż można zmienić wartość podczas kompilowania WebHost
przy użyciu UseShutdownTimeout
rozszerzenia IWebHostBuilder
. Oznacza to, że oczekuje się, że nasza usługa zostanie anulowana w ciągu 5 sekund. W przeciwnym razie zostanie ona nagle zabita.
Poniższy kod zmieni ten czas na 10 sekund.
WebHost.CreateDefaultBuilder(args)
.UseShutdownTimeout(TimeSpan.FromSeconds(10))
...
Diagram klas podsumowania
Na poniższej ilustracji przedstawiono wizualne podsumowanie klas i interfejsów zaangażowanych podczas implementowania IHostedServices.
Rysunek 6–27. Diagram klas przedstawiający wiele klas i interfejsów związanych z usługą IHostedService
Diagram klas: IWebHost i IHost mogą hostować wiele usług, które dziedziczą z usługi BackgroundService, która implementuje usługę IHostedService.
Zagadnienia dotyczące wdrażania i wnioski
Należy pamiętać, że sposób wdrażania ASP.NET Core WebHost
lub .NET Host
może mieć wpływ na ostateczne rozwiązanie. Jeśli na przykład wdrożysz usługę WebHost
IIS lub zwykłą usługę aplikacja systemu Azure, host może zostać zamknięty z powodu recyklingu puli aplikacji. Jeśli jednak wdrażasz host jako kontener w orkiestratorze, na przykład Kubernetes, możesz kontrolować pewną liczbę wystąpień na żywo hosta. Ponadto możesz rozważyć inne podejścia w chmurze, szczególnie w przypadku tych scenariuszy, takich jak usługa Azure Functions. Na koniec, jeśli chcesz, aby usługa mogła być uruchomiona przez cały czas i wdrażana w systemie Windows Server, możesz użyć usługi systemu Windows.
Jednak nawet w przypadku WebHost
wdrożenia w puli aplikacji istnieją scenariusze, takie jak ponowne wypełnianie lub opróżnianie pamięci podręcznej aplikacji w pamięci, które nadal mają zastosowanie.
Interfejs IHostedService
zapewnia wygodny sposób uruchamiania zadań w tle w aplikacji internetowej ASP.NET Core (w wersji .NET Core 2.0 i nowszych) lub w dowolnym procesie/hoście (począwszy od platformy .NET Core 2.1 z IHost
programem ). Jego główną zaletą jest możliwość bezpiecznego anulowania w celu oczyszczenia kodu zadań w tle, gdy sam host jest zamykany.
Dodatkowe zasoby
Tworzenie zaplanowanego zadania w ASP.NET Core/Standard 2.0
https://blog.maartenballiauw.be/post/2017/08/01/building-a-scheduled-cache-updater-in-aspnet-core-2.htmlImplementowanie usługi IHostedService w programie ASP.NET Core 2.0
https://www.stevejgordon.co.uk/asp-net-core-2-ihostedservicePrzykład GenericHost korzystający z ASP.NET Core 2.1
https://github.com/aspnet/Hosting/tree/release/2.1/samples/GenericHostSample