.NET ve modern ASP.NET Core uygulamalarında arka planda düzenli çalışan zamanlanmış görevler kurgulamak, e-ticaret stok senkronizasyonundan fatura kesimine, log temizleme rutinlerinden veri ambarı aktarımına kadar her ölçekte sistemin temel ihtiyacıdır. Geliştiriciler sıklıkla çatının sunduğu dahili BackgroundService veya IHostedService altyapısına yönelseler de bu yapılar bellekte (in-memory) yaşar; uygulama yeniden başlatıldığında çalışma durumu silinir, standart zamanlama ifadeleri sunulmaz ve başarısız görevler için otomatik telafi bulunmaz.
Kurumsal ve ölçeklenebilir .NET projelerinde bu gereksinimleri karşılamak için iki olgun kütüphane öne çıkar: Hangfire ve Quartz.NET. Hangfire, görsel web yönetim paneli (dashboard), hazır veritabanı kalıcılığı ve minimum konfigürasyon adımlarıyla web uygulamalarında büyük kolaylık sağlar. Quartz.NET ise Java dünyasından gelen zengin geçmişi, takvim tabanlı esnek tetikleyicileri, gelişmiş kümeleme (clustering) ve alt seviye kontrol yetenekleriyle karmaşık zamanlama senaryolarında tercih edilir.
Bu rehberde, her iki kütüphanenin kurulum adımlarını, SQL Server ve PostgreSQL depolama konfigürasyonlarını, IIS barındırmasında görevlerin aniden durmasına yol açan kritik Idle Timeout tuzağını ve gerçek kod örneklerini inceleyeceksiniz. Ayrıca ağır veritabanı tabloları kurmadan ASP.NET Core Minimal API uç noktalarını harici olarak yönetmek için ücretsiz cron job servisi mimarisinin nasıl devreye alınacağını adım adım öğreneceksiniz.
Java ve .NET ekipleri aynı zamanlama kurallarını taşırken alan sayılarını karıştırmamak gerekir. Spring Boot @Scheduled ve ShedLock kullanımı, altı alanlı Spring ifadeleriyle küme kilidini birlikte ele alır; Quartz.NET'in saniye içeren ifadelerini ve ? kullanımını bunlarla birebir aynı kabul etmeyin.
.NET ve ASP.NET Core Ekosisteminde Zamanlanmış Görev Mimarisi
Modern bir ASP.NET Core uygulamasında arka plan görevlerini koşturmak için temelde üç ana yaklaşım bulunur: Çatı ile gelen yerel servisler, uygulama içi iş kuyruğu kütüphaneleri ve harici HTTP tetikleyiciler. Dahili BackgroundService sınıfı, CancellationToken desteğiyle bir döngü içerisinde Task.Delay işleterek basit periyodik yoklamalar yapabilir. Ancak "her ayın son iş günü saat 23:45'te çalış" gibi takvim gereksinimlerinde standart crontab sözdizimi desteği sunmadığından yetersiz kalır.
Görev yönetiminde en kritik ayrım noktası kalıcılık (persistence) güvencesidir. Bellek üzerinde çalışan bir zamanlayıcı, sunucu yeniden başladığında veya yeni bir derleme dağıtıldığında (deployment) sıfırlanır; o sırada çalışan veya sıraya girmiş görevler kaybolur. Hangfire ve Quartz.NET (AdoJobStore ile), görevlerin durumunu ilişkisel veritabanı tablolarında saklayarak uygulama çökse dahi ayağa kalktığında iş kayıtlarının korunmasını ve kurtarma ayarlarına göre yeniden yürütülmesini sağlar; yarıda kalan kod aynı satırdan devam etmez. Windows ortamında yerel sunucu yönetimi yapan ekipler alternatif olarak Windows Zamanlanmış Görevler aracını da tercih edebilmektedir.
Hangfire vs Quartz.NET: Kapsamlı Karşılaştırma Tablosu
Projenizin gereksinimlerine en uygun kütüphaneyi seçebilmeniz için öne çıkan teknik farklar aşağıdaki tabloda özetlenmiştir:
| Özellik | Hangfire | Quartz.NET |
|---|---|---|
| Görsel Web Dashboard | Yerleşik, modern ve gerçek zamanlı web paneli | Yerleşik yok (harici topluluk eklentileri gerekir) |
| Kalıcı Depolama (Storage) | SQL Server, PostgreSQL, Redis, MongoDB | AdoJobStore (SQL Server, Postgres, Oracle, SQLite) veya RAM |
| Kurulum ve Yapılandırma | Çok kolay, birkaç satır DI kaydı ile hazır | Orta, JobDetail ve Trigger tanımları gerektirir |
| Eşzamanlılık (Concurrency) | DisableConcurrentExecution veya özel filtre | [DisallowConcurrentExecution] niteliği ile doğrudan |
| Kümeleme (Clustering) | Temel sürümde ortak depolama üzerinden çoklu sunucu desteği | Ücretsiz ve açık kaynak (AdoJobStore üzerinden) |
| CRON Formatı Desteği | 5 alanlı standart UNIX CRON | 6-7 alanlı Quartz CRON (saniye ve ? desteği) |
| Yeniden Deneme (Retry) | Otomatik ve görsel olarak izlenebilir | Manuel yapılandırma veya özel IJobListener gerekir |
| Dağıtım Tipi | Monolitik web ve Worker Service uyumlu | Arka plan Worker Service ve konsol servisleri |
Adım Adım Hangfire Kurulumu ve RecurringJob Tanımlama
Hangfire'ı ASP.NET Core uygulamanıza dahil etmek için öncelikle gerekli NuGet paketlerini kurmanız ve servis koleksiyonuna kaydetmeniz gerekir. Aşağıdaki adımları sırasıyla takip edin:
- Terminal üzerinden çekirdek Hangfire ve veritabanı sağlayıcı paketini projenize ekleyin.
Program.csdosyası içerisinde Hangfire servislerini ve veritabanı bağlantı dizesini tanımlayın.- HTTP istek ardışık düzenine (middleware pipeline) Hangfire Dashboard rotasını ekleyin.
- Uygulama ayağa kalktığında çalışacak olan
RecurringJobkaydını cron ifadesiyle sisteme kaydedin.
dotnet add package Hangfire.AspNetCore
dotnet add package Hangfire.SqlServer
SQL Server ve PostgreSQL Depolama Konfigürasyonu
Hangfire, veritabanına bağlandığında gerekli şema tablolarını (Hangfire.Job, Hangfire.State, Hangfire.Set vb.) otomatik olarak üretir. Bu nedenle bağlantı dizesinde tanımlı kullanıcının veritabanında tablo oluşturma (DDL) yetkisine sahip olması şarttır. PostgreSQL tercih ediyorsanız Hangfire.PostgreSql paketini kullanabilir ve yapılandırmayı buna göre ayarlayabilirsiniz.
// Program.cs
using Hangfire;
using Hangfire.SqlServer;
var builder = WebApplication.CreateBuilder(args);
// Hangfire depolama ve sunucu servisi kaydı
builder.Services.AddHangfire(configuration => configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSqlServerStorage(builder.Configuration.GetConnectionString("DefaultConnection"), new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
UseRecommendedIsolationLevel = true,
DisableGlobalLocks = true
}));
builder.Services.AddHangfireServer(options =>
{
options.WorkerCount = Environment.ProcessorCount * 2;
});
var app = builder.Build();
Hangfire Dashboard Yetkilendirmesi (IDashboardAuthorizationFilter)
Hangfire Dashboard varsayılan olarak yalnızca yerel makineden (localhost) gelen isteklere izin verir. Canlı ortama alındığında harici IP adreslerinden gelen tüm erişim istekleri güvenlik amacıyla 401 veya 403 hatasıyla geri çevrilir. Dashboard'u canlı ortamda güvenle açmak için IDashboardAuthorizationFilter arayüzünü uygulayan bir yetkilendirme filtresi yazılmalıdır:
using Hangfire.Dashboard;
public class HangfireAdminAuthFilter : IDashboardAuthorizationFilter
{
public bool Authorize(DashboardContext context)
{
var httpContext = context.GetHttpContext();
// Örnek: Yalnızca oturum açmış Admin rolündeki kullanıcılara izin ver
return httpContext.User.Identity?.IsAuthenticated == true
&& httpContext.User.IsInRole("Administrator");
}
}
// Program.cs içinde middleware kaydı:
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new HangfireAdminAuthFilter() }
});
Adım Adım Quartz.NET Kurulumu ve IJob Yapısı
Quartz.NET, özellikle bağımsız çalışan .NET Worker Service yapılarında ve ayrıntılı zamanlama gerektiren sistemlerde üstün performans sunar. Microsoft Dependency Injection (DI) altyapısıyla tam uyumlu çalışır.
- Projeye Quartz ve Hosting entegrasyon paketlerini ekleyin.
- Zamanlanacak görevi temsil eden ve
IJobarayüzünden türeyen sınıfı oluşturun. Program.csiçinde Quartz servisini, Job tanımını ve CRON tetikleyicisini (Trigger) kaydedin.- Uygulama kapanırken devam eden işlerin güvenle tamamlanmasını sağlayan Hosted Service seçeneğini aktifleştirin.
dotnet add package Quartz --version '3.*'
dotnet add package Quartz.Extensions.Hosting --version '3.*'
[DisallowConcurrentExecution] ile Eşzamanlılığı Yönetme
Quartz.NET'te periyodu 5 dakika olan bir görevin yürütülmesi 8 dakika sürerse, varsayılan ayarlarda 5. dakikada ikinci bir iş kopyası paralel olarak başlatılır. Bu durum veritabanında kilitlenmelere, aynı verinin iki kez işlenmesine ve sunucu kaynaklarının tükenmesine sebep olur. İş sınıfının başına [DisallowConcurrentExecution] niteliği eklendiğinde Quartz, çalışan mevcut yürütme sonlanana kadar aynı görevin yeni bir tetiklemesini başlatmaz ve sıraya alır.
using Quartz;
[DisallowConcurrentExecution]
public class GunlukRaporJob : IJob
{
private readonly ILogger<GunlukRaporJob> _logger;
private readonly ApplicationDbContext _dbContext;
public GunlukRaporJob(ILogger<GunlukRaporJob> logger, ApplicationDbContext dbContext)
{
_logger = logger;
_dbContext = dbContext;
}
public async Task Execute(IJobExecutionContext context)
{
_logger.LogInformation("Günlük rapor derleme görevi başladı: {Zaman}", DateTimeOffset.Now);
// Veritabanı işlemleri ve rapor hesaplama adımları
await Task.Delay(1000);
_logger.LogInformation("Rapor başarıyla tamamlandı.");
}
}
IIS Idle Timeout ve AppPool Geri Dönüşümü (Recycling) Tehlikesi
ASP.NET Core uygulamanızı Windows Server üzerinde IIS arkasında barındırıyorsanız, zamanlayıcıların belirli bir süre sonra aniden durmasıyla karşılaşabilirsiniz. Bu durumun ana sebebi IIS Application Pool (Uygulama Havuzu) varsayılan ayarlarıdır. IIS, 20 dakika boyunca herhangi bir HTTP isteği almayan uygulama havuzlarını otomatik olarak uyku moduna alır (Idle Timeout Shutdown). İşlem durdurulduğunda bellekteki Hangfire veya Quartz arka plan iş parçacıkları da tamamen ölür; gece yarısı çalışması gereken işler tetiklenemez.
Ayrıca IIS, varsayılan olarak her 29 saatte bir (1740 dakika) havuzu otomatik olarak geri dönüştürür (recycling). Zamanlanmış görevlerinizin kesintisiz çalışması için IIS üzerinde şu ayarları kontrol edin:
- IIS Yöneticisi'ni açın, sol panelden Application Pools bölümüne girin ve uygulamanızın havuzunu seçip Advanced Settings penceresini açın.
- Idle Time-out (minutes) değerini
20yerine0olarak değiştirin. Böylece boşta kalma nedeniyle kapanma engellenir. - Start Mode ayarını
OnDemandyerineAlwaysRunningyapın. - Sol panelden sitenizi seçip Advanced Settings alanından Preload Enabled değerini
Trueyapın. Bu sayede sunucu yeniden başlatılsa dahi uygulama ilk HTTP isteğini beklemeden derhal ayağa kalkar.
Uygulamayı Linux veya konteyner üzerinde çalıştıran ekipler bu tip işletim sistemi kısıtlamalarından muaftır; nitekim Docker cron job yaklaşımlarında süreç konteyner ömrü boyunca sürekli aktif kalır.
Gerçek Kod Örnekleri ve Servis Entegrasyonu
Aşağıdaki örneklerde hem Hangfire hem de Quartz.NET için üretim standartlarında servis kaydı ve zamanlama tanımları gösterilmiştir. Her gün gece 02:00'de çalışan bir senkronizasyon rutini modellenmiştir.
Hangfire ile Gece 02:00 Görevi
// Program.cs - Hangfire RecurringJob Tanımı
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new HangfireAdminAuthFilter() }
});
// Her gün gece 02:00'de çalışacak görev kaydı
RecurringJob.AddOrUpdate<IFaturaServisi>(
"gece-fatura-kesim",
servis => servis.FaturaKesimRutininiBaslatAsync(),
"0 2 * * *",
new RecurringJobOptions
{
TimeZone = TimeZoneInfo.FindSystemTimeZoneById("Turkey Standard Time")
}
);
app.Run();
Quartz.NET ile DI Destekli Servis Kaydı
// Program.cs - Quartz Servis ve Trigger Kaydı
builder.Services.AddQuartz(q =>
{
// Quartz 6 alanlı cron: Saniye Dakika Saat Gün Ay Gün-Kodu
var jobKey = new JobKey("gunluk-rapor-job", "raporlama");
q.AddJob<GunlukRaporJob>(opts => opts.WithIdentity(jobKey));
q.AddTrigger(opts => opts
.ForJob(jobKey)
.WithIdentity("gunluk-rapor-trigger", "raporlama")
.WithCronSchedule("0 0 2 * * ?", cron => cron
.InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Turkey Standard Time")))
);
});
builder.Services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);
Sık Karşılaşılan Hatalar ve Çözümleri
.NET zamanlayıcı kütüphanelerinde en sık karşılaşılan hatalar, nedenleri ve çözüm adımları aşağıda derlenmiştir:
JobStorage.Current has not been initialized
Bu hata, RecurringJob.AddOrUpdate çağrısının Hangfire servisleri henüz IoC konteynerine tanıtılmadan veya AddHangfire çağrısı yapılmadan önce tetiklenmesi durumunda fırlatılır. Çözüm için builder.Services.AddHangfire(...) bloğunu kodun en üst servis kayıt bölümüne alın; job tanımlama komutlarını ise mutlaka builder.Build() satırından sonraya, middleware zincirinin tamamlandığı konuma taşıyın.
IIS Uygulama Havuzunun Kapanmasıyla Görevlerin Durması
IIS üzerinde barındırılan web servislerinde gece saatlerinde zamanlanmış işlerin hiç çalışmadığı görülür. Nedeni, IIS varsayılan Idle Time-out süresinin (20 dakika) dolması ve gelen web trafiği olmadığı için w3wp sürecinin kapatılmasıdır. Çözüm için IIS Application Pool ayarlarından Idle Time-out değerini 0 yapın, Start Mode'u AlwaysRunning olarak güncelleyin ve Windows Server üzerinde "Application Initialization" rol bileşenini aktifleştirin.
Quartz CRON İfadesinde Alan Sayısı Hatası
Quartz.NET CRON sözdizimi standart Linux cron formatından farklı olarak saniye hanesi içerir ve ayın günü ile haftanın günü alanlarından birinde çakışmayı önlemek için mutlaka soru işareti (?) karakterini bekler. Beş alanlı standart bir cron ifadesi doğrudan Quartz 3.x'e verildiğinde cron ayrıştırma hatası meydana gelir. Çözüm olarak ifadenin 6 haneli (örneğin 0 0 2 * * ?) olduğundan ve gün alanında soru işareti bulunduğundan emin olun.
Hangfire Dashboard 401 veya 403 Erişim Engeli
Uygulama yerel geliştirme makinesinde sorunsuz çalışırken canlı sunucuya yüklendiğinde /hangfire paneline girilemez. Hangfire'ın yerleşik LocalRequestsOnlyAuthorizationFilter filtresi yalnızca yerel istek olarak tanınan çağrılara izin verir. Çözüm olarak yukarıda kod örneği sunulan IDashboardAuthorizationFilter sınıfını yazıp DashboardOptions.Authorization koleksiyonuna dahil edin.
cronjob.com.tr ile HTTP Tabanlı Alternatif Kurulum (Minimal API & Cloud Trigger)
Hangfire veya Quartz.NET kurmak her zaman en verimli yol olmayabilir. İlişkisel veritabanında onlarca tablo açmak, IIS uygulama havuzunun kapanmasını önlemek için sunucu ayarlarıyla uğraşmak ve çoklu sunucularda eşzamanlılık kilitlerini yönetmek ciddi bir bakım maliyeti yaratır. Özellikle modern mikroservis ve bulut ortamlarında, zamanlanacak işlemi bir ASP.NET Core Minimal API uç noktası arkasına gizleyip harici bir bulut zamanlayıcı ile tetiklemek çok daha hafif ve sürdürülebilir bir mimaridir.
Bu mimaride uygulamanızda korumalı bir HTTP endpoint'i tanımlarsınız:
// Program.cs - Minimal API Cron Tetikleme Uç Noktası
app.MapPost("/api/jobs/sync-invoices", async (HttpContext context, IFaturaServisi faturaServisi, IConfiguration config) =>
{
var expectedToken = config["CronSettings:SecretToken"];
if (string.IsNullOrEmpty(expectedToken) || !context.Request.Headers.TryGetValue("X-Cron-Token", out var token) || token != expectedToken)
{
return Results.Unauthorized();
}
await faturaServisi.FaturaKesimRutininiBaslatAsync();
return Results.Ok(new { status = "tamamlandi", timestamp = DateTimeOffset.UtcNow });
});
Ardından bu güvenli uç noktayı cronjob.com.tr paneli üzerinden zamanlarsınız. Bu yaklaşımın teknik güvenceleri ve platform kuralları şunlardır:
cronjob.com.tr tamamen ücretsizdir ve kayıt esnasında kredi kartı talep edilmez. Sistem üzerinde tanımlayabileceğiniz en kısa çalışma aralığı 60 saniyedir (1 dakika). Zamanlama planınızı hazır aralıklarla belirleyebileceğiniz gibi, 5 alanlı standart cron ifadesi (dakika, saat, ayın günü, ay, haftanın günü) kullanarak da kurgulayabilirsiniz. Saat dilimi iş bazında seçilebilir ve varsayılan olarak Europe/Istanbul kullanılır.
Platform; GET, POST, PUT, PATCH, DELETE, HEAD ve OPTIONS HTTP metodlarını destekler. Uç noktanıza özel başlık (örneğin X-Cron-Token: GizliAnahtar), istek gövdesi veya HTTP Basic Auth ekleyebilirsiniz. Yanıt bekleme (timeout) süresi 1 dakikalık aralıkta 55 saniye, 5 dakika ve üzeri aralıklarda ya da cron ifadeli işlerde 290 saniyedir. HTTP 200–399 arası durum kodları ve ağ hatası yaşanmaması görevin başarılı sayılması için yeterlidir. Sistem en fazla 5 yönlendirmeyi izler ve HTTPS sertifikasını doğrular.
Güvenlik politikası gereği localhost veya özel ağ (10.x, 192.168.x vb.) adreslerine istek gönderilmez; uç noktanız genel ağdan erişilebilir olmalıdır. Yapılan istekler CronJobComTr/1.0 (+https://cronjob.com.tr) User-Agent başlığıyla iletilir. Varsayılan olarak 3 ardışık hata eşiği aşıldığında sistem yöneticisine erişim sorunu e-postası gönderilir; servis düzeldiğinde ise kesinti süresini bildiren düzeltme e-postası iletilir (aynı iş için bildirimler arasında en az 60 dakika bulunur). Her çalışmanın durum kodu, yanıt süresi, hata ayrıntısı ve yanıt gövdesinin ilk ~64 KB'ı 7 gün boyunca loglarda saklanır. Kurum içi mimarileri kıyaslamak için diğer cron job alternatifleri listesine göz atabilirsiniz.
Sıkça Sorulan Sorular (SSS)
Hangfire ücretsiz midir?
Hangfire'ın temel kütüphanesi ve web dashboard bileşeni açık kaynaklı olup ücretsizdir. Batch jobs ve batch continuations ticari Hangfire Pro lisansı kapsamındadır; tekil görev continuations temel sürümde bulunur.
Hangfire için veritabanı zorunlu mudur?
Evet, Hangfire görev durumlarını ve zamanlama kayıtlarını saklamak için SQL Server, PostgreSQL veya Redis gibi kalıcı bir depolama sağlayıcısına ihtiyaç duyar.
Quartz.NET mi Hangfire mı tercih edilmeli?
Görsel bir yönetim paneli ve hızlı kurulum önceliğinizse Hangfire; karmaşık takvim kuralları ve gelişmiş kümeleme desteği gerekiyorsa Quartz.NET tercih edilmelidir.
ASP.NET Core IHostedService cron için yeterli değil midir?
Basit periyodik timer işlemleri için yeterlidir ancak cron ifadeleri, görev geçmişi, otomatik yeniden deneme ve kalıcılık desteği için harici bir zamanlayıcı gerekir.
Hemen Ücretsiz Cron Job Oluşturun
ASP.NET Core Minimal API görevinizi cronjob.com.tr üzerinden POST isteğiyle zamanlayın; ardışık hata eşiği aşıldığında e-posta alın ve çalışma kayıtlarında sunucunuzun döndürdüğü yanıtı inceleyin.
Ücretsiz Başla →