RRabbitMQ Handbook

TEMEL

Exchange Tipleri

Exchange, mesajları queue'lara yönlendiren bileşendir — bir nevi "trafik polisi". Kendisi mesaj saklamaz; kurallara bakıp "bu mesaj şu queue'ya gitsin" der.

3.1 Direct Exchange — "Tam Adrese Teslim"

En basit tip. Mesajın routing key'i, queue'nun binding key'i ile birebir eşleşiyorsa mesaj o queue'ya gider. Eşleşme yoksa mesaj sessizce kaybolur (bunu önlemek için mandatory: true ayarlanabilir — eşleşme yoksa mesaj geri döner).

Producer key="order.created" orders.direct (direct exchange) order-processing bind: "order.created" ✓ payment-queue bind: "payment.done" ✗ Eşleşme Tablosu (Direct = birebir eşleşme) Routing Key Binding Key (Queue) Sonuç "order.created" "order.created" (order-processing) ✓ Eşleşti → gider "order.created" "payment.done" (payment-queue) ✗ Eşleşmedi → gitmez

Benzetme: Posta kutusuna isim yaz. Sadece o isme gelen mektuplar düşer.

Ne Zaman Kullan Ne Zaman Kullanma
Mesajı bilinen tek bir queue'ya göndermek Aynı mesajı birden fazla yere göndermek
1 gönderici → 1 alıcı basit iletişim Routing key'de joker karakter (*, #) gerekiyorsa
Cevap beklenen istek-yanıt akışlarında (request → response queue) "Herkese duyur" senaryolarında (Fanout daha uygun)
.NET — Direct Exchange
// Direct exchange declare
await channel.ExchangeDeclareAsync("orders.direct", ExchangeType.Direct, durable: true);

// Queue declare & bind (key = "order.created")
await channel.QueueDeclareAsync("order-processing", durable: true, exclusive: false, autoDelete: false);
await channel.QueueBindAsync("order-processing", "orders.direct", routingKey: "order.created");

// Publish — routing key birebir eşleşmeli
await channel.BasicPublishAsync(
    exchange: "orders.direct",
    routingKey: "order.created",  // ← "order.created" bind'ına exact match
    body: messageBody);

3.2 Fanout Exchange — "Herkese Duyuru"

Routing key'i tamamen yok sayar. Exchange'e bind olan tüm queue'lara aynı mesajı gönderir. Kaç queue bind olursa olsun, hepsine aynı mesaj ulaşır.

Producer key="" (önemsiz) user.signup.fanout (fanout exchange) signup-email signup-analytics signup-audit Fanout Kuralı: Key kontrol edilmez — bind olan herkese gider Routing Key Bind Olan Queue'lar Sonuç "" (boş, önemsiz) signup-email, signup-analytics, signup-audit ✓ Hepsine gider

Benzetme: Hoparlörden anons. Kim bağlıysa duysun — seçici değil.

Ne Zaman Kullan Ne Zaman Kullanma
Aynı event'i birden fazla servise iletmek (email + analytics + audit) Mesajın sadece belirli alıcılara gitmesi gerektiğinde
"Bir şey oldu, ilgilenen herkes duysun" duyuruları Alıcıların çoğu mesajı işlemeyecekse (gereksiz yük)
Chat odası gibi: herkese aynı mesaj Mesajları filtrelemek gerekiyorsa → Topic kullan
.NET — Fanout Exchange (Broadcast)
// Fanout exchange — routing key ignored
await channel.ExchangeDeclareAsync(
    exchange: "user.signup.fanout",
    type: ExchangeType.Fanout,
    durable: true);

// Her consumer kendi queue'sunu bind eder
await channel.QueueDeclareAsync("signup-email", durable: true, exclusive: false, autoDelete: false);
await channel.QueueDeclareAsync("signup-analytics", durable: true, exclusive: false, autoDelete: false);
await channel.QueueDeclareAsync("signup-audit", durable: true, exclusive: false, autoDelete: false);

await channel.QueueBindAsync("signup-email", "user.signup.fanout", routingKey: "");       // key ignored
await channel.QueueBindAsync("signup-analytics", "user.signup.fanout", routingKey: "");   // key ignored
await channel.QueueBindAsync("signup-audit", "user.signup.fanout", routingKey: "");       // key ignored

// Publish — routing key boş olabilir, fanout yok sayar
await channel.BasicPublishAsync("user.signup.fanout", routingKey: "", body: body);

3.3 Topic Exchange — "Akıllı Filtreleme"

Direct gibi routing key kullanır ama wildcard pattern destekler. Key noktalarla ayrılmış kelimelerden oluşur (ör: order.eu.created). Queue'lar pattern ile bind olur:

  • * → tam 1 kelime eşleşir
  • #0 veya daha fazla kelime eşleşir
Producer order.eu.created domain.events (topic exchange) order-notifications bind: order.*.created ✓ payment-events bind: payment.# ✗ Eşleşme Tablosu (Topic: * = 1 kelime, # = 0+ kelime) Routing Key Binding Pattern (Queue) Sonuç "order.eu.created" "order.*.created" (order-notifications) ✓ * = "eu" "order.eu.created" "payment.#" (payment-events) ✗ "order" ≠ "payment" "order.eu.created" "order.#" (tüm order.* eşleşir) ✓ # = "eu.created"

Benzetme: E-posta filtresi. "order ile başlayan VE created ile biten tüm mailleri göster."

Pattern Eşleşme Tablosu:

Pattern Açıklama Eşleşen Örnekler
order.*.created * = tam 1 kelime order.eu.created · order.us.created · order.created
payment.# # = 0 veya daha fazla kelime payment · payment.success · payment.eu.visa.success
#.error Sonu "error" olan her şey app.service.error · error
*.*.critical 3 segment, sonu critical app.db.critical · app.critical
Ne Zaman Kullan Ne Zaman Kullanma
Kategori bazlı yönlendirme (ör: sipariş.avrupa.oluşturuldu) Tek bir alıcıya gidecekse (Direct daha hızlı)
Farklı alıcılar farklı konuları dinleyecekse Tüm mesajlar herkese gidecekse (Fanout daha basit)
Log yönlendirme: logs.production.error → sadece ops ekibi Joker desenler hata ayıklamayı zorlaştırıyorsa
.NET — Topic Exchange Declare & Publish
// Exchange declare
await channel.ExchangeDeclareAsync(
    exchange: "domain.events",
    type: ExchangeType.Topic,
    durable: true,
    autoDelete: false);

// Queue declare & bind
await channel.QueueDeclareAsync("order-notifications", durable: true, exclusive: false, autoDelete: false);
await channel.QueueBindAsync("order-notifications", "domain.events", routingKey: "order.*.created");

// Publish
var body = JsonSerializer.SerializeToUtf8Bytes(orderEvent);
var props = new BasicProperties
{
    ContentType = "application/json",
    DeliveryMode = DeliveryModes.Persistent,
    MessageId = Guid.NewGuid().ToString()
};

await channel.BasicPublishAsync(
    exchange: "domain.events",
    routingKey: "order.eu.created",  // matches "order.*.created"
    mandatory: true,
    basicProperties: props,
    body: body);

3.4 Headers Exchange — "Çoklu Kriter"

Routing key'i tamamen yok sayar. Bunun yerine mesajın header'larına bakar. x-match: all (tüm header'lar eşleşmeli) veya x-match: any (herhangi biri yeterli) modlarında çalışır.

Producer format=pdf dept=finance reports.headers (headers exchange) x-match: all pdf-reports format=pdf, dept=finance ✓ json-data format=json ✗ Eşleşme Tablosu (Headers: routing key yok, header'lara bakılır) Mesaj Headers Queue Beklentisi Sonuç format=pdf, dept=finance format=pdf, dept=finance ✓ Tüm header eşleşti format=pdf, dept=finance format=json ✗ format uyuşmuyor x-match: any modunda: format=pdf, dept=hr format=pdf, dept=finance ✓ format eşleşti (1 yeter)

Benzetme: Dosya sınıflandırma sistemi. "format=pdf VE department=finance olan dokümanları bu klasöre koy."

Ne Zaman Kullan Ne Zaman Kullanma
Birden fazla özelliğe göre yönlendirme gerekiyor (format + departman + bölge) Basit key eşleşmesi yeterliyse (Direct/Topic daha hızlı)
Routing key yapısı yetersiz kalıyorsa (tek string'e sığmayan kriterler) Yüksek mesaj hacmi — header karşılaştırma daha yavaş
"PDF + finans departmanı + Avrupa" gibi çoklu filtre Topic'in joker desenleri ihtiyacı karşılıyorsa

Karşılaştırma: Hangi Exchange'i Seçmeliyim?

4 exchange tipini tek bakışta karşılaştır:

Direct Exchange Exact routing key match Exchange key="order" key="payment" Queue A Queue B ✓ match Fanout Exchange Broadcast to all bound queues Exchange Q1 Q2 Q3 Hepsine gider (routing key yok sayılır) Topic Exchange Pattern matching: * (one word), # (zero+) Exchange order.*.created payment.# Queue A Queue B order.eu.created ✓ Headers Exchange Match by message headers (x-match: all|any) Exchange format=pdf, type=report format=json PDF Queue JSON Queue

Karar Tablosu

Senaryo Exchange Tipi Neden Gerçek Hayat
Mesajı tek bir queue'ya yönlendir Direct Exact match, basit ve hızlı Order → order-processing queue
Aynı mesajı tüm subscriber'lara gönder Fanout Routing key gereksiz, herkese broadcast User signup → email + analytics + audit
Hiyerarşik routing (namespace.entity.action) Topic Wildcard patterns ile esnek filtreleme logs.production.error → ops team queue
Multi-attribute routing (header bazlı) Headers Routing key yetersiz, birden fazla kriter Content-type + priority bazlı routing
Load distribution (sharding) Consistent Hash (plugin) Mesajları N queue'ya dengeli dağıt High-throughput ingestion pipeline

Dikkat: Topic exchange, direct'e göre biraz daha fazla CPU kullanır (pattern matching). Ancak fark genelde ihmal edilebilir düzeydedir. Routing karmaşıklığı artacaksa baştan topic exchange tercih edin — sonradan geçiş zordur.

Anti-Pattern: God Exchange — Tek bir fanout exchange'e tüm event'leri publish edip, consumer tarafında filtreleme yapmak. Bu, her consumer'ın tüm mesajları alıp %90'ını drop etmesi demektir → bandwidth ve CPU israfı. Exchange'leri domain boundary'lerine göre ayırın.

Gerçek hayat senaryosu: Bir chat uygulamasında, her oda (room) için bir fanout exchange oluşturulur. Odaya katılan her kullanıcı kendi exclusive queue'sunu bu exchange'e bind eder. Mesaj gönderildiğinde tüm katılımcılara aynı anda ulaşır.