Bloga Dön

Hangfire ile Background Job Processing

Giriş

Modern web uygulamalarında kullanıcı deneyimi kritik öneme sahiptir. Uzun süren işlemler (e-posta gönderimi, rapor oluşturma, veri işleme) kullanıcıyı bekletmemeli ve HTTP request timeout'larına sebep olmamalıdır. Bu noktada background job processing devreye girer.

Hangfire, .NET ekosisteminde en popüler açık kaynak background job processing kütüphanesidir. Bu kapsamlı rehberde, Hangfire'ın özelliklerini, kullanım senaryolarını ve production'a hazır implementasyon detaylarını inceleyeceğiz.

Hangfire Nedir?

Hangfire, ASP.NET uygulamalarında background job'ları yönetmek için kullanılan açık kaynak bir kütüphanedir. En önemli özelliği, herhangi bir Windows Service veya ayrı process gerektirmeden, mevcut web uygulamanız içinde çalışabilmesidir.

Fire-and-Forget

Bir metodu arka planda çalıştır ve unut

Delayed Jobs

Belirli bir süre sonra çalıştırılacak job'lar

Recurring Jobs

Cron ifadeleri ile periyodik job'lar

Continuation

Bir job bittikten sonra çalışacak job'lar

Dashboard

Job'ları izlemek için web arayüzü

Auto Retry

Başarısız job'ları otomatik yeniden deneme

Neden Hangfire?

Hangfire'ın Avantajları

  • Kolay kurulum ve kullanım (5 dakikada başlayabilirsiniz)
  • Built-in dashboard ile görselleştirme
  • Kod değişikliği gerektirmeden mevcut metotları background job yapabilme
  • SQL Server, PostgreSQL, MySQL, Redis gibi birden fazla storage desteği
  • Otomatik retry ve error handling mekanizması
  • Açık kaynak ve aktif topluluk desteği

Kurulum

1. NuGet Paket Yükleme

Install-Package Hangfire
Install-Package Hangfire.SqlServer
// veya PostgreSQL için
Install-Package Hangfire.PostgreSql

2. Startup Configuration

using Hangfire;
using Hangfire.SqlServer;

// Service Registration
builder.Services.AddHangfire(configuration => configuration
    .SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
    .UseSimpleAssemblyNameTypeSerializer()
    .UseRecommendedSerializerSettings()
    .UseSqlServerStorage("ConnectionString"));

builder.Services.AddHangfireServer();

// Pipeline Configuration
app.UseHangfireDashboard("/hangfire");

Job Türleri

1. Fire-and-Forget Jobs

Bir metodu arka planda bir kez çalıştırır ve sonucu beklemez.

Kullanım Senaryoları

  • E-posta gönderimi
  • SMS bildirim gönderme
  • Log kaydı oluşturma
  • Dosya yükleme sonrası işleme
  • Webhook çağrısı yapma
// E-posta servisini arka planda çalıştır
BackgroundJob.Enqueue<EmailService>(x => 
    x.SendWelcomeEmail(user.Email, user.Name));

2. Delayed Jobs

Belirli bir süre sonra çalıştırılacak job'lar.

// 14 gün sonra hatırlatma gönder
BackgroundJob.Schedule<ReminderService>(
    x => x.SendTrialExpirationReminder(user.Id),
    TimeSpan.FromDays(14));

// Veya belirli bir tarihte
BackgroundJob.Schedule<ReminderService>(
    x => x.SendTrialExpirationReminder(user.Id),
    new DateTime(2024, 12, 31, 23, 59, 59));

3. Recurring Jobs

Belirli aralıklarla sürekli çalışan job'lar. Cron ifadeleri ile yapılandırılır.

RecurringJob.AddOrUpdate<ReportService>(
    "daily-report",
    x => x.GenerateDailyReport(),
    Cron.Daily(3)); // Her gün saat 03:00'te

Cron Expression Örnekleri

  • Cron.Minutely() - Her dakika
  • Cron.Hourly() - Her saat başı
  • Cron.Daily() - Her gün gece yarısı
  • Cron.Weekly() - Her hafta
  • Cron.Monthly() - Her ay
  • "*/15 * * * *" - Her 15 dakikada bir
  • "0 9-17 * * *" - 09:00 - 17:00 arası her saat

4. Continuation Jobs

Bir job bittikten sonra çalışacak job'lar. İş akışları (workflows) oluşturmak için kullanılır.

// 1. Video upload
var jobId = BackgroundJob.Enqueue<VideoProcessingService>(
    x => x.UploadVideo(filePath));

// 2. Upload bittikten sonra encoding
var encodeJobId = BackgroundJob.ContinueJobWith<VideoProcessingService>(
    jobId,
    x => x.EncodeVideo(null));

// 3. Encoding bittikten sonra thumbnail
BackgroundJob.ContinueJobWith<VideoProcessingService>(
    encodeJobId,
    x => x.GenerateThumbnail(null));

Dashboard ve Monitoring

Hangfire Dashboard, job'larınızı yönetmek ve izlemek için güçlü bir web arayüzü sunar.

Dashboard Bölümleri

  • Enqueued: Sırada bekleyen job'lar
  • Processing: Şu anda işlenen job'lar
  • Scheduled: Zamanlanmış job'lar
  • Succeeded: Başarılı tamamlanan job'lar
  • Failed: Başarısız olan job'lar
  • Recurring Jobs: Periyodik job'ların listesi
  • Servers: Aktif Hangfire server'lar

Dashboard Güvenliği

Önemli!

Dashboard varsayılan olarak herkese açıktır. Production'da mutlaka güvenlik eklenmelidir.

public class HangfireAuthorizationFilter : IDashboardAuthorizationFilter
{
    public bool Authorize(DashboardContext context)
    {
        var httpContext = context.GetHttpContext();
        // Sadece Admin rolündeki kullanıcılar
        return httpContext.User.IsInRole("Admin");
    }
}

app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
    Authorization = new[] { new HangfireAuthorizationFilter() }
});

Error Handling ve Retry

Hangfire, başarısız job'ları otomatik olarak yeniden dener.

Varsayılan Retry Davranışı

  • İlk retry: 15 dakika sonra
  • İkinci retry: 1 saat sonra
  • Üçüncü retry: 6 saat sonra
  • Dördüncü retry: 12 saat sonra
  • Beşinci retry: 24 saat sonra
  • Toplam 10 kez denenir

Custom Retry Configuration

[AutomaticRetry(Attempts = 5, DelaysInSeconds = new[] { 60, 300, 900 })]
public void ProcessPayment(int orderId)
{
    // Ödeme işleme mantığı
}

// Veya retry'ı devre dışı bırakma
[AutomaticRetry(Attempts = 0)]
public void SendSms(string phoneNumber, string message)
{
    // SMS gönderme (retry'sız)
}

Performans Optimizasyonu

Worker Sayısı Yapılandırması

builder.Services.AddHangfireServer(options =>
{
    options.WorkerCount = 20; // 20 paralel job işlenebilir
});

Best Practice

  • CPU-intensive job'lar: Worker count = CPU core count
  • I/O-intensive job'lar: Worker count = 2-5x CPU core count

Queue Önceliklendirme

builder.Services.AddHangfireServer(options =>
{
    options.Queues = new[] { "critical", "default", "low" };
});

// Kritik job'ları öncelikli queue'ya atama
BackgroundJob.Enqueue<PaymentService>(
    x => x.ProcessPayment(orderId),
    new EnqueuedState("critical"));

Gerçek Dünya Senaryoları

E-Ticaret: Sipariş İşleme Pipeline

// 1. Validasyon
var validateJobId = BackgroundJob.Enqueue<OrderProcessingService>(
    x => x.ValidateOrder(order.Id));

// 2. Ödeme işleme
var paymentJobId = BackgroundJob.ContinueJobWith<OrderProcessingService>(
    validateJobId,
    x => x.ProcessPayment(order.Id));

// 3. Stok güncelleme
var inventoryJobId = BackgroundJob.ContinueJobWith<OrderProcessingService>(
    paymentJobId,
    x => x.UpdateInventory(order.Id));

// 4. Paralel: Depo bildirimi ve e-posta
BackgroundJob.ContinueJobWith<OrderProcessingService>(
    inventoryJobId,
    x => x.NotifyWarehouse(order.Id));

SaaS: Kullanıcı Onboarding

// Hemen hoşgeldin e-postası
BackgroundJob.Enqueue<OnboardingService>(
    x => x.SendWelcomeEmail(userId));

// 1 gün sonra: İlk adımlar
BackgroundJob.Schedule<OnboardingService>(
    x => x.SendGettingStartedEmail(userId),
    TimeSpan.FromDays(1));

// 28 gün sonra: Trial bitişi
BackgroundJob.Schedule<OnboardingService>(
    x => x.ConvertToFreePlan(userId),
    TimeSpan.FromDays(28));

Best Practices

Temel Prensipler

  1. Job'ları İdempotent Yapın: Aynı job iki kez çalışsa bile sorun çıkmamalı
  2. Küçük, Odaklanmış Job'lar: Her job tek bir sorumluluğa sahip olmalı
  3. Timeout Değerleri: Uzun süren işlemler için timeout belirleyin
  4. Structured Logging: Job ID ve parametreleri logla
  5. Queue Priority: Kritik job'ları öncelikli queue'lara atayın

İdempotency Örneği

// İyi: Idempotency check
public void SendEmail(int emailLogId)
{
    var emailLog = _dbContext.EmailLogs.Find(emailLogId);
    
    if (emailLog.IsSent)
    {
        _logger.LogInformation($"Email {emailLogId} already sent");
        return;
    }
    
    _emailService.Send(emailLog.To, emailLog.Subject, emailLog.Body);
    emailLog.IsSent = true;
    _dbContext.SaveChanges();
}

Storage Seçenekleri

SQL Server

Avantajlar: ACID garantisi, transaction desteği

Kullanım: Orta ölçekli sistemler

PostgreSQL

Avantajlar: Açık kaynak, cross-platform

Kullanım: Maliyet-etkin çözümler

Redis

Avantajlar: Çok yüksek performans

Kullanım: Yüksek throughput gereken sistemler

Sonuç

Hangfire, .NET uygulamalarında background job processing için güvenilir, kullanımı kolay ve feature-rich bir çözümdür. Özellikle ASP.NET Core uygulamaları için doğal bir seçimdir.

Hangfire'ı Tercih Etmeniz Gereken Durumlar

  • .NET / ASP.NET Core kullanıyorsunuz
  • Hızlı başlamak istiyorsunuz (minimal configuration)
  • Built-in dashboard istiyorsunuz
  • Küçük-orta ölçekli job volume (saniyede 0-100 job)
  • Ayrı infrastructure yönetmek istemiyorsunuz

Başlangıç Önerileri

  1. Basit Başlayın: Fire-and-forget job'larla başlayın
  2. Monitoring Kurun: İlk günden logging yapın
  3. İdempotency Düşünün: Job'ları idempotent tasarlayın
  4. Test Edin: Unit ve integration testleri yazın
  5. Dokümante Edin: Job'ların ne yaptığını belgelein

Background job processing, modern uygulamaların vazgeçilmez bir parçasıdır. Hangfire, bu ihtiyacı .NET ekosisteminde en kolay ve etkili şekilde karşılayan araçlardan biridir.

Küçük başlayın, öğrenin, ölçeklendirin. Background job processing yolculuğunuzda başarılar!