Ответ
Hangfire — это популярная open-source библиотека для .NET, которая предоставляет надежный и простой способ выполнения фоновых задач, отложенных (delayed) и периодических (recurring) заданий. Ее ключевая особенность — хранение состояния задач в персистентном хранилище (БД), что гарантирует выполнение даже после перезапуска приложения.
Зачем использовать Hangfire вместо ThreadPool или IHostedService?
- Надежность: Задачи сохраняются в БД (SQL Server, PostgreSQL, Redis и др.). Если процесс упадет, задачи будут подхвачены и выполнены после перезапуска.
- Мониторинг: Встроенная веб-панель (Dashboard) для просмотра состояния, перезапуска и управления задачами.
- Масштабируемость: Можно запустить несколько экземпляров обработчиков (серверов Hangfire) для распределения нагрузки.
- Простота: Не нужно писать сложный код для управления очередями, потоками и повторами.
Основные типы задач:
- Фоновая задача (Fire-and-forget): Выполняется один раз, почти сразу.
- Отложенная задача (Delayed): Выполняется один раз, через указанный интервал времени.
- Периодическая задача (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) для приоритизации задач.