RRabbitMQ Handbook

TEMEL

Temel Kavramlar

RabbitMQ Nedir ve Ne Zaman Kullanılır bölümünde mesaj kuyruğunun neden gerekli olduğunu gördük. Şimdi RabbitMQ'nun iç yapısına — bileşenlerine — bakalım.

❌ Senkron: Sipariş onay sonrası kullanıcı bekler DB ✓ Email PDF Push SMS ✗ ← 6sn bekledi + SMS servisi timeout'ta → kullanıcıya hata döndü (sipariş aslında başarılı!) → ✅ Asenkron: DB yazıldı, kullanıcıya hemen 200 OK DB ✓ RabbitMQ Event Email PDF Push SMS 50ms'de 200 OK. Bildirimler arkaplanda, bağımsızca. Transactional iş (ödeme+stok) senkron kalır. Bağımsız side-effect'ler (bildirim, PDF, analytics) queue'ya gider — biri çökse bile sipariş geçerli.

Bileşenler (Posta Ofisi Analojisi)

RabbitMQ 7 temel parçadan oluşur. Hepsini bir posta ofisi gibi düşünebilirsin:

Producer Mesajı gönderen 📮 Mektup yazan kişi Exchange Mesajı yönlendirir (saklamaz!) 📬 Ayırma masası "Bu mektup nereye?" Binding Binding Queue A Mesajları sıraya koyar 📦 Dolap A (siparişler) Queue B Mesajları sıraya koyar 📦 Dolap B (ödemeler) Consumer Mesajı işler 👷 Sipariş ekibi Consumer Mesajı işler 👷 Ödeme ekibi Binding = Adres etiketi kuralı. "order ile başlayan mektuplar → Dolap A'ya" gibi bir eşleme kuralı.
Bileşen Ne Yapar Posta Analojisi Dikkat
Connection Uygulamanla RabbitMQ arasındaki hat 📞 Telefon hattı (bir kere aç, konuşmaya devam et) Uygulama başına 1-2 tane yeter
Channel Hat içindeki paralel yollar. Her iş parçacığı kendi yolunu kullanır 📺 TV kanalları (tek kablo, çok kanal) Aynı channel'ı iki thread paylaşmasın
Producer Mesaj oluşturup gönderir ✉ Mektup yazan kişi Mesajı exchange'e gönderir, doğrudan queue'ya değil
Exchange Mesajı kurallara bakarak doğru queue'ya yönlendirir 📬 Posta ayırma masası ("bu nereye?") Kendisi mesaj tutmaz — sadece yönlendirir
Binding Exchange ile queue arasındaki eşleme kuralı 🏷 "İstanbul adresliler → Dolap-3" kuralı Bir queue birden fazla kurala sahip olabilir
Queue Mesajları sırayla saklar, consumer gelene kadar tutar 📦 Posta dolabı Quorum = birden fazla kopya (güvenli). Classic = tek kopya
Consumer Queue'dan mesaj alıp işleyen uygulama 👷 Dolaptaki mektupları alan kişi "Aldım" (ack) deyince mesaj silinir

Aşağıdaki şemada Connection ve Channel'ın diğer bileşenlerle ilişkisini görebilirsin:

TCP Connection (long-lived) Channel (lightweight, multiplexed) Producer publish() Exchange Routing Logic direct|fanout|topic|headers Binding routing_key = "order.*" Binding routing_key = "payment.#" Queue A durable, quorum Queue B durable, classic Consumer ack/nack Consumer ack/nack 1 Connection = N Channels 1 Channel = 1 thread context N Bindings = routing rules

📖 Terminoloji Rehberi (bu terimleri görünce korkma):

Terim Ne Demek
TCP İki bilgisayar arasında güvenilir veri iletimi sağlayan bağlantı protokolü
TLS TCP üzerinde şifreleme (HTTPS'teki "S" gibi)
AMQP RabbitMQ'nun konuştuğu dil — mesaj gönder/al komutlarının standardı
Multiplexed Tek kablo üzerinde birden fazla bağımsız konuşma (TV kanalları gibi)
Durable Broker yeniden başlasa bile hayatta kalır (diske yazılır)
Quorum Birden fazla node'da kopyası tutulan queue (biri çökse diğeri devam eder)
Ack "Aldım, işledim" onayı. Bu onay gelene kadar mesaj queue'da tutulur.

Connection & Channel Kullanım Kuralları

Durum Doğru Yanlış
Connection Uygulama başında 1 tane aç, kapanana kadar kullan Her mesajda yeni connection aç (broker boğulur)
Channel Her paralel iş için ayrı channel al Tek channel'ı her yerden kullan (veri karışır)
Exchange İhtiyaca göre tip seç (Exchange Tipleri sayfasında detaylandırılıyor) Her şeyi default exchange'e gönder

Neden "1 connection, N channel"? TCP bağlantısı açmak pahalıdır. Ama bir bağlantı açtıktan sonra içinden binlerce "kanal" geçirebilirsin — bedavaya. Bağlantıyı bir kere aç, kanallarla çoğalt.

.NET — Bağlantı Nasıl Açılır (Connection + Channel)
// Program.cs — Uygulama başladığında 1 kere çalışır
// Connection: uygulama boyunca açık kalır (singleton)
services.AddSingleton<IConnection>(sp =>
{
    var factory = new ConnectionFactory
    {
        HostName = "rabbitmq-cluster.internal",
        Port = 5672,
        UserName = "app_service",
        Password = configuration["RabbitMQ:Password"],
        AutomaticRecoveryEnabled = true,
        NetworkRecoveryInterval = TimeSpan.FromSeconds(10),
        RequestedHeartbeat = TimeSpan.FromSeconds(30)
    };
    return factory.CreateConnectionAsync().GetAwaiter().GetResult();
});

// Per-scope channel (Scoped lifetime)
services.AddScoped<IChannel>(sp =>
{
    var connection = sp.GetRequiredService<IConnection>();
    return connection.CreateChannelAsync().GetAwaiter().GetResult();
});
Yanlış — Her Mesajda Yeni Bağlantı (Neden Kötü?)
// ❌ YANLIŞ: Her mesaj gönderiminde sıfırdan bağlantı açıyor
public async Task PublishOrderAsync(Order order)
{
    var factory = new ConnectionFactory { HostName = "localhost" };
    using var connection = await factory.CreateConnectionAsync(); // Her seferinde yeni TCP bağlantısı!
    using var channel = await connection.CreateChannelAsync();
    // ...mesaj gönder...
} // Fonksiyon bitince bağlantı kapanıyor. Sonraki mesajda tekrar açılacak.
  // Saniyede 100 mesaj = 100 bağlantı aç-kapa → broker boğulur