TickerQ: Trình Lập Lịch Công Việc .NET Hiện Đại Vượt Trội Quartz và Hangfire

Oct 14, 2025

6 phút đọc

Giới thiệu

Lập lịch các công việc chạy ngầm là phần cơ bản của nhiều ứng dụng .NET — cho dù đó là gửi email, xử lý báo cáo, hay đồng bộ dữ liệu.

Trong nhiều năm, các nhà phát triển đã dựa vào các công cụ như Quartz.NETHangfire.

Mặc dù chúng là những công cụ tuyệt vời, chúng có một số nhược điểm.

Nhược điểm của Quartz.NET:

  • Đăng ký công việc dựa trên Reflection
  • API phức tạp
  • Cấu hình nặng nề
  • Truyền tham số vào công việc khó khăn
  • Không có tích hợp EF Core sẵn (chỉ qua các gói của bên thứ ba)
  • Không có bảng điều khiển tích hợp để giám sát công việc (chỉ qua các gói của bên thứ ba)
  • Không có tính năng thử lại tích hợp

Nhược điểm của Hangfire:

  • Đăng ký công việc dựa trên Reflection
  • Hỗ trợ DI hạn chế
  • Hỗ trợ async hạn chế
  • Một số nhà cung cấp lưu trữ tích hợp yêu cầu giấy phép thương mại

TickerQ là một cách tiếp cận mới cho việc lập lịch công việc trong .NET — cung cấp API hiện đại, sạch sẽ, lưu trữ tích hợp, tích hợp EF Core và bảng điều khiển đẹp mắt để giám sát công việc.

Nó loại bỏ nhiều mã mẫu và thủ tục mà chúng ta đã quen thuộc, và thay vào đó cung cấp trải nghiệm thân thiện với nhà phát triển, ưu tiên code-first.

Trong bài viết này, chúng ta sẽ khám phá:

  • Cách thiết lập TickerQ trong dự án .NET
  • Tích hợp TickerQ với EF Core
  • Tạo và lập lịch công việc (lặp lại và một lần)
  • Đăng ký công việc động
  • Sử dụng TickerQ Dashboard để giám sát việc thực thi công việc

Bắt đầu với TickerQ

TickerQ có sẵn dưới dạng gói NuGet nhắm đến .NET Standard 2.1 và tương thích với bất kỳ ứng dụng .NET Core 3.1+ nào. Nó hoạt động ngay lập tức với ASP.NET Core và không yêu cầu bất kỳ tệp cấu hình bổ sung nào — mọi thứ đều là code-first và thân thiện với dependency injection.

Làm theo các bước sau để bắt đầu với TickerQ:

Bước 1: Cài đặt gói NuGet

dotnet add package TickerQ

Điều này thêm thư viện TickerQ cốt lõi. Bạn có thể tùy chọn cài đặt các gói khác như TickerQ.EntityFrameworkCoreTickerQ.Dashboard (chúng ta sẽ đề cập trong các phần tiếp theo).

Bước 2: Đăng ký TickerQ trong Program.cs

TickerQ kết nối trực tiếp vào đường ống khởi động ứng dụng của bạn. Thêm các dòng sau trong Program.cs của bạn:

builder.Services.AddTickerQ();<br><br>var app = builder.Build();<br>app.UseTickerQ();<br><br>app.Run();

Phương thức AddTickerQ đăng ký tất cả các dịch vụ cần thiết cho TickerQ. Phương thức UseTickerQ thêm middleware xử lý các yêu cầu đến.

Thế là xong. Không cần quản lý các dịch vụ chạy nền. Không cần dịch vụ được lưu trữ bổ sung. TickerQ tự xử lý điều đó cho bạn.

Bước 3: Định nghĩa một Công việc

Công việc TickerQ chỉ là các phương thức lớp thông thường, và các lớp không cần kế thừa bất kỳ giao diện hoặc lớp cơ sở nào. Chỉ cần thêm thuộc tính TickerFunction vào bất kỳ phương thức nào trong bất kỳ lớp nào.

Đây là ví dụ về một công việc tạo báo cáo:

public class CreateReportJob<br>{<br> private readonly ReportDbContext _dbContext;<br><br> public CreateReportJob(ReportDbContext dbContext)<br> {<br> _dbContext = dbContext;<br> }<br><br> [TickerFunction(functionName: "Send Notifications", cronExpression: "0 * * * *")]<br> public async Task CreateReport(TickerFunctionContext tickerContext,<br> CancellationToken cancellationToken)<br> {<br> var report = new Report<br> {<br> Title = $"Scheduled Report - {DateTime.UtcNow:yyyy-MM-dd HH:mm}",<br> Content = $"This is an automatically generated report created at {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss}",<br> CreatedAt = DateTime.UtcNow<br> };<br><br> _dbContext.Reports.Add(report);<br> await _dbContext.SaveChangesAsync(cancellationToken);<br> }<br>}

Trong ví dụ này, TickerQ sẽ gọi phương thức CreateReport mỗi giờ.

Tham số cronExpression là tùy chọn và có thể được sử dụng để lập lịch các công việc lặp lại dựa trên biểu thức cron. Nếu bạn bỏ qua nó, bạn có thể cấu hình lịch trình khi đăng ký TickerQ trong DI hoặc chỉ định nó động.

Dependency injection được hỗ trợ ngay lập tức, và bạn có thể chỉ định nhiều phương thức công việc trong một lớp duy nhất mà bạn muốn.

Ở đây, TickerFunctionContext được sử dụng để truy cập thông tin bổ sung về việc thực thi công việc.

Bạn cũng có thể sử dụng context để hủy hoặc xóa công việc:

tickerContext.CancelTicker();<br><br>await tickerContext.DeleteAsync();

Bạn cũng có thể tham chiếu biểu thức cron từ appsettings.json bằng cách sử dụng cú pháp %Section:Key%:

[TickerFunction(FunctionName: "ExampleMethod", CronExpression: "%CronTicker:EveryMinute%")]<br>public void ExampleMethod() { }

Bước 4: Xử lý Ngoại lệ (tùy chọn)

Để xử lý ngoại lệ, bạn có thể triển khai giao diện ITickerExceptionHandler và đăng ký nó trong DI:

public class TickerExceptionHandler : ITickerExceptionHandler<br>{<br> public async Task HandleExceptionAsync(Exception exception,<br> Guid tickerId, TickerType tickerType)<br> {<br> // logic của bạn...<br> }<br><br> public async Task HandleCanceledExceptionAsync(Exception exception,<br> Guid tickerId, TickerType tickerType)<br> {<br> // logic của bạn...<br> }<br>}

Tích hợp TickerQ với EF Core

Để làm cho TickerQ sẵn sàng cho production, đặc biệt trong các trường hợp ứng dụng của bạn có thể được khởi động lại hoặc mở rộng quy mô trên nhiều instance, bạn sẽ muốn tính bền vững.

EF Core cung cấp cho TickerQ một kho lưu trữ quan hệ cho trạng thái công việc, lịch sử, khóa và khôi phục.

Đầu tiên, cài đặt tiện ích mở rộng EF Core cho TickerQ:

dotnet add package TickerQ.EntityFrameworkCore

Trong đăng ký dịch vụ của bạn, kích hoạt các tính năng EF Core khi gọi AddTickerQ(...). Ví dụ:

services.AddTickerQ(options =><br>{<br> // Đặt thời gian chờ dự phòng để kiểm tra các công việc bị bỏ lỡ và thực thi.<br> options.UpdateMissedJobCheckDelay(TimeSpan.FromSeconds(10));<br><br> options.SetInstanceIdentifier("TickerQ");<br><br> // Cấu hình kho lưu trữ hoạt động được hỗ trợ bởi EF Core cho metadata, khóa và trạng thái của TickerQ.<br> options.AddOperationalStore<ReportDbContext>(efCoreOptions =><br> {<br> // Áp dụng cấu hình mô hình tùy chỉnh chỉ trong quá trình EF Core migrations<br> // (design-time). Mô hình runtime không bị ảnh hưởng.<br> // efCoreOptions.UseModelCustomizerForMigrations();<br><br> // Khi ứng dụng khởi động, hủy các ticker còn ở trạng thái Expired hoặc InProgress (đã kết thúc)<br> // để ngăn chặn việc thực thi lại trùng lặp sau sự cố hoặc tắt đột ngột.<br> efCoreOptions.CancelMissedTickersOnAppStart();<br><br> // Các hàm dựa trên cron được định nghĩa tự động được seed vào cơ sở dữ liệu theo mặc định.<br> // Ví dụ: [TickerFunction(..., "*/5 * * * *")]<br> // Sử dụng điều này để bỏ qua chúng và giữ seed chỉ trong runtime.<br> efCoreOptions.IgnoreSeedMemoryCronTickers();<br> });<br>});

TickerQ sử dụng EF Core để định nghĩa các bảng/entity nội bộ của riêng nó.

Bạn có hai lựa chọn:

1. Sử dụng tùy chỉnh mô hình tích hợp cho migrations

Nếu bạn gọi UseModelCustomizerForMigrations(), các cấu hình entity TickerQ được áp dụng tự động trong quá trình migrations. Điều này giữ cho mọi thứ sạch sẽ.

2. Cấu hình thủ công trong DbContext

Nếu bạn không sử dụng trình tùy chỉnh mô hình, bạn cần áp dụng cấu hình entity trong OnModelCreating một cách rõ ràng, ví dụ:

public class ReportDbContext : DbContext<br>{<br> public ReportDbContext(DbContextOptions<ReportDbContext> options)<br> : base(options) { }<br><br> public DbSet<Report> Reports { get; set; } = null!;<br><br> protected override void OnModelCreating(ModelBuilder modelBuilder)<br> {<br> base.OnModelCreating(modelBuilder);<br><br> modelBuilder.HasDefaultSchema(DbConsts.SchemaName);<br><br> // Thêm schema TickerQ vào EntityFrameworkCore<br> modelBuilder.ApplyConfiguration(new TimeTickerConfigurations(schema: DbConsts.SchemaName));<br> modelBuilder.ApplyConfiguration(new CronTickerConfigurations(schema: DbConsts.SchemaName));<br> modelBuilder.ApplyConfiguration(new CronTickerOccurrenceConfigurations(schema: DbConsts.SchemaName));<br><br> // ...<br> }<br>}

Sau khi thiết lập, tạo migration EF của bạn như thường lệ với lệnh EF Core CLI:

dotnet ef migrations add InitialMigration -c ReportDbContext

Đăng ký Công việc Động

TickerQ hỗ trợ cả công việc một lần / bị trễ (gọi là TimeTickers) và công việc lặp lại hoặc dựa trên cron (gọi là CronTickers).

Bạn có thể lập lịch công việc động qua các endpoint API hoặc trong quá trình khởi động ứng dụng, với chính sách thử lại, khoảng thời gian tùy chỉnh và hơn thế nữa.

Giả sử bạn có một endpoint API nơi người dùng yêu cầu gửi thông báo tại một ngày/giờ trong tương lai.

Bạn có thể sử dụng ITimeTickerManager<TimeTicker> để lập lịch công việc như vậy:

public record NotificationJobContext(string Title, string Content);<br><br>app.MapPost("/api/schedule‑notification", async (NotificationRequest request, <br> ITimeTickerManager<TimeTicker> timeTickerManager) =><br>{<br> if (request.ScheduledTime <= DateTime.Now)<br> {<br> return Results.BadRequest("Scheduled time must be in the future");<br> }<br><br> // Tạo dữ liệu công việc được định kiểu.<br> var jobData = new NotificationJobContext(request.Title, request.Content);<br><br> // Lập lịch công việc.<br> var result = await timeTickerManager.AddAsync(new TimeTicker<br> {<br> Function = "Send Notifications",<br> ExecutionTime = request.ScheduledTime.ToUniversalTime(),<br> Request = TickerHelper.CreateTickerRequest(jobData),<br> Retries = 3,<br> RetryIntervals = new[] { 30, 60, 120 } // Thử lại sau 30s, 60s, sau đó 2 phút<br> });<br><br> return Results.Ok(new<br> {<br> JobId = result.Result.Id,<br> Message = $"Notification '{request.Title}' scheduled for {request.ScheduledTime}"<br> });<br>});

Ở đây tôi truyền NotificationJobContext vào công việc và tôi có thể trích xuất nó trong phương thức công việc:

public class NotificationJob<br>{<br> private readonly ILogger<NotificationJob> _logger;<br> public const string TitleKey = "Title";<br> public const string ContentKey = "Content";<br><br> public NotificationJob(ILogger<NotificationJob> logger)<br> {<br> _logger = logger;<br> }<br><br> [TickerFunction(functionName: "Send Notifications", cronExpression: "0 0 * * *" )]<br> public Task Execute(TickerFunctionContext<NotificationJobContext> tickerContext,<br> CancellationToken cancellationToken)<br> {<br> var title = tickerContext.Request.Title;<br> var content = tickerContext.Request.Content;<br><br> // ...<br><br> return Task.CompletedTask;<br> }<br>}

Thay vào đó, bạn có thể sử dụng ICronTickerManager<CronTicker> để lập lịch công việc lặp lại:

var result = await cronTickerManager.AddAsync(new CronTicker<br>{<br> Function = "Send Notifications",<br> Request = TickerHelper.CreateTickerRequest(jobData),<br> Expression = "* * * * *",<br> Retries = 3,<br> RetryIntervals = new[] { 30, 60, 120 } // Thử lại sau 30s, 60s, sau đó 2 phút<br>});

Đây là cách bạn có thể cập nhật và xóa một CronTicker:

// Cập nhật CronTicker theo ID<br>// Bạn nhận được ID khi lập lịch công việc (result.Result.Id)<br>await cronTickerManager.UpdateAsync(tickerId, ticker =><br>{<br> ticker.Description = "Updated cron description";<br> ticker.Expression = "*/10 * * * *"; // mỗi 10 phút<br>});<br><br>// Xóa CronTicker theo ID<br>await cronTickerManager.DeleteAsync(tickerId);

Sử dụng TickerQ Dashboard

TickerQ có bảng điều khiển UI tích hợp, chính hãng cung cấp cho bạn khả năng hiển thị và kiểm soát thời gian thực đối với tất cả các công việc được lập lịch (cả dựa trên thời gian và dựa trên cron).

Nó hữu ích cho việc gỡ lỗi, bảo trì và quản lý khối lượng công việc production.

Thêm gói NuGet TickerQ.Dashboard vào dự án của bạn.

dotnet add package TickerQ.Dashboard

Đăng ký hỗ trợ dashboard khi cấu hình TickerQ trong DI:

builder.Services.AddTickerQ(options =><br>{<br> // ...<br> <br> options.AddOperationalStore<ReportDbContext>(efCoreOptions =><br> {<br> // ...<br> });<br> <br> options.AddDashboard(x =><br> {<br> x.BasePath = "/tickerq-dashboard";<br> x.EnableBasicAuth = true;<br> });<br>});

Bạn cũng có thể cấu hình xác thực cho giao diện người dùng của bảng điều khiển; xem tài liệu để biết thêm chi tiết.

TickerQ hỗ trợ Xác thực Cơ bản ngay lập tức cho giao diện người dùng của bảng điều khiển. Bạn có thể cấu hình thông tin đăng nhập trong appsettings.json của mình:

"TickerQBasicAuth": {<br> "Username": "admin",<br> "Password": "admin"<br>}

Để truy cập bảng điều khiển, điều hướng đến https://localhost:5001/tickerq-dashboard trong trình duyệt của bạn (bất kể cổng của bạn là gì).

Tóm tắt

TickerQ là một thư viện lập lịch hiện đại, nhẹ và sẵn sàng cho production cho các ứng dụng .NET — mang đến một giải pháp thay thế mới mẻ cho Hangfire và Quartz.NET.

Bạn có thể xem Quartz yêu cầu nhiều code như thế nào khi so sánh với TickerQ trong bài viết blog này.

TickerQ loại bỏ nhiều mã mẫu và thủ tục mà chúng ta đã quen thuộc, và thay vào đó cung cấp trải nghiệm thân thiện với nhà phát triển, ưu tiên code-first. Nó tích hợp sẵn với EF Core, có bảng điều khiển đẹp mắt và hỗ trợ đăng ký công việc động.

Thẻ: TickerQ, .NET, ASP.NET Core, EF Core, Job Scheduler, Background Jobs, C#, Cron, Hangfire alternative, Quartz alternative

Chỉ mục