ORTA
Dead Letter Exchange & Retry Patterns
Bir mesaj işlenemediğinde ne olur? Kaybolmasın istiyorsan bir "çöp değil, tekrar deneme" mekanizması lazım. Dead Letter Exchange (DLX) tam bunu yapar: başarısız mesajları ayrı bir queue'ya yönlendirir — oradan tekrar denenebilir, incelenebilir veya loglanabilir.
DOĞRULANMAMIŞ ÖZELLİK: Bu bölümdeki Native Delayed Retry policy key'leri (
delayed-retry-type,delayed-retry-min,delayed-retry-max) RabbitMQ 4.3 release notes'tan alınmıştır ancak resmi dokümantasyonda henüz doğrulanamamıştır. Production'da kullanmadan öncerabbitmqctl list_policiesile key'lerin geçerli olduğunu mutlaka test edin. Eski sürümlerde bu key'ler sessizce ignore edilir — hata vermez ama retry çalışmaz.
Basit analoji: Postane bir mektubu teslim edemezse çöpe atmaz — "iade" damgası vurup gönderene veya özel bir dolaba koyar. DLX = o iade mekanizması.
Mesaj ne zaman "dead letter" olur: reddedildiğinde (nack/reject), süresi dolduğunda (TTL) veya queue dolduğunda (limit aşımı). RabbitMQ 4.3 ile gelen native Delayed Retry özelliği, eski workaround'lara olan ihtiyacı azaltır.
DLX Tetiklenme Koşulları
| Koşul | Açıklama | Gerçek Hayat |
|---|---|---|
basic.reject / basic.nack (requeue=false) |
Consumer mesajı işleyemedi | Geçersiz format, business rule violation |
| Message TTL expired | x-message-ttl süresi doldu |
24 saat içinde işlenmeyen sipariş |
| Queue length limit exceeded | x-max-length veya x-max-length-bytes |
Queue overflow koruması |
| Delivery limit exceeded | x-delivery-limit (Quorum queue, default: 20) |
Poison message koruması |
Delayed Retry: Eski vs Yeni
| Özellik | Eski Yöntem (TTL + DLX loop) | RabbitMQ 4.3 Native Delayed Retry |
|---|---|---|
| Kurulum | Retry queue + DLX binding + TTL per queue | Tek policy: delayed-retry-type |
| Backoff | Sabit (queue başına sabit TTL) | Linear: min_delay × delivery_count |
| Max delay | Queue sayısı kadar farklı TTL gerekir | delayed-retry-max ile cap |
| Queue overhead | Her retry seviyesi için ayrı queue | Tek queue, internal deferred state |
| Gözlenebilirlik | Zayıf (hangi retry'da belirsiz) | Management UI'da deferred count görünür |
Gerçek hayat senaryosu: E-ticaret stok servisi: Payment confirmation geldi ama inventory API geçici olarak down. Consumer nack gönderir, delayed retry devreye girer: 1. deneme 1sn, 2. deneme 2sn, 3. deneme 3sn... max 30sn'ye kadar. 5 başarısız delivery sonrası mesaj DLX'e düşer ve ops team alert alır.
# ⚠️ DOĞRULANMAMIŞ: Bu policy key'leri RabbitMQ 4.3 release notes'tan alınmıştır.
# Production'da önce test ortamında doğrulayın: rabbitmqctl list_policies
# Çalışmazsa aşağıdaki .NET DLX+TTL pattern'ini kullanın (doğrulanmış).
#
# Delayed retry policy: tüm "order." queue'larına uygula
rabbitmqctl set_policy order-retry "^order." '{"delayed-retry-type":"all","delayed-retry-min":1000,"delayed-retry-max":30000,"delivery-limit":5,"dead-letter-exchange":"dlx.main"}' --priority 10 --apply-to "quorum_queues"
# Sonuç:
# - İlk retry: 1000ms
# - İkinci retry: 2000ms
# - Üçüncü retry: 3000ms
# - ... (capped at 30000ms)
# - 5. delivery-count aşılırsa → dlx.main exchange'e dead-letter
// 1. DLX Exchange
await channel.ExchangeDeclareAsync("dlx.main", ExchangeType.Direct, durable: true);
// 2. Dead letter queue (manual review)
await channel.QueueDeclareAsync("dead-letters", durable: true, exclusive: false, autoDelete: false,
arguments: new Dictionary<string, object?>
{
["x-queue-type"] = "quorum",
["x-message-ttl"] = 604_800_000 // 7 gün retention sonra sil
});
await channel.QueueBindAsync("dead-letters", "dlx.main", routingKey: "#");
// 3. Main queue — DLX bağlantılı
await channel.QueueDeclareAsync("order-processing", durable: true, exclusive: false, autoDelete: false,
arguments: new Dictionary<string, object?>
{
["x-queue-type"] = "quorum",
["x-dead-letter-exchange"] = "dlx.main",
["x-dead-letter-routing-key"] = "order.failed",
["x-delivery-limit"] = 5
});
at-least-once DLX (Quorum Queue):
dead-letter-strategy: at-least-onceaktifleştirildiğinde, DLX target queue confirm gönderene kadar mesaj source queue'da tutulur. Bu daha güvenlidir amaoverflow: reject-publishzorunludur (drop-headile çalışmaz).