Hangfire в .NET Core 7 — фоновые задания

  • Михаил
  • 30 мин. на прочтение
  • 232
  • 23 Feb 2023
  • 01 Mar 2023

Hangfire — это хорошо задокументированный планировщик задач с открытым исходным кодом для ASP.NET и ASP.NET Core, полностью бесплатный для коммерческого использования. Он многопоточный, легко масштабируемый и предлагает различные типы заданий. Он хорошо структурирован, прост в использовании и обеспечивает высокую производительность.

Для каких задач Hangfire используется

Он в основном используется для выполнения фоновых задач, таких как пакетное уведомление/уведомление по электронной почте, пакетный импорт файлов, обработка видео/изображений, обслуживание базы данных, очистка файлов и т. д. Hangfire можно использовать для фоновых задач с высоким/низким потреблением ЦП, короткими/длительными выполнение задач, повторяющиеся задания, запуск и забвение и многое другое.

Панель инструментов и база данных Hangfire

На панели управления Hangfire мы можем видеть все запущенные и запланированные задания, которые мы создаем с помощью нашего клиента Hangfire. Мы также можем отслеживать серверы, повторные попытки и неудачные задания, а также отслеживать все задания в очереди. Еще одна замечательная вещь, которую мы можем сделать на панели инструментов, — вручную запускать любые существующие задания.

Для хранения определений заданий и статусов Hangfire по умолчанию может использовать базу данных SQL Server, и мы также можем выбрать другие параметры.

Различные типы заданий в Hangfire 

Различные типы заданий, доступных в Hangfire.

  • Задания «отправил и забыл»
    Задания «отправил и забыл» выполняются только один раз и почти сразу после создания.
  • Отложенные задания
    Отложенные задания выполняются только один раз, но не сразу, через определенный интервал времени.
  • Повторяющиеся задания
    Повторяющиеся задания запускаются много раз по указанному расписанию CRON.
  • Продолжения
    Продолжения выполняются после завершения родительского задания.

В Hangfire также доступны два задания, но они присутствуют в версии Hangfire Pro и используются для пакетного выполнения нескольких заданий как единого объекта, которые приведены ниже. 

  • Пакеты
    Пакет — это группа фоновых заданий, созданных атомарно и считающихся единым объектом.
  • Продолжение пакета
    Продолжение пакета запускается после завершения всех фоновых заданий в родительском пакете.

 

Настройка Hangfire в ASP.NET Core

Шаг 1

Давайте создадим новый проект веб-API ASP.NET Core в Visual Studio 2022. Скачать проект.

Шаг 2

Укажите имя проекта HangfireTest, а затем укажите местоположение

Шаг 3

Предоставьте некоторую дополнительную информацию, например .NET 7, настройте для HTTPS и включите поддержку Open API.

Шаг 4

Установите следующие пакеты NuGet:

  • Hangfire.AspNetCore
  • Hangfire.SqlServer

Шаг 5

Создайте новый контроллер API с именем HomeController.

using Hangfire;
using Microsoft.AspNetCore.Mvc;

namespace HangfireTest.Controllers
{
    [ApiController]
    public class HomeController : Controller
    {
        [HttpGet]
        [Route("FireAndForgetJob")]
        public string FireAndForgetJob()
        {
            //Fire - and - Forget Jobs
            //Fire - and - forget jobs are executed only once and almost immediately after creation.
            var jobId = BackgroundJob.Enqueue(() => Console.WriteLine("Welcome user in Fire and Forget Job Demo!"));

            return $"Job ID: {jobId}. Welcome user in Fire and Forget Job Demo!";
        }

        [HttpGet]
        [Route("DelayedJob")]
        public string DelayedJob()
        {
            //Delayed Jobs
            //Delayed jobs are executed only once too, but not immediately, after a certain time interval.
            var jobId = BackgroundJob.Schedule(() => Console.WriteLine("Welcome user in Delayed Job Demo!"), TimeSpan.FromSeconds(60));

            return $"Job ID: {jobId}. Welcome user in Delayed Job Demo!";
        }

        [HttpGet]
        [Route("ContinuousJob")]
        public string ContinuousJob()
        {
            //Fire - and - Forget Jobs
            //Fire - and - forget jobs are executed only once and almost immediately after creation.
            var parentjobId = BackgroundJob.Enqueue(() => Console.WriteLine("Welcome user in Fire and Forget Job Demo!"));

            //Continuations
            //Continuations are executed when its parent job has been finished.
            BackgroundJob.ContinueJobWith(parentjobId, () => Console.WriteLine("Welcome Sachchi in Continuos Job Demo!"));

            return "Welcome user in Continuos Job Demo!";
        }

        [HttpGet]
        [Route("RecurringJob")]
        public string RecurringJobs()
        {
            //Recurring Jobs
            //Recurring jobs fire many times on the specified CRON schedule.
            RecurringJob.AddOrUpdate(() => Console.WriteLine("Welcome user in Recurring Job Demo!"), Cron.Hourly);

            return "Welcome user in Recurring Job Demo!";
        }

        [HttpGet]
        [Route("BatchesJob")]
        public string BatchesJob()
        {
            //Batches - This option is available into hangfire Pro only
            //Batch is a group of background jobs that is created atomically and considered as a single entity.
            //Commenting the code as it's only available into Pro version


            //var batchId = BatchJob.StartNew(x =>
            //{
            //    x.Enqueue(() => Console.WriteLine("Batch Job 1"));
            //    x.Enqueue(() => Console.WriteLine("Batch Job 2"));
            //});

            return "Welcome user in Batches Job Demo!";
        }

        [HttpGet]
        [Route("BatchContinuationsJob")]
        public string BatchContinuationsJob()
        {
            //Batch Continuations - This option is available into hangfire Pro only
            //Batch continuation is fired when all background jobs in a parent batch finished.
            //Commenting the code as it's only available into Pro version

            //var batchId = BatchJob.StartNew(x =>
            //{
            //    x.Enqueue(() => Console.WriteLine("Batch Job 1"));
            //    x.Enqueue(() => Console.WriteLine("Batch Job 2"));
            //});

            //BatchJob.ContinueBatchWith(batchId, x =>
            //{
            //    x.Enqueue(() => Console.WriteLine("Last Job"));
            //});

            return "Welcome user in Batch Continuations Job Demo!";
        }
    }
}

 

Шаг 6

Настройте в Program.cs элементы, связанные с Hangfire, такие как подключение к базе данных SQL Server и ПО промежуточного слоя.

using Hangfire;
using Hangfire.SqlServer;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddHangfire(configuration => configuration
       .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
       .UseSimpleAssemblyNameTypeSerializer()
       .UseRecommendedSerializerSettings()
       .UseSqlServerStorage(builder.Configuration.GetConnectionString("HangfireConnection"), new SqlServerStorageOptions
       {
           CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
           SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
           QueuePollInterval = TimeSpan.Zero,
           UseRecommendedIsolationLevel = true,
           DisableGlobalLocks = true
       }));
builder.Services.AddHangfireServer();

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseHangfireDashboard("/dashboard");

app.UseAuthorization();

app.MapControllers();

app.Run();

 

Шаг 7

Настройте строку подключения в файле appsettings.json.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Hangfire": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "HangfireConnection": "Server=10.10.11.18;Database=Hangfire;Persist Security Info=True;User ID=sa;Password=P@ssw0rd;MultipleActiveResultSets=True;",
    "CultureInfo": "en-US"
  }
}

 

Шаг 8

Теперь запустите приложение. Он откроет окно swagger и покажет все созданные нами конечные точки API. Мы будем использовать API один за другим, чтобы протестировать вещи.

Кроме того, он создаст таблицы в базе данных Hangfire, связанные с управлением заданиями Hangfire.

Шаг 9

После нажатия на API вы теперь можете открыть панель управления Hangfire для управления фоновыми заданиями.

Откройте новое окно/вкладку и нажмите URI  https://localhost:7222/dashboard.

Мы настроили /Dashboard URI в файле Program.cs, поэтому наш маршрут /Dashboard

Если вы не настроили какое-либо имя маршрута в файле Program.cs, URI вашей панели мониторинга будет URI по умолчанию  https://localhost:7222/hangfire.

Теперь нажмите API один за другим и следите за панелью инструментов.

Панель инструментов показывает все запущенные и запланированные задания, которые мы создали с помощью нашего клиента Hangfire. Мы также можем отслеживать серверы, повторные попытки и неудачные задания, а также отслеживать все задания в очереди. Еще одна замечательная вещь, которую мы можем сделать на панели инструментов, — вручную запускать существующие задания.

Вызов заданий через интерфейс Hangfire через внедрение зависимостей

В HomeController нашего API мы использовали классы Hangfire для вызова различных заданий, таких как  BackgroundJob и  RecurringJob.

Мы также можем использовать интерфейс Hangfire для вызова различных заданий, таких как  IBackgroundJobClient и  IRecurringJobManager.

Пример кода для вызова различных заданий через интерфейс с помощью внедрения зависимостей.

using Hangfire;
using Microsoft.AspNetCore.Mvc;

namespace HangfireTest.Controllers
{
    public class HangfireController : Controller
    {
        private readonly IBackgroundJobClient _backgroundJobClient;
        private readonly IRecurringJobManager _recurringJobManager;
        public HangfireController(IBackgroundJobClient backgroundJobClient, IRecurringJobManager recurringJobManager)
        {
            _backgroundJobClient = backgroundJobClient;
            _recurringJobManager = recurringJobManager;
        }

        [HttpGet]
        [Route("IFireAndForgetJob")]
        public string FireAndForgetJob()
        {
            //Fire - and - Forget Jobs
            //Fire - and - forget jobs are executed only once and almost immediately after creation.
            var jobId = _backgroundJobClient.Enqueue(() => Console.WriteLine("Welcome user in Fire and Forget Job Demo!"));

            return $"Job ID: {jobId}. Welcome user in Fire and Forget Job Demo!";
        }

        [HttpGet]
        [Route("IDelayedJob")]
        public string DelayedJob()
        {
            //Delayed Jobs
            //Delayed jobs are executed only once too, but not immediately, after a certain time interval.
            var jobId = _backgroundJobClient.Schedule(() => Console.WriteLine("Welcome user in Delayed Job Demo!"), TimeSpan.FromSeconds(60));

            return $"Job ID: {jobId}. Welcome user in Delayed Job Demo!";
        }

        [HttpGet]
        [Route("IContinuousJob")]
        public string ContinuousJob()
        {
            //Fire - and - Forget Jobs
            //Fire - and - forget jobs are executed only once and almost immediately after creation.
            var parentjobId = _backgroundJobClient.Enqueue(() => Console.WriteLine("Welcome user in Fire and Forget Job Demo!"));

            //Continuations
            //Continuations are executed when its parent job has been finished.
            BackgroundJob.ContinueJobWith(parentjobId, () => Console.WriteLine("Welcome Sachchi in Continuos Job Demo!"));

            return "Welcome user in Continuos Job Demo!";
        }

        [HttpGet]
        [Route("IRecurringJob")]
        public string RecurringJobs()
        {
            //Recurring Jobs
            //Recurring jobs fire many times on the specified CRON schedule.
            _recurringJobManager.AddOrUpdate("jobId", () => Console.WriteLine("Welcome user in Recurring Job Demo!"), Cron.Hourly);

            return "Welcome user in Recurring Job Demo!";
        }
    }
}

Резюме

В этой статье мы рассмотрели Hangfire, который мы использовали в .NET 6 для планирования фоновых заданий. Надеюсь, вы понимаете вещи, связанные с Hangfire.