Як запустити фоновий процес Asp.net

Мені знадобилося запустити фоновий процес ASP.NET. Виникло питання: як краще це зробити? Трохи погугливши в блозі SCOTT HANSELMAN, я знайшов запис «How to run Background Tasks in ASP.NET». Стаття не дуже нова – 2014 роки, але цілком актуальна, тому я вирішив перекласти її на російську мову.

Кілька років тому Phil Haack написав чудову статтю про небезпеки виконання фонових завдань ASP.NET. Він виділив три основних ризику, пов'язаних із запуском фонового процесу:

  1. Необроблене виняток у потоці, несвязанном з запитом, може зняти процес.
  2. Якщо запустити сайт у веб-фермі, то є ймовірність випадково завершити кілька екземплярів програми, яка могла запустити кілька примірників однієї й тієї ж задачі одночасно.
  3. Домен додатка, в якому запущений сайт, з різних причин може вивантажитися і зняти фонову завдання, запущену в ньому.
Звичайно, можна написати власний менеджер для управління фоновими завданнями. Але, найімовірніше, Ви зробите це неправильно. Ніхто не збирається заперечувати Ваші навички розробника. Просто створення такого менеджера — це досить тонка річ. Та й навіщо Вам це треба?

Є безліч відмінних варіантів запуску завдань у фоновому режимі. І не просто абстрактних методик, а готових бібліотек.

Якісь ASP.NET додатки можуть працювати на Ваших власних серверах під IIS, якісь розміщуватися в Azure.

Для запуску фонових задач можна виділити кілька варіантів:

  • Основний: Hangfire або аналогічна бібліотека, яку можна використовувати для написання фонових завдань на Вашому власному ASP.NET сайті.
  • Домен: Azure WebJobs веб-завдання Azure. Власний сервіс в Azure, який можна використовувати для запуску фонових задач поза Вашого веб-сайту з можливістю масштабування навантаження.
  • Розширений: Веб-роль Azure хмарних службах. Сервіс, що дозволяє запускати фонові завдання незалежно від Вашого сайту, з можливістю масштабування навантаження і контролем виконання.
У мережі можна знайти велику кількість статей і відео про те, як використовувати завдання Azure і як працюють веб-ролі в хмарних службах Azure. Але не так багато статей про те, як запускати фонові завдання на власному сервері.

WEBBACKGROUNDER
Як написано на сайті: «WebBackgrounder — це перевірка концепції сумісного з веб-фермами менеджера фонових завдань, який повинен працювати тільки з веб-додатками ASP.NET». Його код не змінювався вже багато років, але NuGet пакет був викачаний більше півмільйона разів.

Проект дозволяє працювати тільки з одним завданням, управляти повторюваних завдань у фоновому режимі під час роботи веб-додатки.

Це явно бібліотека не для всіх випадків життя. Але якщо під час роботи ASP.NET додатки необхідно запускати всього одне завдання, то WebBackgrounder — все, що Вам потрібно.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace WebBackgrounder.DemoWeb
{
public class SampleJob : Job
{
public SampleJob(TimeSpan interval, TimeSpan timeout)
: base("Sample Job", interval, timeout)
{
}

public override Task Execute()
{
return new Task(() => Thread.Sleep(3000));
}
}
}

Доданий в .NET 4.5.2 QUEUEBACKGROUNDWORKITEM
Можна сказати, що QueueBackgroundWorkItem був доданий в .NET 4.5.2 у відповідь на появу WebBackgrounder. Це не просто реалізація «Task.Run». Це щось більше:

QBWI управляє за розкладом завданнями, які повинні запускатися у фоновому режимі, незалежно від будь-якого запиту. Різниця з звичайним елементом ThreadPool полягає в тому, що ASP.NET автоматично відстежує скільки робочих елементів, зареєстрованих за допомогою API, що запущено в даний момент, а в разі вимкнення домену програми середовище виконання ASP.NET намагається відстрочити його, даючи можливість завершити роботу запущеним фонових завдань.

Необхідно враховувати, що середовище виконання ASP.NET може відстрочити вимикання домену програми всього на 90 секунд, даючи можливість завершити завдання. Тому, якщо Ви не можете виконати завдання за цей час, то вам необхідно використати інші, більш надійні засоби.

API дуже просте — використовується «Func<CancellationToken, Task>». Далі невеликий приклад, який запускає фоновий процес з контролера MVC:

public ActionResult SendEmail([Bind(Include = "Name,Email")] User user)
{
if (ModelState.IsValid)
{
HostingEnvironment.QueueBackgroundWorkItem(ct => SendMailAsync(user.Email));
return RedirectToAction("Index", "Home");
}

return View(user);
}

FLUENTSCHEDULER
FluentScheduler — більш просунутий і складний менеджер завдань з fluent інтерфейсом. З його допомогою ви дійсно можете керувати процесом запуску завдання.

using FluentScheduler;

public class MyRegistry : Registry
{
public MyRegistry()
{
// Запуск завдання ITask через певний інтервал часу
Schedule<MyTask>().ToRunNow().AndEvery(2).Seconds();

// Простий Запуск завдання у певний момент часу 
Schedule(() => Console.WriteLine("Timed Task - Will run every day at 9:15pm: " + DateTime.Now)).ToRunEvery(1).Days().At(21, 15);

// Запуск більш складної дії – запуск завдання негайно і з місячним інтервалом
Schedule(() =>
{
Console.WriteLine("Complex Action Task Starts: " + DateTime.Now);
Thread.Sleep(1000);
Console.WriteLine("Complex Action Task Ends: " + DateTime.Now);
}).ToRunNow().AndEvery(1).Months().OnTheFirst(DayOfWeek.Monday).At(3, 0);
}
}

Також FluentScheduler підтримує IoC і може легко підключатися за допомогою Вашої улюбленої Dependency Injection бібліотеки, просто реалізуючи його ITaskFactory інтерфейс.

Необхідно відзначити, що FluentScheduler може працювати .NET Core.

Для обробки винятків, що виникають у фоновому режимі, можна використовувати подія JobException об'єкта JobManager. Подія дозволяє отримати інформацію про виниклу в задачі виключення.

JobManager.JobException += (info) => Log.Fatal("An error just happened with a scheduled job: " + info.Exception);

QUARTZ.NET
Quartz.NET — це версія популярного фреймворку для Java, реалізованого в .NET. Фреймворк активно розвивається. Для створення фонової задачі в Quartz використовується інтерфейс IJob з єдиним методом Execute, який і треба реалізувати.

using Quartz;
using Quartz.Impl;
using System;

namespace ScheduledTaskExample.ScheduledTasks
{
public class JobScheduler
{
public static void Start()
{
IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
scheduler.Start();

IJobDetail job = JobBuilder.Create<MyJob>().Build();

ITrigger trigger = TriggerBuilder.Create()
.WithDailyTimeIntervalSchedule
(s =>
s.WithIntervalInHours(24)
.OnEveryDay()
.StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(0, 0))
)
.Build();

scheduler.ScheduleJob(job, trigger);
}
}
}

Для запуску менеджера всередині Application_Start необхідно викликати JobScheduler.Start(). Для початку роботи можна прочитати Дії за розкладом і Quartz.NET (переклад Scheduled Tasks In ASP.NET With Quartz.Net).

Проект має досить пристойну документацію, яку варто прочитати перед початком використання (як мінімум, варто переглянути Tutorials for Developing with Quartz).

HANGFIRE
І останній в огляді, але, безумовно, непоследний по функціональності, найбільш просунутий з усіх Hangfire. Це дійсно дуже просунутий фреймворк для фонових завдань ASP.NET. Опціонально він може використовувати Redis, SQL Server, SQL Azure, MSMQ або RabbitMQ для підвищення надійності виконання завдань.

Документація Hangfire дійсно чудова. Хотілося б, щоб кожен проект з відкритим вихідним кодом мав таку документацію.

Однією з найефектніших функцій Hangfire є вбудована аналітична панель, яка дозволяє переглядати розкладу, виконувані завдання, успішні і успішно завершені завдання.

Hangfire дозволяє легко визначити завдання типу «запустити і забути», інформація про яких буде зберігатися в базі даних:

BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget"));

Можна відстрочити виконання завдання:

BackgroundJob.Schedule(() => Console.WriteLine("Delayed"), TimeSpan.FromDays(1));

Або запустити завдання CRON стилі

RecurringJob.AddOrUpdate(() => Console.Write("Recurring"), Cron.Daily);

Працювати з Hangfire дуже зручно. Hangfire має хорошу документацію і навчальні керівництва, засновані на реальних прикладах.

Hangfire — це ціла екосистема для роботи з фоновими завданнями в ASP.NET додатках.

Бібліотеки доступні у вигляді відкритих вихідних кодів або Nuget пакетів.

Підсумки (особисто від себе)
Вибираючи бібліотеку для себе, я, з одного боку, хочу мати пристойну функціональність, а з іншого, поки не хочу використовувати, наприклад, базу даних для зберігання інформації про запущених завдань. Тому прості рішення типу WebBackgrounder або QueueBackgroundWorkItem я навіть не став розглядати.

Я вже знаю, що мені потрібно запускати більше одного процесу, і працювати процеси можуть довго (обмеження в 90 секунд на завершення QueueBackgroundWorkItem). FluentScheduler виглядає непогано, але хотілося більшого. Hangfire – відмінне рішення, але, начебто, одразу вимагає використання бази даних для зберігання черги завдань. Та й не зовсім там все безкоштовно – є і платна версія.

У підсумку я поки вибрав Quartz.NET: цілком пристойна документація, достатня кількість прикладів, щоб швидко почати використовувати, а також розширюваний функціонал, який можна додавати в міру зростання потреб.

Якщо ви знаєте інші бібліотеки для запуску фонових задач або маєте досвід розв'язання подібних завдань – діліться в коментарях.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.