Ответ
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) для приоритизации задач.
Ответ 18+ 🔞
Ну что за дичь, опять эти фоновые задачи в .NET! Слушай, я тебе сейчас объясню про Hangfire так, что даже твой менеджер поймёт, хотя он, блядь, думает, что бэкенд — это когда спина болит от стула.
Вот представь: тебе надо отправить 10 тысяч писем. Если делать это прямо в HTTP-запросе, пользователь будет ждать, пока твой сервер не обосрётся окончательно, а потом получит таймаут. Красота, да? А если сервер упадёт посередине — всё, пиши пропало, половина писем в тартарары.
Вот тут-то и выходит на сцену Hangfire, этот красавец, блядь. По сути, это такой надёжный мудак, который берёт твою задачу, аккуратно кладёт её в базу данных (да-да, не в оперативку, а в персистентное хранилище, ёпта!), и потом не спеша, с чувством, так сказать, долга, её выполняет. Сервер упал? Похуй! Перезапустился — Hangfire подцепит с того же места, как будто ничего и не было. Это не то что ваши кривые костыли на ThreadPool или IHostedService, которые при падении забывают всё, как пьяный студент после сессии.
И главный плюс — есть веб-морда, панель управления, где видно все задачи: какие выполнились, какие в процессе, какие сдохли. Это ж как камера наблюдения за твоим собственным кодом, просто праздник какой-то!
Основные фишки, которые он умеет:
- Фоновая задача (Fire-and-forget) — отправил и забыл, типа «эй, чувак, выполни это, когда освободишься».
- Отложенная задача (Delayed) — «сделай это через 5 минут, а то я щас занят».
- Периодическая задача (Recurring) — «каждый день в 6 утра делай вот эту хуйню, по расписанию, как робот».
Как впихнуть это в твой ASP.NET Core проект:
Смотри, всё проще, чем кажется. Открываешь Program.cs и пишешь:
// 1. Сначала говорим: «Хочу Hangfire, сука!»
builder.Services.AddHangfire(config => config
.UseSqlServerStorage(builder.Configuration.GetConnectionString("HangfireConnection"))); // Хранить будем в SQL Server
// 2. Добавляем фоновый сервер — это тот самый работяга, который задачи будет жрать
builder.Services.AddHangfireServer();
var app = builder.Build();
// 3. Вешаем красивую панельку мониторинга (например, на адрес /hangfire)
app.UseHangfireDashboard("/hangfire");
// ВАЖНО, БЛЯДЬ! Панель по умолчанию открыта всем подряд!
// Это как оставить дверь в банк нараспашку.
// Обязательно настрой авторизацию, например, только для админов!
// app.UseHangfireDashboard("/hangfire", new DashboardOptions { Authorization = new[] { new MyAuthFilter() } });
// 4. Где-то в коде (в контроллере, сервисе) ставим задачи в очередь
BackgroundJob.Enqueue(() => Console.WriteLine("Привет, фоновая задача!"));
BackgroundJob.Schedule(() => SendEmail("user@mail.com"), TimeSpan.FromMinutes(2));
RecurringJob.AddOrUpdate("cleanup", () => DeleteOldLogs(), Cron.Daily);
А вот реальный пример, как это работает с зависимостями:
Допустим, у тебя есть сервис для отправки писем. Ты хочешь запустить его в фоне.
public class EmailSenderService
{
private readonly ILogger<EmailSenderService> _logger;
public EmailSenderService(ILogger<EmailSenderService> logger)
{
_logger = logger; // Зависимости инжектятся как обычно, красота!
}
// Метод ДОЛЖЕН быть публичным, иначе Hangfire его не увидит, дурачок!
[AutomaticRetry(Attempts = 3)] // Если ошибётся, попробует ещё 3 раза, не паникуй
public async Task SendSpamAsync(List<string> emails, string topic, string text)
{
_logger.LogInformation($"Начинаю спамить {emails.Count} лохам...");
foreach (var email in emails)
{
// Тут твоя логика отправки
await Task.Delay(50); // Имитируем работу
}
_logger.LogInformation("Спам-кампания успешно завершена. Совесть чиста.");
}
}
// Где-то в другом месте (например, в контроллере заказа) ставим задачу
var emailService = serviceProvider.GetRequiredService<EmailSenderService>();
BackgroundJob.Enqueue(() => emailService.SendSpamAsync(emails, "Акция!", "Купи уже наконец!"));
На что обратить внимание, чтобы не обжечься:
- Панель мониторинга (
/hangfire) — без авторизации это дыра размером с чёрную дыру. Закрой её, ёб твою мать! - Аргументы задач — они сериализуются в JSON. Не пытайся передать что-то сложное, типа
DbContext— получишь боль и страдание. Только простые типы или DTO. - Повторы — настрой
AutomaticRetryтолько для идемпотентных операций (тех, которые можно повторять без последствий). Отправка письма дважды — окей, списание денег дважды — пипец. - Производительность — если у тебя задачи летят как из пулемёта, бери Redis в качестве хранилища, а не SQL Server. SQL Server начнёт тупить, как старый дед на подъёме.
- Очереди — можно создавать разные (
default,critical,low), чтобы важные задачи не ждали, пока отработает какая-нибудь ерунда.
В общем, Hangfire — это как нанять ответственного работягу, который не забудет и не потеряет твои поручения. А твои самопальные решения на потоках — это как доверить дело другу после пятой рюмки: вроде и договорились, но чем кончится — хуй знает.