Что такое Hangfire?

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

Ответ

Hangfire — это популярная open-source библиотека для .NET, которая предоставляет надежный и простой способ выполнения фоновых задач, отложенных (delayed) и периодических (recurring) заданий. Ее ключевая особенность — хранение состояния задач в персистентном хранилище (БД), что гарантирует выполнение даже после перезапуска приложения.

Зачем использовать Hangfire вместо ThreadPool или IHostedService?

  • Надежность: Задачи сохраняются в БД (SQL Server, PostgreSQL, Redis и др.). Если процесс упадет, задачи будут подхвачены и выполнены после перезапуска.
  • Мониторинг: Встроенная веб-панель (Dashboard) для просмотра состояния, перезапуска и управления задачами.
  • Масштабируемость: Можно запустить несколько экземпляров обработчиков (серверов Hangfire) для распределения нагрузки.
  • Простота: Не нужно писать сложный код для управления очередями, потоками и повторами.

Основные типы задач:

  1. Фоновая задача (Fire-and-forget): Выполняется один раз, почти сразу.
  2. Отложенная задача (Delayed): Выполняется один раз, через указанный интервал времени.
  3. Периодическая задача (Recurring): Выполняется по расписанию (CRON-выражение).

Базовая настройка в ASP.NET Core:

// Startup.cs или Program.cs
using Hangfire;

// 1. Регистрация сервисов
builder.Services.AddHangfire(config => config
    .SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
    .UseSimpleAssemblyNameTypeSerializer()
    .UseRecommendedSerializerSettings()
    .UseSqlServerStorage(builder.Configuration.GetConnectionString("HangfireConnection"))); // Используем SQL Server

builder.Services.AddHangfireServer(); // Добавляем фоновый сервер

var app = builder.Build();

// 2. Настройка панели мониторинга (обычно на отдельном эндпоинте)
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
    Authorization = new[] { new MyAuthorizationFilter() } // ВАЖНО: Настроить авторизацию!
});

// 3. Создание задач (например, в контроллере)
BackgroundJob.Enqueue(() => Console.WriteLine("Фоновая задача!"));
BackgroundJob.Schedule(() => SendWelcomeEmail(userId), TimeSpan.FromMinutes(5));
RecurringJob.AddOrUpdate("cleanup-job", () => CleanupOldData(), Cron.Daily);

Пример реальной задачи с внедрением зависимостей:

public class EmailService
{
    private readonly ILogger<EmailService> _logger;
    public EmailService(ILogger<EmailService> logger) => _logger = logger;

    // Метод ДОЛЖЕН быть публичным! Hangfire его сериализует.
    [AutomaticRetry(Attempts = 3)] // Автоматические повторы при ошибке
    public async Task SendBatchNewsletter(List<string> emails, string subject, string body)
    {
        _logger.LogInformation($"Начата рассылка на {emails.Count} адресов");
        foreach (var email in emails)
        {
            // Логика отправки email...
            await Task.Delay(100); // Имитация работы
        }
        _logger.LogInformation("Рассылка завершена");
    }
}

// Постановка задачи в очередь из другого места в коде
var emailService = serviceProvider.GetRequiredService<EmailService>();
BackgroundJob.Enqueue(() => emailService.SendBatchNewsletter(emails, "Акция!", "Текст..."));

Важные практические аспекты:

  • Авторизация Dashboard: Панель /hangfire по умолчанию открыта всем. Обязательно ограничьте доступ (например, только для роли Admin).
  • Сериализация аргументов: Аргументы метода задачи сериализуются в JSON. Используйте простые типы (int, string) или DTO.
  • Повторы (Retries): Настраивайте политики повторных попыток для идемпотентных операций.
  • Производительность: Для высоких нагрузок в качестве хранилища лучше использовать Redis, а не SQL Server.
  • Очереди: Можно создавать несколько очередей (default, critical, emails) для приоритизации задач.