Thực thi các Background Jobs sử dụng Hangfire trong .NET

Trong một số ứng dụng hiện nay, ta cần thực thi các tasks chạy ngầm, đây được cho là những công việc dài và tẻ nhạt hay một công việc lặp đi lặp lại mà chúng ta cần thực hiện hàng ngày, nhưng trong bài viết này chúng ta sẽ tìm hiểu cách thực hiện nó một cách dễ dàng.

Thực thi các Background Jobs sử dụng Hangfire trong .NET

 

Background Jobs là gì?

Để giữ cho các ứng dụng của chúng ta chạy trơn tru hơn và người dùng hài lòng hơn, đối với một số tác vụ nhất định, chúng ta có thể sử dụng một khái niệm gọi là Background Jobs.
Hiểu một cách đơn giản thì background job là một nơi xử lý các luồng, không can thiệp vào luồng chính request/response của một trang web, và thực hiện trên server.
Vậy nó giúp ích được gì cho chúng ta? Giả sử ứng dụng của bạn có chức năng lưu tạm file, vậy mỗi lần người dùng lưu tạm mãi vậy và không dùng, đến một lúc nào đó nó sẽ đầy và tốn dung lượng. Lúc này bạn cần một công việc tự động là 3 ngày nó sẽ dọn các file tạm đó đi. Do đó, đối với các công việc định kì, lặp đi lặp lại thì background jobs/ background tasks là giải pháp hoàn hảo.

Hangfire là gì?

Hangfire là một công cụ lập lịch tác vụ mã nguồn mở, đa luồng, có thể mở rộng dễ dàng và cung cấp nhiều loại công việc khác nhau. Nó có cấu trúc tốt, sử dụng đơn giản và mang lại hiệu suất mạnh mẽ.
So với các bộ lập lịch có sẵn khác, Hangfire mang lại rất nhiều lợi thế. Trong đó, nó rất dễ cài đặt và cấu hình, nó sử dụng bộ lưu trữ liên tục và có một bảng điều khiển giao diện người dùng đẹp mắt để kiểm tra các tasks bất cứ lúc nào. Nó hỗ trợ xử lý nhiều hàng đợi và chúng ta cũng có thể chọn rõ ràng hàng đợi nào mà chúng ta muốn sử dụng cho một task cụ thể.

Sử dụng Hangfire trong .NET Core

Bước 1: Để bắt đầu, đầu tiên bạn cần tạo một ASP.NET Core Web API project, sau đó tiến hành cài đặt Hangfire NuGet Package:
Thực thi các Background Jobs sử dụng Hangfire trong .NET

 

Hoặc bạn có thể đặt bằng command sau:
PM> Install-Package Hangfire
Bước 2: Thiết lập Connection String:
Để lưu trữ các jobs và statuses, như chúng ta đã biết, Hangfire mặc định sử dụng cơ sở dữ liệu SQL Server và chúng ta cũng có thể chọn các cơ sở dữ liệu khác. Ví dụ để đơn giản, bạn có thể sử dụng bộ lưu trữ MSSQL LocalDB.
Bây giờ chúng ta cần update lại connection string bên trong file appsettings.json như dưới đây:
"AllowedHosts": "*",
    "ConnectionStrings": {
        "DBConnection": "Server=HIUPC;Database=HangfireDb;Trusted_Connection=True"
    }

 

Bước 3: Bây giờ bạn cần thêm các configure, đối với .NET 6 trở lên, bạn sẽ config giống mình như bên dưới:
builder.Services.AddHangfire(x =>
{
    x.UseSqlServerStorage(builder.Configuration.GetConnectionString("DBConnection"));
});
builder.Services.AddHangfireServer();

Như các bạn thấy ở trên, với phương thức AddHangfire(), chúng ta đã thêm Hangfire service đi kèm với SQL server option. Chúng ta cũng đã cung cấp connection string đã chỉ định bên trong file appsettings.json. Và sau cùng là thêm Hangfire server bằng method AddHangfireServer().

Tiếp đến ta cần update pipeline sau:
app.UseHangfireDashboard();

Khi chạy chương trình, Hangfire sẽ tự động kiểm tra database được chỉ định để tìm các bảng cần thiết và nếu các bảng đó chưa tồn tại, nó sẽ tự tạo ra các bảng. Nhưng bạn cần tạo database trước đó trùng với tên bạn đã thiết lập trong appsettings.json.

Các bảng được tự động sinh ra sẽ như sau:
Thực thi các Background Jobs sử dụng Hangfire trong .NET

 

Đó là về phần cấu hình và tất cả chúng ta đã sẵn sàng bắt đầu sử dụng Hangfire. Bây giờ, ta nhanh chóng kiểm tra được Hangfire dashboard và sau đó xem cách có thể tạo một số background tasks như hướng dẫn dưới đây.

Hangfire Dashboard

Bây giờ bạn có thể run project và navigate đến URL https://localhost:5001/hangfire để xem dashboard của Hangfire.

 

Trong Hangfire dashboard, chúng ta có thể thấy tất cả các tasks đang chạy và đã lên lịch mà chúng ta đã tạo trước đó. Chúng ta cũng có thể theo dõi servers, thử lại các jobs, jobs thất bại và theo dõi tất cả các jobs trong hàng đợi. Một điều tuyệt vời khác mà chúng ta có thể làm trong dashboard là chúng ta có thể kích hoạt bất kỳ jobs hiện có nào theo cách thủ công.

Các loại Background Jobs trong Hangfire

Hangfire hỗ trợ các loại jobs khác nhau mà chúng ta có thể sử dụng cho các dịp khác nhau. Từ các tasks chạy một lần đến các tasks chạy hàng ngày. Hãy xem cách dễ dàng triển khai chúng và kiểm tra việc thực hiện chúng trong hangfire dashboard.
Để bắt đầu testing một jobs mới, bây giờ ta cần tạo một thư mục Services với class JobTestService và interface:
public interface IJobTestService
{
    void FireAndForgetJob();
    void ReccuringJob();
    void DelayedJob();
    void ContinuationJob();
}

Đầu tiên, chúng ta cần chỉ định interface IJobTestService nơi đăng ký bốn phương thức khác nhau – được đặt tên thuận tiện theo loại jobs trong Hangfire.

Sau đó, ta modify class JobTestService như sau:
public class JobTestService : IJobTestService
{
    public void FireAndForgetJob()
    {
        Console.WriteLine("Hello from a Fire and Forget job!");
    }
    public void ReccuringJob()
    {
        Console.WriteLine("Hello from a Scheduled job!");
    }
    public void DelayedJob()
    {
        Console.WriteLine("Hello from a Delayed job!");
    }
    public void ContinuationJob()
    {
        Console.WriteLine("Hello from a Continuation job!");
    }
}
Class JobTestService được implement từ interface IJobTestService. Mỗi phương thức trong class chỉ in ra một số text trong console, thay vì thực hiện công việc thực tế như trong ứng dụng thực.
Thực tế, bên trong các phương thức như thế này, có thể có code upload ảnh lên hoặc kiểm tra sự không hoạt động của người dùng… Trong ví dụ này, các Console.WriteLine() đơn giản sẽ cho phép chúng ta tập trung hơn vào việc lên lịch cho các jobs.
Sau khi xác định class, bây giờ ta cần update configure:
builder.Services.AddScoped<IJobTestService, JobTestService>();

Fire and Forget Jobs

Các Fire và Forget Jobs được thực hiện một lần và gần như ngay lập tức sau khi tạo.
Để demo cho loại job này, bên trong folder Controller, ta sẽ tạo  JobTestController.cs lần lượt code như sau:
[Route("api/[controller]")]
[ApiController]
public class JobTestController : ControllerBase
{
    private readonly IJobTestService _jobTestService;
    private readonly IBackgroundJobClient _backgroundJobClient;
    public JobTestController(IJobTestService jobTestService, IBackgroundJobClient backgroundJobClient)
    {
        _jobTestService = jobTestService;
        _backgroundJobClient = backgroundJobClient;
    }

Cụ thể, chúng ta tạo JobTestController constructor và inject đến IBackgroundJobClient interface mà Hangfire cung cấp.  Chúng ta sẽ sử dụng các phương thức của interface này để lên lịch cho các loại jobs khác nhau. Bên cạnh đó, chúng ta cũng thêm interface IJobTestService mà đã tạo trước đó.

Và bây giờ ta thêm method sau:
[HttpGet("/FireAndForgetJob")]
public ActionResult CreateFireAndForgetJob()
{
    _backgroundJobClient.Enqueue(() => _jobTestService.FireAndForgetJob());
    return Ok();
}
Chúng ta sẽ gọi phương thức Enqueue() của interface và call phương thức FireAndForgetJob() làm tham số. Phương thức Enqueue tiện lợi này đảm nhiệm việc tạo và lưu job definition vào database và tất nhiên, đưa nó vào hàng đợi để xử lý.

Delayed Jobs

Delayed Jobs là những job mà chúng ta chắc chắn muốn thực hiện, nhưng không phải lúc này. Chúng ta có thể lên lịch để thực hiện chúng vào một thời điểm cụ thể, có thể là một phút nữa hoặc ba tháng nữa.
Để test loại job này, hãy tạo một endpoint mới trong JobTestController:
[HttpGet("/DelayedJob")]
public ActionResult CreateDelayedJob()
{
    _backgroundJobClient.Schedule(() => _jobTestService.DelayedJob(), TimeSpan.FromSeconds(60));
    return Ok();
}

Chúng ta sẽ thêm một method CreateDelayedJob(), trong đó sử dụng interface IBackgroundJobClient. Lần này, chúng ta sẽ không gọi phương thức Enqueue(). Thay vào đó, chúng ta sẽ sử dụng phương thức Schedule() và truyền vào hai tham số – DelayedJob() và delay time.

Phương thức Schedule() sẽ tạo job definition và lưu nó, nhưng cũng đảm bảo schedule để thực hiện nó trong queue vào thời điểm cụ thể.

Bây giờ khi chúng ta gửi một request GET đến endpoint này, các bạn có thể chuyển sang swagger và send request đến endpoint trên, và chúng ta sẽ schedule một job trong 60 giây trong tương lai.

Sau khi request, job vừa rồi sẽ được show trên Scheduled tab và chúng ta sẽ thấy cột Scheduled với mốc thời gian ta vừa request.

 

 

Thực thi các Background Jobs sử dụng Hangfire trong .NET

 

Sau khi job thực thi thành công, nó sẽ move vào Succeeded tab.

Recurring Jobs

Chúng ta thường schedule một recurring job (job định kỳ) để chúng có thể lặp lại sau một khoảng thời gian cụ thể. Đối với những loại job như vậy, Hangfire sẽ sử dụng CRON.
Để lên lịch công việc này, chúng ta cần một Hangfire interface khác, vì vậy trước hết, chúng ta sẽ sửa đổi constructor của controller:
[Route("api/[controller]")]
[ApiController]
public class JobTestController : ControllerBase
{
    private readonly IJobTestService _jobTestService;
    private readonly IBackgroundJobClient _backgroundJobClient;
    private readonly IRecurringJobManager _recurringJobManager;

    public JobTestController(IJobTestService jobTestService, IBackgroundJobClient backgroundJobClient, IRecurringJobManager recurringJobManager)
    {
        _jobTestService = jobTestService;
        _backgroundJobClient = backgroundJobClient;
        _recurringJobManager = recurringJobManager;
    }
Bây giờ chúng ta inject vào interface Hangfire IReccuringJobManager vào constructor. Nó cung cấp các phương thức hữu ích để xử lý công việc định kỳ và chúng ta sẽ sử dụng nó để lên lịch một job mới.
Bây giờ, chúng ta tạo method mới sau:
[HttpGet("/ReccuringJob")]
public ActionResult CreateReccuringJob()
{
    _recurringJobManager.AddOrUpdate("jobId", () => _jobTestService.ReccuringJob(), Cron.Minutely);
    return Ok();
}
Chúng ta sử dụng phương thức AddOrUpdate() của interface. Trong phương thức này, chúng ta truyền một job ID, và call đến phương thức RecurringJob() và CRON time.
Phương thức của Hangfire sẽ tạo một job mới với ID đã chỉ định hoặc cập nhật một job đã tồn tại. Trong ví dụ này, chúng ta chỉ đơn giản là tạo job lần đầu tiên.
Sau khi gửi một request GET đến endpoint này bằng Swagger, reccuring job của chúng ta sẽ hiển thị dưới tab Recurring Jobs trên dashboard:
Thực thi các Background Jobs sử dụng Hangfire trong .NET

 

Continuation Jobs

Chúng ta sẽ khám phá loại job cuối cùng là Continuation job. Điểm đặc biệt của job này là nó kết nối việc thực hiện nhiệm vụ. Với công cụ này, chúng ta có thể làm cho hai công việc chạy liên tiếp sau nhau theo trình tự.
Bây giờ hay update controller với code sau:
[HttpGet("/ContinuationJob")]
public ActionResult CreateContinuationJob()
{
    var parentJobId = _backgroundJobClient.Enqueue(() => _jobTestService.FireAndForgetJob());
    _backgroundJobClient.ContinueJobWith(parentJobId, () => _jobTestService.ContinuationJob());
            
    return Ok();
}
Trong phương thức CreateContinuationJob(), chúng ta sẽ một lần nữa sử dụng interface Hangfire IBackgroundJobClient. Trước tiên, chúng ta sẽ sử dụng nó để tạo một job Fire and Forget đơn giản.
Chúng ta muốn công việc này làm nhiệm vụ kích hoạt cho công việc tiếp theo, vì vậy chúng ta đảm bảo thu thập các job ID mà phương thức Enqueue() trả về. Sau khi nó được tạo, chúng ta sẽ gọi phương thức ContinueJobWith() và truyền job ID đã được tạo cùng với call đến phương thức ContinuationJob(). Phương thức ContinueJobWith() sẽ đảm bảo kết nối hai công việc lại với nhau.
Sau khi gửi request GET, nếu chúng ta kiểm tra tab Succeeded trong dashboard, chúng ta có thể thấy rằng hai công việc trên đã chạy liên tiếp nhau:
Thực thi các Background Jobs sử dụng Hangfire trong .NET

 

Lời kết

Chúng ta khám phá nhiều về Hangfire rất đơn giản và support mạnh mẽ. Bây giờ chúng ta biết cách thiết lập nó trong ứng dụng .NET Core của mình và cách sử dụng nó để lên lịch các loại công việc khác nhau.
Nguồn tham khảo bài viết qua link dưới đây:
https://code-maze.com/hangfire-with-asp-net-core/

Sắp tới, mình sẽ tiếp tục với nhiều khía cạnh và khái niệm hơn về công nghệ này.

Mong bài viết hữu ích, chúc các bạn thành công!
Hieu Ho.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *