Как лучше всего запускать фоновый сервис в ASP.NET Core?

«Как лучше всего запускать фоновый сервис в ASP.NET Core?» — вопрос из категории ASP.NET Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Предпочтительный способ — использование IHostedService и его удобной абстракции BackgroundService. Они интегрированы в систему зависимостей и жизненный цикл хоста приложения, что обеспечивает корректный запуск и graceful shutdown.

1. Реализация через BackgroundService (рекомендуется для большинства задач):

public class TimedBackgroundService : BackgroundService
{
    private readonly ILogger<TimedBackgroundService> _logger;

    public TimedBackgroundService(ILogger<TimedBackgroundService> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Timed Background Service запущен.");

        // Периодическая фоновая задача
        using var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));

        try
        {
            while (await timer.WaitForNextTickAsync(stoppingToken))
            {
                // Работа, выполняемая каждые 5 секунд
                _logger.LogInformation("Выполняется фоновая задача в: {time}", DateTimeOffset.Now);
                // await DoWorkAsync(stoppingToken);
            }
        }
        catch (OperationCanceledException)
        {
            _logger.LogInformation("Timed Background Service остановлен.");
        }
    }
}

2. Регистрация в DI-контейнере:

// В файле Program.cs
builder.Services.AddHostedService<TimedBackgroundService>();

Ключевые преимущества этого подхода:

  • Управление жизненным циклом: Сервис автоматически запускается при старте приложения и останавливается при его завершении.
  • Graceful Shutdown: Параметр stoppingToken автоматически отменяется при попытке остановки хоста, давая сервису время на корректное завершение операций.
  • Внедрение зависимостей: Полноценная поддержка DI, можно инжектировать любые зарегистрированные сервисы (репозитории, клиенты API, логгеры).
  • Асинхронность: Базовая реализация асинхронна.

Альтернативы и когда их использовать:

  • IHostedService (прямая реализация): Если нужен более тонкий контроль над процессом запуска/остановки.
  • Специализированные библиотеки:
    • Hangfire: Для сложных фоновых заданий с планировщиком, повторными попытками, панелью мониторинга.
    • Quartz.NET: Для высокоточного планирования по cron-выражениям.
  • Timer или Task.Run в контроллере: Антипаттерн. Задачи могут быть потеряны при перезапуске приложения, сложно управлять, нет graceful shutdown.

Важные моменты для BackgroundService:

  • Переопределяйте ExecuteAsync для основной логики.
  • Всегда используйте переданный stoppingToken для проверки отмены в долгих циклах или асинхронных операциях.
  • Обрабатывайте исключения внутри сервиса, чтобы одно упавшее фоновое задание не привело к падению всего хоста приложения.