Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Wskazówka
Ta treść jest fragmentem eBooka "Architektura mikrousług .NET dla konteneryzowanych aplikacji .NET", dostępnego na .NET Docs lub jako bezpłatny plik PDF do pobrania i czytania 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. Podstawowa idea to możliwość zarejestrowania wielu zadań w tle (usługi hostingowe), które są uruchamiane w tle, gdy działa host internetowy lub inny host, jak pokazano na rysunku 6–26.
Rysunek 6–26. Używanie usługi IHostedService w WebHost vs. Host
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 prostymi aplikacjami konsolowymi. Zwróć uwagę na różnicę między WebHost
i Host
.
A (WebHost
implementacja klasy bazowej IWebHost
) w ASP.NET Core 2.0 to element infrastruktury używany do udostępniania funkcji serwera HTTP procesowi, na przykład podczas tworzenia aplikacji webowej MVC lub usługi Web API. Zapewnia wszystkie nowe funkcje infrastruktury w ASP.NET Core, umożliwiając korzystanie z mechanizmu wstrzykiwania zależności, dodawania oprogramowania pośredniczącego do kolejki żądań i innych podobnych rozwiązań.
WebHost
używa tych samych IHostedServices
do zadań w tle.
A Host
(klasa bazowa implementująca IHost
) została wprowadzona w .NET Core 2.1. Zasadniczo element Host
umożliwia korzystanie z podobnej infrastruktury do tej, którą masz z WebHost
(iniekcja zależności, hostowane usługi itp.), ale w tym przypadku chcesz mieć po prostu prosty i lżejszy proces jako host, bez funkcji związanych z MVC, interfejsem API sieci Web lub serwerem HTTP.
W związku z tym możesz wybrać i utworzyć wyspecjalizowany proces hosta z IHost
służący do obsługi hostowanych usług, jak mikrousługa przeznaczona wyłącznie do hostowania IHostedServices
, lub możesz rozszerzyć istniejącą aplikację ASP.NET Core WebHost
, na przykład ASP.NET Core Web API lub aplikację 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 pewne dane w pamięci podręcznej.
- Implementacja QueueBackgroundWorkItem, która pozwala na wykonanie zadania 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ć jedną z tych akcji na zadanie w tle, które implementuje IHostedService
.
Sposób dodawania jednego lub wielu IHostedServices
do elementu WebHost
lub Host
polega na zarejestrowaniu ich za pomocą metody rozszerzenia AddHostedService w WebHost
środowisku ASP.NET Core (lub w Host
środowisku .NET Core 2.1 i nowszym). 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 cyklem życia aplikacji (hosta lub mikrousługi). Rejestrujesz zadania podczas uruchamiania aplikacji i masz okazję wykonać pewne działania lub posprzątać, gdy aplikacja się zamyka.
Bez użycia polecenia IHostedService
zawsze można uruchomić wątek w tle, aby uruchomić dowolne zadanie. Różnica polega dokładnie na tym, że podczas zamykania aplikacji wątek ten zostanie po prostu przerwany, bez możliwości wykonania łagodnych czynności porządkowych.
Interfejs IHostedService
Podczas rejestrowania elementu IHostedService
, platforma .NET podczas uruchamiania i zatrzymywania aplikacji wywołuje odpowiednio metody StartAsync()
i StopAsync()
typu IHostedService
. 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 podstawowe czynności wymagane 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 przypadku aplikacja eShopOnContainers wykonuje metodę, która przeszukuje tabelę bazy danych w poszukiwaniu zamówień o określonym stanie. Podczas wprowadzania zmian publikuje zdarzenia integracyjne za pomocą magistrali zdarzeń (pod spodem może używać 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 oczekujemy anulowania naszej usługi w ciągu 5 sekund, w przeciwnym razie zostanie ona gwałtownie zakończona.
Poniższy kod zmieni ten czas na 10 sekund.
WebHost.CreateDefaultBuilder(args)
.UseShutdownTimeout(TimeSpan.FromSeconds(10))
...
Diagram klas podsumowujący
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. Na przykład, jeśli wdrożysz swoje WebHost
na IIS lub zwykłą usługę Azure App Service, twój host może zostać wyłączony z powodu recyklingu puli aplikacji. Jeśli jednak wdrażasz host jako kontener do orkiestratora, na przykład Kubernetes, możesz kontrolować zagwarantowaną liczbę działających instancji 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 wdrożenia WebHost
do puli aplikacji, istnieją scenariusze, takie jak ponowne wypełnianie lub opróżnianie pamięci podręcznej aplikacji przechowywanej w pamięci, które nadal są odpowiednie.
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 lub hoście (począwszy od platformy .NET Core 2.1 z IHost
). Jego główną zaletą jest możliwość łagodnego anulowania, które pozwala na oczyszczenie kodu zadań w tle, kiedy 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