Quartz.NET¶
Quartz.NET, .NET için güçlü bir iş zamanlama kütüphanesidir; yanlış yapılandırma zamanlama hatalarına ve kaynak israfına yol açar.
1. DI Entegrasyonu Yapmamak¶
❌ Yanlış Kullanım: Job içinde bağımlılıkları manuel oluşturmak.
public class ReportJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
var dbContext = new AppDbContext(); // Manuel oluşturma, DI yok
var service = new ReportService(dbContext);
await service.GenerateAsync();
}
}
✅ İdeal Kullanım: Quartz DI entegrasyonu ile bağımlılıkları inject edin.
builder.Services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionJobFactory();
q.AddJob<ReportJob>(opts => opts.WithIdentity("report-job"));
q.AddTrigger(opts => opts
.ForJob("report-job")
.WithIdentity("report-trigger")
.WithCronSchedule("0 0 2 * * ?")); // Her gün saat 02:00
});
builder.Services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
public class ReportJob : IJob
{
private readonly IReportService _reportService;
private readonly ILogger<ReportJob> _logger;
public ReportJob(IReportService reportService, ILogger<ReportJob> logger)
{
_reportService = reportService;
_logger = logger;
}
public async Task Execute(IJobExecutionContext context)
{
_logger.LogInformation("Rapor oluşturma başladı");
await _reportService.GenerateAsync(context.CancellationToken);
}
}
2. Trigger Konfigürasyonunu Hardcoded Yapmak¶
❌ Yanlış Kullanım: Zamanlama ifadelerini kod içinde sabitlemek.
q.AddTrigger(opts => opts
.ForJob("cleanup-job")
.WithCronSchedule("0 0 3 * * ?")); // Değiştirmek için deploy gerekir
✅ İdeal Kullanım: Konfigürasyondan zamanlamayı okuyun.
builder.Services.AddQuartz(q =>
{
var jobConfig = builder.Configuration.GetSection("Jobs");
q.AddJob<CleanupJob>(opts => opts.WithIdentity("cleanup-job"));
q.AddTrigger(opts => opts
.ForJob("cleanup-job")
.WithIdentity("cleanup-trigger")
.WithCronSchedule(jobConfig["Cleanup:CronExpression"]));
});
// appsettings.json
{
"Jobs": {
"Cleanup": {
"CronExpression": "0 0 3 * * ?",
"Enabled": true
}
}
}
3. Job Persistence Kullanmamak¶
❌ Yanlış Kullanım: RAMJobStore ile job durumunu bellekte tutmak.
builder.Services.AddQuartz(q =>
{
q.UseInMemoryStore(); // Uygulama restart olduğunda çalışan joblar kaybolur
});
✅ İdeal Kullanım: Veritabanı persistence ile job durumunu saklanır hale getirin.
builder.Services.AddQuartz(q =>
{
q.UsePersistentStore(store =>
{
store.UseProperties = true;
store.UseSqlServer(builder.Configuration.GetConnectionString("Quartz"));
store.UseNewtonsoftJsonSerializer();
});
q.UseDefaultThreadPool(tp => tp.MaxConcurrency = 10);
});
4. Misfire Stratejisi Belirlememek¶
❌ Yanlış Kullanım: Misfire (kaçırılan tetikleme) durumunu yönetmemek.
q.AddTrigger(opts => opts
.ForJob("daily-sync")
.WithCronSchedule("0 0 1 * * ?"));
// Uygulama gece 01:00'de kapalıysa job hiç çalışmaz
✅ İdeal Kullanım: Misfire instruction tanımlayarak kaçırılan işleri yönetin.
q.AddTrigger(opts => opts
.ForJob("daily-sync")
.WithIdentity("daily-sync-trigger")
.WithCronSchedule("0 0 1 * * ?", x =>
x.WithMisfireHandlingInstructionFireAndProceed()) // Kaçırılan job'ı hemen çalıştır
.StartNow());
5. Job Execution Context Kullanmamak¶
❌ Yanlış Kullanım: Joblar arası veri paylaşımını global state ile yapmak.
public static class JobState
{
public static DateTime LastRun { get; set; }
public static int ProcessedCount { get; set; }
}
public class SyncJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
JobState.LastRun = DateTime.UtcNow;
JobState.ProcessedCount++;
}
}
✅ İdeal Kullanım: JobDataMap ile job bazlı veri saklayın.
public class SyncJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
var dataMap = context.JobDetail.JobDataMap;
var lastRun = dataMap.GetDateTime("LastRun");
_logger.LogInformation("Son çalışma: {LastRun}", lastRun);
// İşlem sonrası state güncelle
dataMap.Put("LastRun", DateTime.UtcNow);
dataMap.Put("ProcessedCount", dataMap.GetInt("ProcessedCount") + 1);
}
}
// Job tanımında başlangıç verileri
q.AddJob<SyncJob>(opts => opts
.WithIdentity("sync-job")
.UsingJobData("LastRun", DateTime.MinValue)
.UsingJobData("ProcessedCount", 0)
.StoreDurably());