Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Подсказка
Это фрагмент из электронной книги «Архитектура микрослужб .NET для контейнеризованных приложений .NET», доступной в документации .NET или в виде бесплатного скачиваемого PDF-файла, который можно прочитать в автономном режиме.
Фоновые задачи и запланированные задания — это то, что может потребоваться использовать в любом приложении, независимо от того, соответствует ли он шаблону архитектуры микрослужб. Разница в использовании архитектуры микрослужб заключается в том, что вы можете реализовать фоновую задачу в отдельном процессе или контейнере для размещения, чтобы уменьшить или увеличить масштаб в зависимости от необходимости.
С универсальной точки зрения в .NET мы назвали эти типы задач размещенными службами, так как они являются службами или логикой, размещенными в узле, приложении или микрослужбе. Обратите внимание, что в этом случае служба-хост просто означает класс с логикой фоновых задач.
С версии .NET Core 2.0, платформа предоставляет новый интерфейс под названием IHostedService, который помогает легко реализовать размещенные службы. Основная идея заключается в том, что можно зарегистрировать несколько фоновых задач (хостинговых служб), которые запускаются в фоновом режиме, пока работает ваш хост, как показано на изображении 6-26.
Рис. 6–26. Использование IHostedService в WebHost и Host
ASP.NET Core 1.x и 2.x поддерживают IWebHost
для фоновых процессов в веб-приложениях. .NET Core 2.1 и более поздние версии поддерживают IHost
для фоновых процессов с простыми консольными приложениями. Обратите внимание на разницу между WebHost
и Host
.
(базовый WebHost
класс, реализующий IWebHost
) в ASP.NET Core 2.0 — это артефакт инфраструктуры, используемый для предоставления функций HTTP-сервера в процесс, например при реализации веб-приложения MVC или службы веб-API. Он предоставляет все новые возможности инфраструктуры в ASP.NET Core, позволяя использовать внедрение зависимостей, добавлять промежуточное программное обеспечение в конвейер запросов и выполнять подобные задачи.
WebHost
использует эти же самые IHostedServices
для фоновых задач.
В .NET Core 2.1 был введён базовый класс Host
, реализующий IHost
. В основном, Host
позволяет вам иметь аналогичную инфраструктуру, как у вас с WebHost
(передача зависимостей, размещенные службы и т. д.), но в этом случае вы хотите иметь простой и легкий процесс в качестве хоста, ничем не связанный с MVC, веб-API или HTTP-сервером.
Поэтому можно выбрать и создать специализированный процесс узла для IHost
обработки размещенных служб и ничего другого, например микрослужбу, созданную только для размещения IHostedServices
, или можно также расширить существующий ASP.NET Core WebHost
, например существующее веб-API ASP.NET Core или приложение MVC.
Каждый подход имеет преимущества и недостатки в зависимости от потребностей бизнеса и масштабируемости. В основном это то, что если фоновые задачи не имеют ничего общего с HTTP (IWebHost
) следует использовать IHost
.
Регистрация размещенных служб в веб-хостинге или хосте
Давайте подробно рассмотрим интерфейс IHostedService
, так как его использование довольно похоже на WebHost
или Host
.
SignalR является одним из примеров компонента, использующего размещённые службы, но его также можно использовать для гораздо более простых задач, таких как:
- Задача в фоновом режиме опрашивает базу данных в поисках изменений.
- Запланированная задача периодически обновляет некоторые кэши.
- Реализация компонента QueueBackgroundWorkItem, позволяющая выполнить задачу в фоновом потоке.
- Обработка сообщений из очереди сообщений в фоновом режиме веб-приложения при совместном использовании общих служб, таких как
ILogger
. - Фоновая задача запущена с
Task.Run()
.
Вы можете просто выгрузить любое из этих действий в фоновую задачу, которая реализует IHostedService
.
Способ добавления одного или нескольких IHostedServices
в ваш WebHost
или Host
путем их регистрации с помощью метода расширения AddHostedService в WebHost
ASP.NET Core (или в Host
для .NET Core 2.1 и выше). По сути, необходимо зарегистрировать размещенные службы в Program.cs при запуске приложения.
//Other DI registrations;
// Register Hosted Services
builder.Services.AddHostedService<GracePeriodManagerService>();
builder.Services.AddHostedService<MyHostedServiceB>();
builder.Services.AddHostedService<MyHostedServiceC>();
//...
В этом коде размещенная GracePeriodManagerService
служба представляет собой реальный код микрослужбы заказов в eShopOnContainers, а остальные два — всего два дополнительных примера.
Выполнение IHostedService
фоновой задачи координируется со временем существования приложения (узла или микрослужбы). Вы регистрируете задачи при запуске приложения, и у вас есть возможность выполнить некоторые изящные действия или очистку при завершении работы приложения.
Без использования IHostedService
всегда можно запустить фоновый поток для выполнения любой задачи. Разница заключается именно во времени завершения работы приложения, когда этот поток просто будет завершен, не имея возможности выполнить аккуратные действия по очистке.
Интерфейс IHostedService
При регистрации IHostedService
, .NET вызывает методы StartAsync()
и StopAsync()
вашего типа IHostedService
во время запуска и остановки приложения соответственно. Дополнительные сведения см. в интерфейсе IHostedService.
Как вы можете себе представить, можно создать несколько реализаций IHostedService и зарегистрировать каждую из них в Program.cs, как показано ранее. Все размещенные службы будут запущены и остановлены вместе с приложением или микрослужбой.
Как разработчик, вы несете ответственность за обработку действия остановки своих служб, когда StopAsync()
метод активируется хостом.
Реализация IHostedService с пользовательским классом хост-сервиса, который наследует от базового класса BackgroundService
Вы можете идти вперед и создать пользовательский класс размещенной службы с нуля и реализовать IHostedService
, как необходимо сделать при использовании .NET Core 2.0 и более поздних версий.
Однако, так как большинство фоновых задач будут иметь аналогичные потребности в отношении управления маркерами отмены и других типичных операций, существует удобный абстрактный базовый класс, от который можно наследовать (доступно с BackgroundService
.NET Core 2.1).
Этот класс предоставляет основную работу, необходимую для настройки фоновой задачи.
Следующий код — абстрактный базовый класс BackgroundService, реализованный в .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();
}
}
При создании производных классов из предыдущего абстрактного базового класса благодаря наследуемой реализации необходимо просто реализовать метод ExecuteAsync()
в собственном пользовательском классе расположенного сервиса, как в следующем упрощенном коде из eShopOnContainers, который опрашивает базу данных и публикует интеграционные события в шину событий по мере необходимости.
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.");
}
.../...
}
В этом конкретном случае для eShopOnContainers выполняется метод приложения, который запрашивает таблицу базы данных для поиска заказов с определенным состоянием и при применении изменений, он публикует события интеграции через шину событий (под ним можно использовать RabbitMQ или служебную шину Azure).
Конечно, можно запустить любую другую фоновую задачу, связанную с бизнесом.
По умолчанию токен отмены устанавливается с временем ожидания в 5 секунд, хотя вы можете изменить это значение при создании WebHost
с использованием расширения UseShutdownTimeout
для IWebHostBuilder
. Это означает, что ожидается завершение работы нашей службы в течение 5 секунд, в противном случае она будет внезапно прекращена.
Следующий код будет изменять это время на 10 секунд.
WebHost.CreateDefaultBuilder(args)
.UseShutdownTimeout(TimeSpan.FromSeconds(10))
...
Схема сводных классов
На следующем рисунке показана визуальная сводка классов и интерфейсов, участвующих при реализации IHostedServices.
Рис. 6–27. Схема классов с несколькими классами и интерфейсами, связанными с IHostedService
Схема классов: IWebHost и IHost могут размещать множество служб, которые наследуются от BackgroundService, которая реализует IHostedService.
Рекомендации по развертыванию и основные выводы
Важно отметить, что способ развертывания ASP.NET Core WebHost
или .NET Host
может повлиять на окончательное решение. Например, если вы развернете WebHost
в IIS или обычной службе приложений Azure, ваш хост может быть отключен из-за перезапуска пула приложений. Но если вы размещаете хост в качестве контейнера в оркестраторе, например Kubernetes, вы можете контролировать обеспеченное количество активных экземпляров вашего хоста. Кроме того, вы можете рассмотреть другие подходы в облаке, специально разработанные для этих сценариев, такие как функции Azure. Наконец, если требуется, чтобы служба выполнялось все время и развертывается на Windows Server, можно использовать службу Windows.
Но даже для WebHost
, развернутого в пуле приложений, существуют сценарии, такие как восстановление или очистка кэша приложения в памяти, которые все еще актуальны.
Интерфейс IHostedService
предоставляет удобный способ запуска фоновых задач в веб-приложении ASP.NET Core (в .NET Core 2.0 и более поздних версиях) или в любом процессе или узле (начиная с .NET Core 2.1 с IHost
). Основное преимущество заключается в том, что плавная отмена позволяет очистить код фоновых задач, когда хост сам завершает работу.
Дополнительные ресурсы
Создание запланированной задачи в ASP.NET Core/Standard 2.0
https://blog.maartenballiauw.be/post/2017/08/01/building-a-scheduled-cache-updater-in-aspnet-core-2.htmlРеализация IHostedService в ASP.NET Core 2.0
https://www.stevejgordon.co.uk/asp-net-core-2-ihostedserviceПример GenericHost с помощью ASP.NET Core 2.1
https://github.com/aspnet/Hosting/tree/release/2.1/samples/GenericHostSample