Modern yazılım geliştirme süreçlerinde en büyük problemlerden biri, uzun süren işlemleri doğru şekilde yönetmektir. Özellikle .NET Core ile geliştirilen web uygulamalarında, veri işleme, entegrasyon süreçleri ve arka plan görevleri doğrudan HTTP isteği içinde çalıştırıldığında ciddi performans sorunları ortaya çıkar.

Bu noktada BackgroundService, .NET Core ekosisteminde arka plan işlemlerini yönetmek için kullanılan en kritik yapılardan biridir. Ancak birçok geliştirici bu yapıyı ya yanlış kullanır ya da eksik anlar. Bu rehberde, sadece temel anlatımla kalmayacak; gerçek dünya senaryoları, performans detayları ve doğru mimari yaklaşımlarla konuyu derinlemesine ele alacağız.

Eğer ölçeklenebilir ve stabil bir backend sistemi kurmak istiyorsanız, bu konuyu doğru anlamanız şarttır.

BackgroundService Nedir?

BackgroundService, .NET Core içerisinde uzun süre çalışan işlemleri uygulamanın HTTP request akışından bağımsız olarak arka planda yürüten bir soyut sınıftır. Bu yapı sayesinde kullanıcı isteğine bağlı olmayan, sürekli veya gecikmeli çalışması gereken işlemler güvenli şekilde yönetilebilir.

Temel Amaç Nedir?

BackgroundService’in temel amacı, kullanıcı isteğine bağlı olmayan işlemleri sistemden izole ederek daha kontrollü ve sürdürülebilir bir şekilde çalıştırmaktır.

  • İşlemleri request lifecycle’dan ayırır
  • Bağımsız çalışan servisler oluşturur
  • Uzun süreli görevleri yönetilebilir hale getirir

Nasıl Çalışır?

BackgroundService, .NET Core’un IHostedService altyapısı üzerine kuruludur ve uygulama başlatıldığında otomatik olarak devreye girer.

  1. Uygulama başlatılır
  2. Hosted Service sistemi ayağa kalkar
  3. BackgroundService tetiklenir
  4. ExecuteAsync metodu çalışır
public class MyWorker : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            Console.WriteLine("Arka plan işlemi çalışıyor...");
            await Task.Delay(2000, stoppingToken);
        }
    }
}

Ne Tür İşler İçin Kullanılır?

BackgroundService, kullanıcıdan bağımsız çalışan süreçler için tasarlanmıştır:

  • Mesaj kuyruğu tüketimi
  • Veri senkronizasyon işlemleri
  • E-posta ve bildirim gönderimi
  • Dosya işleme süreçleri
  • Periyodik kontrol işlemleri

Önemli Özellikleri

Özellik Açıklama
Lifecycle entegrasyonu Uygulama ile birlikte başlar ve durur
Asenkron çalışma Non-blocking işlem yapar
Bağımsız execution HTTP request’e bağlı değildir
Uzun süreli çalışma Servis sürekli aktif kalabilir

Ne Değildir?

BackgroundService’in doğru anlaşılması için ne olmadığını bilmek kritik:

  • Bir job scheduler değildir
  • Tek başına queue sistemi değildir
  • Cron yönetim aracı değildir

Bu tür ihtiyaçlar için genellikle ek sistemlerle birlikte kullanılır.

Kritik Mimari Not

BackgroundService bir altyapıdır, çözüm değildir. Gerçek dünyada çoğunlukla queue, retry ve logging mekanizmaları ile birlikte kullanılarak tam bir arka plan işlem sistemi oluşturulur.

.NET Core Hosted Service Mimarisi

Hosted Service mimarisi, .NET Core uygulamalarında arka plan servislerinin uygulama yaşam döngüsü ile entegre şekilde çalışmasını sağlayan altyapıdır. Bu mimari sayesinde uygulama başlatıldığında devreye giren ve uygulama kapanana kadar çalışan servisler oluşturulabilir.

Mimarinin Temel Bileşenleri

Hosted Service yapısı üç ana bileşenden oluşur:

  1. IHostedService → Temel kontrat (interface)
  2. BackgroundService → Kolaylaştırılmış abstract sınıf
  3. Generic Host → Tüm sistemi yöneten ana yapı

IHostedService Nedir?

IHostedService, arka plan servislerinin nasıl başlatılıp durdurulacağını tanımlayan temel interface’tir. Bu yapı, servis lifecycle’ını manuel olarak kontrol etmek isteyen geliştiriciler için düşük seviyeli bir çözüm sunar.

  • StartAsync → Servis başlatılırken çalışır
  • StopAsync → Servis durdurulurken çalışır
public class CustomService : IHostedService
{
    public Task StartAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Servis başladı");
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Servis durdu");
        return Task.CompletedTask;
    }
}

BackgroundService Bu Yapıda Nerede Konumlanır?

BackgroundService, IHostedService’in daha pratik bir implementasyonudur. Geliştiriciye lifecycle yönetimi yerine doğrudan iş mantığına odaklanma imkanı verir.

Bu yapı sayesinde sadece ExecuteAsync metodunu yazmak yeterlidir:

public class MyWorker : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            await Task.Delay(1000, stoppingToken);
        }
    }
}

Generic Host Nedir?

Generic Host, .NET Core uygulamasının çalışmasını sağlayan ana altyapıdır. Hosted Service’ler bu yapı tarafından yönetilir.

Generic Host şu sorumlulukları üstlenir:

  • Dependency Injection yönetimi
  • Configuration yükleme
  • Logging altyapısı
  • Hosted Service’leri başlatma ve durdurma

Servis Nasıl Register Edilir?

Bir BackgroundService’i sisteme dahil etmek için DI container’a eklenmesi gerekir:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHostedService<MyWorker>();

var app = builder.Build();
app.Run();

Bu işlemden sonra uygulama başlatıldığında servis otomatik olarak çalışır.

Lifecycle Entegrasyonu

Hosted Service mimarisi doğrudan uygulama lifecycle’ına bağlıdır. Bu entegrasyon sayesinde servisler manuel kontrol gerektirmeden yönetilir.

  1. Uygulama başlar
  2. Generic Host ayağa kalkar
  3. Tüm Hosted Service’ler başlatılır
  4. Servisler çalışmaya başlar
  5. Uygulama kapanırken servisler durdurulur

Dependency Injection ile Çalışma

Hosted Service’ler, .NET Core’un DI sistemi ile tam uyumludur. Bu sayede servisler içerisinde başka servisler kolayca kullanılabilir.

public class EmailWorker : BackgroundService
{
    private readonly IEmailService _emailService;

    public EmailWorker(IEmailService emailService)
    {
        _emailService = emailService;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await _emailService.SendAsync();
    }
}

Bu yapı modüler, test edilebilir ve sürdürülebilir kod yazmayı sağlar.

Mimari Avantaj

Hosted Service mimarisi, arka plan işlemlerini uygulamaya entegre ederken kontrolü tamamen sistem seviyesinde tutar. Bu sayede servisler güvenli, düzenli ve öngörülebilir şekilde çalışır.

Neden BackgroundService Kullanılmalı?

BackgroundService, uzun süren veya sürekli çalışan işlemleri HTTP request sürecinden ayırarak sistemin daha stabil ve verimli çalışmasını sağlar. Bu yaklaşım, özellikle yüksek trafikli uygulamalarda performans problemlerini önlemek için kritik bir rol oynar.

1. HTTP Request Süresini Kısaltır

Uzun süren işlemler doğrudan request içinde çalıştırıldığında kullanıcı beklemek zorunda kalır. BackgroundService ile bu işlemler arka plana alınır ve request hızlıca tamamlanır.

  • Kullanıcı beklemez
  • Response süresi düşer
  • Uygulama daha hızlı hissedilir

2. Thread Bloklanmasını Önler

ASP.NET Core her request için thread kullanır. Uzun işlemler bu thread’leri meşgul eder. BackgroundService kullanımı ile thread’ler serbest kalır.

Yaklaşım Sonuç
Controller içinde uzun işlem Thread bloklanır
BackgroundService kullanımı Thread serbest kalır

3. Timeout Problemlerini Engeller

Web uygulamalarında request süresi sınırlıdır. Uzun işlemler timeout hatasına neden olabilir. BackgroundService ile bu risk ortadan kaldırılır.

  • Timeout hataları azalır
  • İşlemler kesilmez
  • Sistem daha güvenilir çalışır

4. Uzun Süreli İşlemleri Ayrıştırır

Uzun süren işlemleri ayrı bir katmana almak, sistemin daha düzenli çalışmasını sağlar.

  • İş mantığı ayrılır
  • Kod daha temiz olur
  • Bakım kolaylaşır

5. Sürekli Çalışan İşleri Destekler

Bazı işlemler uygulama boyunca sürekli çalışmalıdır. BackgroundService bu tür senaryolar için doğal bir çözümdür.

  • Queue dinleme
  • Sistem kontrolü
  • Veri senkronizasyonu

6. Hata İzolasyonu Sağlar

Arka plan işlemleri request akışından bağımsız olduğu için oluşan hatalar doğrudan kullanıcıyı etkilemez.

  • Hatalar izole edilir
  • Sistem çökmez
  • Kontrollü hata yönetimi yapılır

Worker ve Queue Mimarisi

Worker ve Queue mimarisi, arka plan işlemlerinin kontrollü ve sıralı şekilde işlenmesini sağlayan bir sistem tasarımıdır. Bu yapıda gelen işler doğrudan çalıştırılmaz; önce bir kuyruğa alınır, ardından worker servisleri tarafından işlenir.

Mimari Akış

  1. İstek sisteme gelir
  2. İş kuyruğa eklenir
  3. Worker kuyruğu dinler
  4. İş alınır ve işlenir
  5. Sonuç kaydedilir
Client → API → Queue → Worker → Database

Queue (Kuyruk) Yapısı

Queue, işlerin belirli bir sıraya göre işlendiği veri yapısıdır. Genellikle FIFO (First In First Out) mantığı ile çalışır.

  • İşleri sıraya alır
  • Yoğunluğu dengeler
  • Asenkron çalışma sağlar

Basit Queue Örneği

public class JobQueue
{
    private readonly Queue<string> _jobs = new();

    public void Enqueue(string job)
    {
        _jobs.Enqueue(job);
    }

    public string Dequeue()
    {
        return _jobs.Count > 0 ? _jobs.Dequeue() : null;
    }
}

Worker (İşleyici) Yapısı

Worker, kuyruğa alınan işleri arka planda işleyen servistir. Genellikle BackgroundService kullanılarak oluşturulur.

public class QueueWorker : BackgroundService
{
    private readonly JobQueue _queue;

    public QueueWorker(JobQueue queue)
    {
        _queue = queue;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var job = _queue.Dequeue();

            if (job != null)
            {
                Console.WriteLine($"İşleniyor: {job}");
            }

            await Task.Delay(500, stoppingToken);
        }
    }
}

Çalışma Mantığı

  • Worker sürekli kuyruğu kontrol eder
  • Yeni iş geldiğinde işlem başlatır
  • İşler sırayla veya kontrollü paralel şekilde yürütülür

Queue Türleri

Tür Açıklama
In-Memory Queue Basit ve tek sunuculu sistemler için
Redis Queue Hızlı ve dağıtık sistemler için
RabbitMQ Mesajlaşma odaklı sistemler için
Kafka Yüksek hacimli veri akışı için

İşleme Modelleri

  • Single Consumer → Tek worker işler
  • Multiple Consumer → Birden fazla worker paralel çalışır

Temel Prensip

İş üretimi ile iş işleme birbirinden ayrılmalıdır.

Bu ayrım sayesinde sistem daha kontrol edilebilir ve genişletilebilir hale gelir.

Yaygın Hatalar

  • Queue olmadan worker kullanmak
  • İşleri doğrudan çalıştırmak
  • Kuyruk yönetimini ihmal etmek

Concurrency ve Performans Yönetimi

Concurrency (eşzamanlılık) yönetimi, aynı anda çalışan işlem sayısını kontrol ederek sistem kaynaklarının dengeli kullanılmasını sağlar. Performans yönetimi ise bu işlemlerin en verimli şekilde yürütülmesini hedefler.

Concurrency Nedir?

Concurrency, birden fazla işlemin aynı anda yürütülmesidir. Worker tabanlı sistemlerde bu, aynı anda birden fazla job’ın işlenmesi anlamına gelir.

Neden Kontrol Edilmelidir?

  • CPU aşırı kullanımı oluşabilir
  • Bellek tüketimi artar
  • Dış servisler rate limit uygulayabilir
  • Sistem stabilitesi bozulabilir

CPU-bound ve IO-bound Ayrımı

İş Türü Açıklama Yaklaşım
CPU-bound Yoğun hesaplama işlemleri Düşük paralellik
IO-bound API, DB, dosya işlemleri Daha yüksek paralellik

Semaphore ile Limit Koyma

Concurrency kontrolü için en yaygın yöntem SemaphoreSlim kullanmaktır.

private readonly SemaphoreSlim _semaphore = new(3);

public async Task ProcessAsync()
{
    await _semaphore.WaitAsync();

    try
    {
        await DoWork();
    }
    finally
    {
        _semaphore.Release();
    }
}

Bu yapı aynı anda maksimum 3 işlemin çalışmasını sağlar.

Paralel İşlem Yönetimi

var tasks = jobs.Select(job => ProcessJob(job));
await Task.WhenAll(tasks);

Kontrolsüz kullanıldığında sistem aşırı yüklenebilir, bu nedenle sınırlandırma gereklidir.

Batch Processing

Büyük iş listeleri parçalara bölünerek işlenmelidir.

var batches = jobs.Chunk(10);

foreach (var batch in batches)
{
    var tasks = batch.Select(x => ProcessJob(x));
    await Task.WhenAll(tasks);
}

Rate Limiting

Dış servislerle çalışırken istekler sınırlandırılmalıdır.

await Task.Delay(200);
  • API limit aşımı önlenir
  • Sistem dengeli çalışır

Backpressure

Sisteme gelen iş miktarını kontrol etmek gerekir.

  • Queue uzunluğu sınırlandırılır
  • Yeni işler reddedilebilir
  • Worker sayısı ayarlanabilir

Temel Kurallar

  1. Sınırsız paralellikten kaçın
  2. Async/await kullan
  3. Kaynak kullanımını gözlemle
  4. İş tipine göre strateji belirle

Retry ve Hata Yönetimi

Retry ve hata yönetimi, arka plan işlemlerinin başarısız olduğu durumlarda sistemin stabil kalmasını ve işlemlerin güvenli şekilde yeniden denenmesini sağlar.

Retry Nedir?

Retry, başarısız bir işlemin tekrar çalıştırılmasıdır. Özellikle geçici hatalarda kritik rol oynar.

Hangi Durumlarda Kullanılır?

  • API timeout
  • Ağ bağlantı sorunları
  • Geçici veritabanı hataları
  • Rate limit problemleri

Basit Retry Örneği

int maxRetry = 3;

for (int i = 0; i < maxRetry; i++)
{
    try
    {
        await DoWork();
        break;
    }
    catch
    {
        await Task.Delay(1000);
    }
}

Exponential Backoff

Her retry denemesinde bekleme süresi artırılır.

for (int i = 0; i < 3; i++)
{
    try
    {
        await DoWork();
        break;
    }
    catch
    {
        int delay = (int)Math.Pow(2, i) * 1000;
        await Task.Delay(delay);
    }
}

Retry Edilmemesi Gereken Hatalar

Hata Türü Retry
Network hatası Evet
Timeout Evet
Validation hatası Hayır
Null reference Hayır

Exception Handling

try
{
    await ProcessJob();
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

Hatalar mutlaka yakalanmalı ve kontrol altına alınmalıdır.

Idempotency

Aynı işlem tekrar çalıştırıldığında sonuç değişmemelidir.

if (IsProcessed(jobId))
{
    return;
}

Dead Letter Queue

Retry sonrası başarısız olan işler ayrı bir kuyruğa alınır.

  • Başarısız işler kaybolmaz
  • Sonradan incelenebilir

Polly ile Gelişmiş Retry

var policy = Policy
    .Handle()
    .WaitAndRetryAsync(3, retryAttempt =>
        TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));

await policy.ExecuteAsync(async () =>
{
    await DoWork();
});

Temel Kurallar

  1. Sınırsız retry yapma
  2. Sadece geçici hataları retry et
  3. Bekleme süresi kullan
  4. Hataları logla

Gerçek Kod Örnekleri

BackgroundService ve worker mimarisini doğru anlamanın en etkili yolu, gerçek senaryolara yakın kod örnekleri incelemektir. Aşağıdaki örnekler, arka plan işlem sistemlerinin nasıl kurulduğunu pratik olarak gösterir.

1. Basit Worker

public class SimpleWorker : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            Console.WriteLine("Worker çalışıyor...");
            await Task.Delay(2000, stoppingToken);
        }
    }
}

2. Queue Tabanlı Worker

public class JobQueue
{
    private readonly Queue _jobs = new();

    public void Enqueue(string job)
    {
        _jobs.Enqueue(job);
    }

    public string Dequeue()
    {
        return _jobs.Count > 0 ? _jobs.Dequeue() : null;
    }
}
public class QueueWorker : BackgroundService
{
    private readonly JobQueue _queue;

    public QueueWorker(JobQueue queue)
    {
        _queue = queue;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var job = _queue.Dequeue();

            if (job != null)
            {
                Console.WriteLine($"İşleniyor: {job}");
            }

            await Task.Delay(500, stoppingToken);
        }
    }
}

3. Zamanlanmış Worker

public class ScheduledWorker : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            Console.WriteLine("Görev çalıştı");
            await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
        }
    }
}

4. Paralel İşleme

public async Task ProcessJobsAsync(List jobs)
{
    var tasks = jobs.Select(job => ProcessJob(job));
    await Task.WhenAll(tasks);
}

5. Limitli Paralellik

private readonly SemaphoreSlim _semaphore = new(3);

public async Task ProcessAsync(string job)
{
    await _semaphore.WaitAsync();

    try
    {
        await Task.Delay(1000);
    }
    finally
    {
        _semaphore.Release();
    }
}

6. Retry Mekanizması

public async Task ProcessWithRetry()
{
    for (int i = 0; i < 3; i++)
    {
        try
        {
            await DoWork();
            break;
        }
        catch
        {
            await Task.Delay(1000);
        }
    }
}

7. Dependency Injection Kullanımı

public class EmailWorker : BackgroundService
{
    private readonly IEmailService _service;

    public EmailWorker(IEmailService service)
    {
        _service = service;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await _service.SendAsync();
    }
}

8. Logging

private readonly ILogger _logger;

_logger.LogInformation("Worker başladı");

Örnek Akış

API → Queue → Worker → Database

Önemli Notlar

  • Async/await kullanılmalıdır
  • CancellationToken ihmal edilmemelidir
  • Retry mekanizması eklenmelidir
  • Concurrency kontrolü yapılmalıdır

Alternatif Çözümler

BackgroundService, arka plan işlemleri için güçlü bir yapı sunar; ancak bazı senaryolarda farklı araçlar daha uygun olabilir. Özellikle zamanlama, job yönetimi ve dağıtık sistem ihtiyaçlarında alternatif çözümler tercih edilir.

1. Hangfire

  • Dashboard ile job takibi
  • Hazır retry mekanizması
  • Cron destekli zamanlama
BackgroundJob.Enqueue(() => Console.WriteLine("Job çalıştı"));

2. Quartz.NET

  • Zaman bazlı job scheduling
  • Cron ifadeleri ile çalışma
  • Detaylı zamanlama kontrolü
IJobDetail job = JobBuilder.Create().Build();

3. Azure Functions

  • Serverless yapı
  • Otomatik ölçeklenme
  • Event-driven çalışma
public static void Run(TimerInfo myTimer, ILogger log)
{
    log.LogInformation("Çalıştı");
}

4. Message Queue Sistemleri

  • RabbitMQ
  • Kafka
  • Redis Queue
Producer → Queue → Consumer

5. OS Seviyesi Servisler

  • Windows Service
  • Linux Daemon

Karşılaştırma

Çözüm Kullanım Avantaj
BackgroundService Genel worker Basit ve entegre
Hangfire Job yönetimi Dashboard
Quartz.NET Zamanlama Cron desteği
Azure Functions Serverless Otomatik scaling
RabbitMQ/Kafka Dağıtık sistem Yüksek performans

Sık Sorulan Sorular

BackgroundService memory leak yapar mı?

Doğrudan yapmaz; ancak yanlış resource yönetimi, sürekli büyüyen koleksiyonlar veya dispose edilmeyen nesneler memory leak’e neden olabilir.

Worker crash olursa ne olur?

Unhandled exception oluşursa worker durabilir. Bu nedenle exception handling ve gerekiyorsa restart stratejisi uygulanmalıdır.

Scoped servisler neden doğrudan kullanılamaz?

BackgroundService singleton olarak çalışır. Scoped servisler ise request bazlıdır. Bu nedenle CreateScope kullanılarak oluşturulmalıdır.

BackgroundService içinde DbContext kullanılır mı?

Evet, ancak doğrudan değil. Her işlem için yeni bir scope oluşturularak kullanılmalıdır.

Worker kaç adet çalıştırılmalı?

Bu, sistem kaynaklarına ve iş yüküne bağlıdır. Genellikle concurrency limiti belirlenerek kontrollü şekilde artırılır.

BackgroundService ne zaman durur?

Uygulama kapanırken veya CancellationToken tetiklendiğinde durur.

Queue olmadan kullanılabilir mi?

Evet, ancak karmaşık ve yoğun sistemlerde önerilmez.

Retry kaç kez yapılmalı?

Genellikle 3–5 deneme yeterlidir. Daha fazlası sistem yükünü artırabilir.

Logging zorunlu mudur?

Evet. Arka plan işlemlerinde hata takibi ve izleme için logging kritik öneme sahiptir.

BackgroundService ile cron job yapılabilir mi?

Evet, ancak gelişmiş zamanlama ihtiyaçları için Quartz.NET veya Hangfire tercih edilmelidir.