
Redis Pub/Sub in .Net
Günümüz yazılım mimarilerinde yüksek performanslı, ölçeklenebilir ve gerçek zamanlı haberleşme çözümleri büyük bir önem taşır. Redis Pub/Sub (Publish/Subscribe) mekanizması, özellikle dağıtık sistemler için düşük gecikmeli mesajlaşma ihtiyacını karşılayan güçlü bir çözümdür.
Redis Pub/Sub Nedir?
Redis’in Publish/Subscribe (Pub/Sub) özelliği, bir kanal (channel) üzerinden publisher (yayıncı) tarafından gönderilen mesajların, bu kanala abone olmuş subscriber (abone) tarafından anında alınmasını sağlayan bir mesajlaşma mekanizmasıdır.
Nasıl Çalışır?
Redis Pub/Sub modeli şu temel bileşenlerden oluşur:
- Publisher (Yayıncı): Bir kanala mesaj yayınlar.
- Subscriber (Abone): Belirli kanallara abone olur ve o kanala gelen mesajları dinler.
- Channel (Kanal): Mesajların iletildiği sanal bir kanaldır.
Bir subscriber, Redis’e belirli bir kanala abone olduğunu bildirir. Bu kanal üzerinden herhangi bir publisher mesaj gönderdiğinde, Redis ilgili mesajı anında abonelere yönlendirir.

Redis Channels ve Topic-Based Publish-Subscribe Model
“Redis channels follow a topic-based publish-subscribe model.”
(Redis kanalları, konu tabanlı bir yayın-abone modelini takip eder.)
Redis’in Pub/Sub mekanizmasında “channel” (kanal) kavramı, bir mesajın iletildiği ve aboneler tarafından dinlendiği sanal bir iletişim hattıdır.
Konu tabanlı (topic-based) model, mesajların belirli kanallara (topics) yönlendirildiği ve yalnızca bu kanallara abone olan tüketicilere iletildiği anlamına gelir.
Örneğin, bir e-ticaret sisteminde ürün fiyat değişiklikleri ve sipariş durum güncellemeleri gibi farklı konular olabilir:
"product_updates"
kanalına abone olanlar, sadece ürün güncellemeleriyle ilgili mesajları alır."order_status"
kanalına abone olanlar, sadece sipariş durum değişikliklerini alır.
Bu sistemin avantajları şunlardır:
✅ İzolasyon: Her konu için ayrı bir kanal açılabilir.
✅ Esneklik: Farklı subscriber grupları, sadece ilgilendikleri konulara abone olabilir.
✅ Ölçeklenebilirlik: Yayıncılar (publishers) ve aboneler (subscribers) birbirinden bağımsız çalışır.
Çoklu Yayıncı (Multiple Publishers) ve Çoklu Abone (Multiple Subscribers) Desteği
“Multiple publishers can send messages to a channel, and multiple subscribers can receive messages from that channel.”
(Birden fazla yayıncı bir kanala mesaj gönderebilir ve birden fazla abone bu kanaldan mesaj alabilir.)
Redis Pub/Sub modelinde bir kanala birden fazla yayıncı (publisher) mesaj gönderebilir ve birden fazla abone (subscriber) o kanalın mesajlarını dinleyebilir.
Bu şu anlama gelir:
- Tek bir kanal, farklı kaynaklardan (mikroservisler, farklı modüller, bağımsız uygulamalar) gelen mesajları alabilir.
- Aynı kanalın birden fazla abonesi olabilir ve her bir abone, aynı anda gelen mesajları alır.
🔹 Örnek Senaryo:
Diyelim ki bir kripto para borsasında fiyat güncellemelerini bir Redis kanalı üzerinden yayınlıyoruz.
"crypto_prices"
kanalına birden fazla fiyat sağlayıcı (publisher) yeni fiyat verilerini yayınlayabilir.- Aynı zamanda, bu kanala abone olan farklı analiz sistemleri ve dashboard uygulamaları (subscribers) anlık olarak fiyat güncellemelerini alabilir.
Bu yapı, yüksek performanslı, ölçeklenebilir ve dağıtık sistemler için büyük avantaj sağlar.
Redis Pub/Sub Mesaj Saklamaz (Message Persistence)
“Redis channels do not store messages. If there are no subscribers for a channel when a message is published, that message is immediately discarded.”
(Redis kanalları mesajları saklamaz. Eğer bir kanalın o anda hiçbir abonesi yoksa, yayınlanan mesaj anında kaybolur.)
Redis’in Pub/Sub mekanizması bir mesaj kuyruğu (message queue) sistemi değildir. Örneğin, RabbitMQ veya Kafka gibi sistemlerde mesajlar, aboneler gelip tüketene kadar kuyrukta saklanabilir. Ancak Redis Pub/Sub’da böyle bir kalıcılık (persistence) yoktur.
Bu ne anlama gelir?
- Eğer bir kanalın o anda bir abonesi varsa, mesaj ona anında iletilir.
- Eğer o kanalda hiç abone yoksa, mesaj anında kaybolur.
🔴 Örnek Hata Senaryosu:
- Bir sistem,
"user_notifications"
kanalına bir mesaj gönderiyor. - Ancak o anda hiçbir abone (subscriber) yok.
- Bu durumda mesaj Redis tarafından tutulmaz ve yok olur.
- Eğer daha sonra bir abone bağlanırsa, eski mesajları alamaz.
Bu durumun olası çözümleri:
✅ Redis Streams kullanmak: Redis Streams, mesajları saklama ve daha sonra tüketme imkanı sağlar.
✅ RabbitMQ veya Kafka gibi Message Queue sistemlerini tercih etmek.
✅ Subscriber’ların her zaman aktif olmasını sağlamak.
Redis Pub/Sub “At-Most-Once” Delivery Semantics
“Redis channels have an at-most-once delivery semantics.”
(Redis kanalları, en fazla bir kere teslimat garantisine sahiptir.)
At-Most-Once (En Fazla Bir Kez) Teslimat Nedir?
- Bir mesaj bir kez iletilir ve kaybolur.
- Eğer abone mesajı kaçırırsa, yeniden iletilmez.
- Mesaj tekrar gönderilmez, kuyrukta saklanmaz.
Bu, güvenilirlik (reliability) açısından bir dezavantaj olabilir çünkü bazı durumlarda aboneler mesajları kaçırabilir.
🔹 Örnek Hata Senaryosu:
"order_updates"
kanalına yeni bir sipariş durumu gönderildi.- Ancak subscriber uygulaması anlık bir hata nedeniyle mesajı kaçırdı (örneğin, ağ kesintisi oldu).
- Redis bu mesajı yeniden göndermez ve subscriber bu mesajı sonsuza kadar kaybeder.
At-Most-Once Alternatifleri:
🔵 At-Least-Once (En Az Bir Kez) Teslimat:
- Kafka ve RabbitMQ gibi sistemler, mesajın en az bir kez teslim edilmesini garanti eder.
- Mesajı alan abone bir ACK (onay) mesajı gönderir ve bu onay alınmazsa mesajı tekrar gönderebilir.
🔵 Exactly-Once (Tam Olarak Bir Kez) Teslimat:
- Bazı sistemler (Kafka’nın transactional mesajlaşma modeli gibi), mesajın kesinlikle sadece bir kez işlenmesini garanti edebilir.
Redis Pub/Sub mekanizmasının eksik yönleri:
❌ Mesaj garanti mekanizması yok.
❌ Mesajlar kaybolabilir.
❌ Mesajlar kuyruklanamaz veya tekrar okunamaz.
Redis Pub/Sub yerine kullanılabilecek sistemler:
✅ Redis Streams: Mesajları belirli bir süre saklayabilir.
✅ Kafka veya RabbitMQ: Mesajları kuyruğa alabilir, tekrar gönderebilir.
Redis Pub/Sub Kullanım Senaryoları
Redis Pub/Sub mekanizması, at-most-once delivery prensibiyle çalıştığı için mesaj kaybının tolere edilebildiği, gerçek zamanlı (real-time) veya neredeyse gerçek zamanlı (near-real-time) iletişim gerektiren senaryolar için idealdir. Aşağıda, Redis Pub/Sub’ın kullanımına uygun bazı örnek senaryolar yer almaktadır:
1. Sosyal Medya Akışları (Social Media Feeds)
Kullanıcılar yeni bir gönderi paylaştığında veya bir güncelleme yaptığında, bu bilgiyi takipçilerine anında iletmek için Redis Pub/Sub kullanılabilir. Örneğin, bir sosyal medya platformunda:
"new_posts"
adında bir kanal oluşturulabilir.- Kullanıcılar yeni gönderi paylaştığında, bu gönderinin bilgileri yayıncı (publisher) tarafından bu kanala gönderilir.
- Abone olan kullanıcılar gerçek zamanlı olarak yeni gönderileri görebilir.
Bu sistem, haber akışlarının hızlı bir şekilde yayılmasını sağlar, ancak mesaj kaybı durumunda eski gönderiler alınamayacağı için Redis Streams gibi kalıcı veri yapıları daha uygun olabilir.
2. Canlı Skor Güncellemeleri (Live Score Updates)
Spor karşılaşmaları sırasında anlık skor güncellemeleri, Redis Pub/Sub kullanılarak abonelere iletilebilir. Örneğin:
"match_scores"
kanalı üzerinden futbol veya basketbol maçlarının güncellenmiş skorları gönderilebilir.- Kullanıcılar veya mobil uygulamalar, bu kanala abone olarak anlık skor değişikliklerini alabilir.
Bu kullanım senaryosunda, eski mesajlara erişim gerekmeyeceği için Redis Pub/Sub’ın hafif ve hızlı yapısı büyük avantaj sağlar.
3. Gerçek Zamanlı Sohbet Uygulamaları (Real-Time Chat Applications)
Redis Pub/Sub, anlık mesajlaşma uygulamaları için de uygundur. Örneğin:
- Kullanıcılar belirli bir sohbet odasına katıldığında,
"chat_room_123"
gibi bir kanala abone olabilir. - Mesajlar bir yayıncı tarafından bu kanala gönderildiğinde, aboneler anında mesajları alır.
Ancak Redis Pub/Sub, mesajları saklamadığı için, kullanıcı çevrimdışıyken gelen mesajlar kaybolacaktır. Daha güvenilir bir çözüm için Redis Streams, Kafka veya RabbitMQ gibi mesaj kuyruğu sistemleri önerilebilir.
4. Ortak Çalışma Ortamları (Collaborative Editing)
Google Docs veya Figma gibi gerçek zamanlı işbirliği (collaborative editing) sistemlerinde, kullanıcıların yaptığı değişikliklerin anında diğer katılımcılara yansıması gerekir.
- Bir kullanıcı bir dokümanı düzenlediğinde
"doc_edit_456"
gibi bir kanal üzerinden değişiklikler yayınlanabilir. - Diğer kullanıcılar bu kanala abone olarak değişiklikleri anında görebilir.
Bu tür uygulamalarda Redis Pub/Sub, hızlı ve düşük gecikmeli (low-latency) bir çözüm sunar. Ancak, geçmiş değişiklikleri kaydetme gereksinimi olan sistemlerde Redis Streams veya event sourcing mekanizmaları kullanmak daha mantıklıdır.
5. Dağıtık Önbellek Güncellemeleri (Distributed Cache Updates)
Büyük ölçekli uygulamalarda, birden fazla sunucuda bulunan önbelleklerin senkronize edilmesi gerekir. Örneğin:
- Bir mikroservis, bir veri değişikliği olduğunda
"cache_invalidation"
kanalına bir mesaj yayınlayabilir. - Diğer mikroservisler veya cache sunucuları, bu kanala abone olarak önbelleklerini güncelleyebilir veya temizleyebilir.
Ne Zaman Redis Pub/Sub Kullanılmamalı?
Redis Pub/Sub, mesaj kaybının tolere edilemediği kritik sistemler için uygun değildir.
- Ödeme sistemleri (Bir ödeme işleminin bildirilmesi gerekebilir.)
- Sipariş yönetimi (Sipariş onay mesajlarının kaybolması ciddi hatalara yol açabilir.)
- IoT cihaz yönetimi (Sensör verilerinin kaybolması analiz hatalarına neden olabilir.)
Eğer bir sistemde mesaj kaybı kabul edilemezse, Redis Pub/Sub yerine daha güvenilir (reliable) bir mesaj kuyruğu sistemi olan RabbitMQ, Kafka veya Redis Streams gibi çözümler tercih edilmelidir.
Implementation
Bu aşamada basit bir örnek üzerinden .Net üzerinde Redis Pub/Sub ve channel kavramlarının uygulanmasını örneklerle aktarmaya çalışacağım.
Örnek senaryomuz şu şekilde olsun;
- Bir kategori servisimiz olsun. Bu servis ilk çalıştığında ilgili veri tabanı üzerinden bütün kategoriler alınıyor ve memory cache’de saklanıyor olsun.
- Bu servis üzerinde yapılan bütün get işlemleri memory cache üzerinden sağlanıyor olsun.
- Bu servis üzerinden yapılan yeni kategori ekleme, mevcut kategoriyi güncelleme ve mevcut kategoriyi silme gibi işlemlerde redis pub/sub kullanarak diğer servislerin ve/veya podların cachelerini güncellemelerini sağlanıyor olsun.
Localde Redis üzerinde çalıştırmak için aşağıdaki komutu kullanabilirsiniz.
docker run --name redis-cache -d -p 6379:6379 redis
Kategori servisini oluşturalım;
dotnet new webapi -n CategoryService
cd CategoryService
dotnet add package StackExchange.Redis
dotnet add package Microsoft.Extensions.Caching.Memory
dotnet add package Microsoft.Extensions.Hosting
using Microsoft.Extensions.Caching.Memory;
using StackExchange.Redis;
using System.Collections.Generic;
using System.Text.Json;
public class CategoryService
{
private readonly IMemoryCache _memoryCache;
private readonly ConnectionMultiplexer _redis;
private readonly ISubscriber _publisher;
public CategoryService(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
_redis = ConnectionMultiplexer.Connect("localhost:6379");
_publisher = _redis.GetSubscriber();
}
public List<string> GetCategories()
{
if (!_memoryCache.TryGetValue("categories", out List<string> categories))
{
// Normalde verileri DB'den alırız. Burada örnek statik veri ekliyoruz.
categories = new List<string> { "Elektronik", "Moda", "Ev & Yaşam", "Spor" };
// Cache'e ekleyelim (30 dakika geçerli olacak)
_memoryCache.Set("categories", categories, TimeSpan.FromMinutes(30));
}
return categories;
}
public void UpdateCategory(List<string> updatedCategories)
{
// Güncellenmiş kategorileri JSON formatına çevirerek Redis'e mesaj gönderiyoruz
var message = JsonSerializer.Serialize(updatedCategories);
_publisher.Publish("category-updated", message);
}
}
CategoryService.cs
içerisinde bulunan GetCategories()
memory cache üzerinden veriyi okur. Eğer yoksa 30 dakika boyunca geçerli olacak şekilde ekler.
UpdateCategory
yöntemi ise güncellenen kategorileri Redis Pub/Sub
ile category-updated
kanalına gönderir.
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Hosting;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
public class RedisSubscriberService : BackgroundService
{
private readonly IMemoryCache _memoryCache;
private readonly ConnectionMultiplexer _redis;
private readonly ISubscriber _subscriber;
public RedisSubscriberService(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
_redis = ConnectionMultiplexer.Connect("localhost:6379");
_subscriber = _redis.GetSubscriber();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine("Redis Subscriber Başlatıldı...");
await _subscriber.SubscribeAsync("category-updated", (channel, message) =>
{
Console.WriteLine($"Kategori güncellendi: {message}");
// Gelen mesajı JSON formatından deserialize ederek listeye çeviriyoruz
var updatedCategories = JsonSerializer.Deserialize<List<string>>(message);
if (updatedCategories != null)
{
_memoryCache.Set("categories", updatedCategories, TimeSpan.FromMinutes(30));
Console.WriteLine("Cache güncellendi!");
}
});
await Task.CompletedTask;
}
}
Subscriber
olarak Background Service
kullanabiliriz. RedisSubscriberService
category-updated
kanalını dinleyerek gelen mesaj ile memory cache’i günceller.
Bir CategoryController
ile bu işlemleri test edebiliriz.
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
[Route("api/categories")]
[ApiController]
public class CategoryController : ControllerBase
{
private readonly CategoryService _categoryService;
public CategoryController(CategoryService categoryService)
{
_categoryService = categoryService;
}
[HttpGet]
public IActionResult GetCategories()
{
var categories = _categoryService.GetCategories();
return Ok(categories);
}
[HttpPost("update")]
public IActionResult UpdateCategory([FromBody] List<string> updatedCategories)
{
_categoryService.UpdateCategory(updatedCategories);
return Ok("Kategori güncellendi, tüm servislerin cache'i güncellendi.");
}
}
Örnek bir POST isteği:
POST /api/categories/update
Content-Type: application/json
["Elektronik", "Moda", "Kozmetik", "Spor"]
Category Service
birden fazla pod ile çalışabilir. Herhangi bir pod’da gerçekleşen category-updated
olayı ilgili verileri Redis Pub/Sub
ile ilgili kanala göndererek diğer servislerin de verilerini güncellemesini sağlar. Böylece bu basit örnekte de olduğu gibi dağıtık sistemlerde cache invalidation
implemente edilmiş olur.
Özellikle yüksek trafikli sistemlerde, domain kurallarına göre sık değişen verilerin cache ile optimize edilmesi büyük önem taşır. Redis Pub/Sub kullanarak, dağıtık mikroservis ortamlarında verilerin hızlı ve senkronize şekilde güncellenmesini sağlayabilirsiniz.
Bu yapıyı daha da geliştirmek için Redis Streams veya Kafka gibi mesaj kuyruğu çözümleri ile kalıcı (durable) mesajlaşma mekanizmaları entegre edebilirsiniz Böylece kritik verilerin kaybolmasını önleyen daha güvenilir bir sistem inşa edilebilir.
Sonuç olarak, .NET ve Redis kullanarak ölçeklenebilir, hızlı ve güçlü bir cache yönetimi nasıl yapılır sorusuna bir cevap oluşturabildiğimi düşünüyorum. Bu teknikler, büyük ölçekli uygulamalarda yüksek performanslı veri erişimi ve senkronizasyon sağlamak için önemli bir adım olduğunu düşünüyorum.🚀
Umarım faydalı olur.