C# İle Özel Bir Dağıtık Önbellek Yönetim Sistemi Nasıl Yapılır?

C# İle Özel Bir Dağıtık Önbellek Yönetim Sistemi Nasıl Yapılır?
C# İle Özel Bir Dağıtık Önbellek Yönetim Sistemi Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Sisteminizi geliştirmeye başlamadan önce .NET SDK 9.0 veya daha güncel bir sürümün bilgisayarınızda yüklü olması gerekir. Dağıtık bir yapı kuracağımız için arka planda veriyi tutacak bir "Distributed Cache Provider" (Dağıtık Önbellek Sağlayıcısı) kullanacağız. Bu örnekte endüstri standardı olan Redis'i temel alacağız.

  • .NET 9 SDK veya üzeri.
  • Docker Desktop (Redis konteynerini çalıştırmak için).
  • Visual Studio 2022 veya JetBrains Rider IDE.
  • Microsoft.Extensions.Caching.StackExchangeRedis NuGet paketi.

Projenizi oluşturmak için terminalde şu komutu çalıştırabilirsiniz:

dotnet new webapi -n DistributedCacheSystem
cd DistributedCacheSystem
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis

Dağıtık Önbellek Arayüzünün Tasarlanması

Sistemimizi soyutlamak, ileride sağlayıcıyı (Redis, Memcached veya NCache) değiştirmek istediğimizde kodunuzun geri kalanını korumamızı sağlar. ICacheService arayüzü, temel CRUD (Ekleme, Okuma, Güncelleme, Silme) işlemlerini tanımlayacaktır.

public interface ICacheService
{
    Task GetAsync(string key);
    Task SetAsync(string key, T value, TimeSpan? expiration = null);
    Task RemoveAsync(string key);
}

Bu arayüz, jenerik (generic) yapısı sayesinde her türlü nesneyi serileştirerek önbelleğe almamıza olanak tanır. T tipi, verinin önbellekten çekilirken otomatik olarak dönüştürülmesini sağlar.

Redis Tabanlı Önbellek Sağlayıcısının Uygulanması

Şimdi, tanımladığımız arayüzü Redis kullanarak hayata geçirelim. Veriyi JSON formatında serileştirmek, C# nesnelerini metin tabanlı depolama alanlarında saklamanın en güvenli yoludur.

using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;

public class RedisCacheService : ICacheService
{
    private readonly IDistributedCache _cache;

    public RedisCacheService(IDistributedCache cache)
    {
        _cache = cache;
    }

    public async Task GetAsync(string key)
    {
        var data = await _cache.GetStringAsync(key);
        return data == null ? default : JsonSerializer.Deserialize(data);
    }

    public async Task SetAsync(string key, T value, TimeSpan? expiration = null)
    {
        var options = new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = expiration ?? TimeSpan.FromMinutes(10)
        };
        var data = JsonSerializer.Serialize(value);
        await _cache.SetStringAsync(key, data, options);
    }

    public async Task RemoveAsync(string key) => await _cache.RemoveAsync(key);
}

Bu sınıf, IDistributedCache arayüzünü kullanarak Redis ile konuşur. SetAsync metodunda varsayılan olarak 10 dakikalık bir yaşam süresi (TTL) tanımladık; bu, bellek sızıntılarını önlemek için kritik bir güvenlik önlemidir.

Bağımlılık Enjeksiyonu ve Yapılandırma

Hazırladığımız servisi .NET'in yerleşik bağımlılık enjeksiyonu (Dependency Injection) sistemine kaydetmemiz gerekir. Program.cs dosyasında Redis bağlantı dizisini tanımlayarak servisi sisteme dahil ediyoruz.

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost:6379";
    options.InstanceName = "AppCache_";
});

builder.Services.AddScoped();

InstanceName kullanımı, aynı Redis sunucusunu farklı uygulamalar için kullanıyorsanız verilerin birbirine karışmasını engeller. Bu, dağıtık sistemlerde "namespace" izolasyonu sağlar.

Güvenlik Uyarısı: Üretim ortamında (Production) Redis bağlantı dizisini asla kod içerisinde sabit (hardcoded) bırakmayın. Bunun yerine appsettings.json veya Azure Key Vault gibi güvenli bir yapılandırma yöneticisi kullanın.

Önbellek Yönetiminde Performans Karşılaştırması

Dağıtık önbellek sistemleri ile yerel (In-Memory) önbellek sistemleri arasındaki farkları anlamak, mimari kararlarınızda size yol gösterir.

Özellik In-Memory Cache Dağıtık (Redis) Cache
Hız Çok Yüksek (RAM) Yüksek (Network + RAM)
Ölçeklenebilirlik Düşük (Sunucuya bağlı) Yüksek (Bağımsız)
Veri Tutarlılığı Zor Kolay (Merkezi)

Uygulama İçinde Kullanım ve Test Etme

Servisimizi bir Controller veya Service katmanında nasıl kullanacağımızı görelim. Aşağıdaki örnekte, veritabanına gitmeden önce önbelleği kontrol eden bir "Cache-Aside" deseni uygulanmıştır.

[HttpGet("{id}")]
public async Task GetProduct(int id)
{
    string cacheKey = $"product_{id}";
    var product = await _cacheService.GetAsync(cacheKey);

    if (product == null)
    {
        // Veritabanından çekme simülasyonu
        product = await _db.Products.FindAsync(id);
        await _cacheService.SetAsync(cacheKey, product, TimeSpan.FromMinutes(30));
    }

    return Ok(product);
}

Bu yaklaşım, veritabanı üzerindeki okuma yükünü ciddi oranda azaltır. Veri bulunamadığında (cache miss) veritabanına gidilir ve sonuç önbelleğe kaydedilir.

Sıkça Sorulan Sorular

Dağıtık önbellekte veri tutarlılığı nasıl sağlanır?

Veri güncellendiğinde (Update/Delete), önbellekteki ilgili anahtarı geçersiz kılmak (Invalidation) en güvenli yoldur. RemoveAsync metodunu veritabanı güncelleme işleminizden hemen sonra çağırın.

Redis bağlantısı koptuğunda ne olur?

Uygulamanızın çökmemesi için StackExchange.Redis kütüphanesinin hata toleransını yapılandırmalısınız. Bağlantı hatalarını yakalayan bir "Circuit Breaker" mekanizması eklemek, sistemin dayanıklılığını artırır.

Önbellek boyutu sınırlı mıdır?

Evet, Redis'te maxmemory politikası ile önbellek boyutunu sınırlayabilirsiniz. Bellek dolduğunda hangi verilerin silineceğini (LRU - Least Recently Used) yapılandırmanız önerilir.

Hangi tür veriler önbelleğe alınmalıdır?

Sık okunan, nadir değişen ve hesaplama maliyeti yüksek olan veriler (örneğin; kullanıcı izinleri, ürün katalogları) önbelleğe alınmalıdır.

Serileştirme performansı nasıl artırılır?

Büyük nesneler için System.Text.Json yerine daha hızlı olan MessagePack veya Protobuf gibi binary serileştirme kütüphanelerini kullanabilirsiniz.

Yasal Sorumluluk Reddi: Bu makalede paylaşılan kod örnekleri eğitim amaçlıdır. Üretim ortamına almadan önce güvenlik taramalarından geçirin ve yük testleri uygulayın. Yazılım hatalarından kaynaklanabilecek veri kayıplarından yazar sorumlu tutulamaz.

İleri Seviye İpuçları: Cache Tagging ve Invalidasyon Stratejileri

Dağıtık önbellek sistemlerinde en büyük zorluklardan biri, verilerin güncelliğini korumak ve ilgili verileri toplu olarak temizleyebilmektir. "Cache Tagging" (Önbellek Etiketleme), belirli bir iş mantığına bağlı verileri gruplandırmanıza olanak tanır. Örneğin, bir kullanıcı profili güncellendiğinde, o kullanıcıya ait tüm önbelleklenmiş verileri tek bir anahtar üzerinden geçersiz kılabilirsiniz.

Aşağıdaki örnek, basit bir etiketleme mekanizmasının nasıl uygulanabileceğini göstermektedir:

public async Task SetWithTagsAsync(string key, object value, string[] tags)
{
    var cacheEntry = new CacheItem { Value = value, Tags = tags };
    await _database.StringSetAsync(key, JsonSerializer.Serialize(cacheEntry));
    
    foreach (var tag in tags)
    {
        await _database.SetAddAsync($"tag:{tag}", key);
    }
}

public async Task InvalidateByTagAsync(string tag)
{
    var keys = await _database.SetMembersAsync($"tag:{tag}");
    foreach (var key in keys)
    {
        await _database.KeyDeleteAsync((string)key);
    }
    await _database.KeyDeleteAsync($"tag:{tag}");
}

Dağıtık Önbellek Sistemlerinde Hata Ayıklama ve İzleme

Dağıtık sistemlerde hata ayıklama, yerel uygulamalara göre daha karmaşıktır. Sistemin sağlığını izlemek için "Health Check" mekanizmalarını ve Redis tarafındaki metrikleri takip etmeniz gerekir. .NET'in yerleşik IHealthCheck arayüzünü kullanarak, önbellek sağlayıcınızın erişilebilirliğini izleyebilirsiniz.

İzleme İçin Temel Kontrol Listesi

  • Cache Hit Ratio: Önbellekten gelen veri oranını izleyin. Düşük oran, anahtar stratejinizin yanlış olduğunu gösterebilir.
  • Eviction Rate: Redis'in hafıza dolduğu için sildiği anahtar sayısını takip edin. INFO memory komutu ile Redis'in bellek kullanımını düzenli kontrol edin.
  • Latency: Önbellek okuma işlemlerinin milisaniye cinsinden süresini loglayın. 10ms üzerindeki değerler ağ trafiği veya yoğun yük belirtisidir.

Hata ayıklama sürecinde, Redis üzerinde gerçekleşen işlemleri anlık olarak izlemek için MONITOR komutunu kullanabilirsiniz. Ancak bu komutun üretim ortamında ciddi performans kaybına yol açabileceğini unutmayın; sadece geliştirme ortamında kullanın.

// Sağlık kontrolü örneği
public class RedisHealthCheck : IHealthCheck
{
    private readonly IConnectionMultiplexer _redis;
    public RedisHealthCheck(IConnectionMultiplexer redis) => _redis = redis;

    public Task CheckHealthAsync(HealthCheckContext context, CancellationToken ct = default)
    {
        try
        {
            var db = _redis.GetDatabase();
            db.Ping();
            return Task.FromResult(HealthCheckResult.Healthy());
        }
        catch (Exception ex)
        {
            return Task.FromResult(HealthCheckResult.Unhealthy(exception: ex));
        }
    }
}

Bu yapılandırma ile uygulamanızın dağıtık önbellek katmanında bir sorun oluştuğunda, sisteminiz bunu otomatik olarak algılayacak ve gerekli uyarıları üretecektir. İleri seviye kullanımda, bu metrikleri Prometheus veya Grafana gibi araçlara aktararak görselleştirmek, sistemin genel performansını yönetmenizi kolaylaştıracaktır.

Sonuç

C# ile özel bir dağıtık önbellek yönetim sistemi kurmak, uygulamanızın performansını ve kullanıcı deneyimini bir üst seviyeye taşır. Redis ve .NET'in sunduğu güçlü altyapıyı kullanarak, veritabanı bağımlılığını azaltan ve yatayda ölçeklenebilen bir mimari oluşturduk. Bir sonraki adım olarak, önbellek anahtarlarını otomatik olarak temizleyen bir "Cache Tagging" mekanizması geliştirerek sisteminizi daha profesyonel hale getirebilirsiniz.

Bu yazıya tepkinizi paylaşın:
Zeynep Kaya

Ev yönetimi ve kendin yap (DIY) projeleri konusunda uzmanlaşmış bir içerik üreticisiyim. Detaylı rehberler hazırlayarak okuyucuların evdeki küçük sorunları profesyonel yardıma gerek duymadan çözmelerini sağlıyorum.

Yorumlar (0)

Yorum Yaz