تسکهای پسزمینه دورهای (Periodic) ⏰
گاهی اوقات میخواهیم یک تسک پسزمینه را به طور مداوم اجرا کنیم و آن را برای انجام عملیاتی به صورت تکراری واداریم. برای مثال، میخواهیم هر ده ثانیه پیامها را از یک صف مصرف کنیم. چگونه این را بسازیم؟
در اینجا یک مثال از PeriodicBackgroundTask برای شروع آمده است:
public class PeriodicBackgroundTask : BackgroundService
{
private readonly TimeSpan _period = TimeSpan.FromSeconds(5);
private readonly ILogger<PeriodicBackgroundTask> _logger;
public PeriodicBackgroundTask(ILogger<PeriodicBackgroundTask> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using PeriodicTimer timer = new PeriodicTimer(_period);
while (!stoppingToken.IsCancellationRequested &&
await timer.WaitForNextTickAsync(stoppingToken))
{
_logger.LogInformation("Executing PeriodicBackgroundTask");
}
}
}
ما از یک PeriodicTimer ⏳ برای انتظار غیرهمزمان برای یک دوره زمانی معین، قبل از اجرای تسک پسزمینه خود استفاده میکنیم.
اگر به راهحل قویتری نیاز داشتید چه؟ 🚀
تا الان باید واضح باشد که IHostedService زمانی مفید است که به تسکهای پسزمینه سادهای نیاز دارید که تا زمانی که اپلیکیشن شما در حال اجراست، فعال هستند.
چه میشود اگر بخواهید یک تسک پسزمینه زمانبندیشده داشته باشید که هر روز ساعت ۲ بامداد اجرا شود؟
شما احتمالاً میتوانید چیزی شبیه این را خودتان بسازید، اما راهحلهای موجودی وجود دارد که باید ابتدا آنها را در نظر بگیرید.
در اینجا دو راهحل محبوب برای اجرای تسکهای پسزمینه آمده است:
Quartz 🔥
Hangfire 🔥
📌ادامه دارد...
🔖 هشتگها:
#CSharp #DotNet #BackgroundTask #HostedService #WorkerService #SystemDesign #TaskScheduling
پیکربندی Job ⚙️
ما قبلاً ProcessOutboxMessagesJob را پیادهسازی کردیم.
کتابخانه Quartz.NET مسئولیت scheduler را بر عهده خواهد گرفت.
و این ما را با پیکربندی trigger برای ProcessOutboxMessagesJob تنها میگذارد.
services.AddQuartz(configure =>
{
var jobKey = new JobKey(nameof(ProcessOutboxMessagesJob));
configure
.AddJob<ProcessOutboxMessagesJob>(jobKey)
.AddTrigger(
trigger => trigger.ForJob(jobKey).WithSimpleSchedule(
schedule => schedule.WithIntervalInSeconds(10).RepeatForever()));
configure.UseMicrosoftDependencyInjectionJobFactory();
});
ما باید background job خود را با یک JobKey به طور منحصر به فرد شناسایی کنیم.
فراخوانی AddJob، ProcessOutboxMessagesJob را با DI و همچنین با Quartz ثبت میکند.
پس از آن ما یک تریگر برای این job با فراخوانی AddTrigger پیکربندی میکنیم. در این مثال، من job را طوری زمانبندی میکنم که هر ده ثانیه اجرا شود و تا زمانی که سرویس میزبانی شده در حال اجراست، برای همیشه تکرار شود.
Quartz
همچنین از پیکربندی تریگرها با استفاده از عبارات cron پشتیبانی میکند.
نکات پایانی 💡
Quartz.NET
اجرای background jobها در NET. را آسان میکند و شما میتوانید از تمام قدرت DI در background jobهای خود استفاده کنید. همچنین برای نیازمندیهای مختلف زمانبندی با پیکربندی از طریق کد یا استفاده از عبارات cron انعطافپذیر است.
چند مورد برای بهبود وجود دارد تا زمانبندی jobها آسانتر و boilerplate کمتر شود:
🔹 افزودن یک متد توسعه برای سادهسازی پیکربندی jobها با زمانبندی ساده.
🔹 افزودن یک متد توسعه برای سادهسازی پیکربندی jobها با عبارات cron از تنظیمات اپلیکیشن.
🔖 هشتگها:
#CSharp #DotNet #QuartzNet #BackgroundJobs #TaskScheduling #HostedService #SystemDesign
زمانبندی Jobهای تکرارشونده 🔄
برای jobهای پسزمینه تکرارشونده، میتوانید از زمانبندی cron استفاده کنید:
app.MapPost("/api/reminders/schedule/recurring", async (
ISchedulerFactory schedulerFactory,
RecurringReminderRequest request) =>
{
// ... (Job creation is the same) ...
var trigger = TriggerBuilder.Create()
.WithIdentity($"recurring-trigger-{Guid.NewGuid()}", "recurring-reminders")
.WithCronSchedule(request.CronExpression)
.Build();
await scheduler.ScheduleJob(job, trigger);
return Results.Ok();
});تریگرهای Cron قدرتمندتر از تریگرهای ساده هستند. آنها به شما اجازه میدهند زمانبندیهای پیچیدهای مانند "هر روز کاری ساعت ۱۰ صبح" یا "هر ۱۵ دقیقه" را تعریف کنید.
راهاندازی پایداری Job (Job Persistence) 💾
بهطور پیشفرض، Quartz از ذخیرهسازی درون-حافظهای استفاده میکند، که یعنی jobهای شما با ریاستارت شدن اپلیکیشن از بین میروند. برای محیطهای پروداکشن، شما به یک فروشگاه پایدار (persistent store) نیاز دارید.
بیایید ببینیم چگونه ذخیرهسازی پایدار را با ایزولهسازی اسکیمای مناسب راهاندازی کنیم:
builder.Services.AddQuartz(options =>
{
options.AddJob<EmailReminderJob>(c => c
.StoreDurably()
.WithIdentity(EmailReminderJob.Name));
options.UsePersistentStore(persistenceOptions =>
{
persistenceOptions.UsePostgres(cfg =>
{
cfg.ConnectionString = connectionString;
cfg.TablePrefix = "scheduler.qrtz_";
});
persistenceOptions.UseNewtonsoftJsonSerializer();
});
});
تنظیم TablePrefix به سازماندهی جداول Quartz در دیتابیس شما کمک میکند - در این مورد، آنها را در یک اسکیمای اختصاصی scheduler قرار میدهد.
جاب های بادوام (Durable Jobs) 📌
توجه کنید که ما EmailReminderJob را با StoreDurably پیکربندی میکنیم. این یک الگوی قدرتمند است که به شما اجازه میدهد jobهای خود را یک بار تعریف کرده و با تریگرهای مختلف از آنها استفاده مجدد کنید.
public async Task ScheduleReminder(string userId, string message, DateTime scheduledTime)
{
var scheduler = await _schedulerFactory.GetScheduler();
// Reference the stored job by its identity
var jobKey = new JobKey(EmailReminderJob.Name);
var trigger = TriggerBuilder.Create()
.ForJob(jobKey) // Reference the durable job
.WithIdentity($"trigger-{Guid.NewGuid()}")
.UsingJobData("userId", userId)
.UsingJobData("message", message)
.StartAt(scheduledTime)
.Build();
await scheduler.ScheduleJob(trigger); // Note: just passing the trigger
}
خلاصه ✅
راهاندازی صحیح Quartz در NET. شامل موارد بیشتری از صرفاً افزودن پکیج NuGet است.
به این موارد توجه کنید:
🔹 تعریف صحیح job و مدیریت داده با JobDataMap
🔹 راهاندازی زمانبندی jobهای یکباره و تکرارشونده
🔹 پیکربندی ذخیرهسازی پایدار با ایزولهسازی اسکیمای مناسب
🔹 استفاده از jobهای بادوام برای حفظ تعاریف ثابت job
هر یک از این عناصر به یک سیستم پردازش پسزمینه قابل اعتماد کمک میکند که میتواند با نیازهای اپلیکیشن شما رشد کند.
🔖 هشتگها:
#CSharp #DotNet #ASPNETCore #QuartzNet #BackgroundJobs #TaskScheduling #Observability #SystemDesign