Eksiksiz RabbitMQ Rehberi — Özellikler, Pattern'ler ve Best Practice'ler
AMQP temellerinden exchange tiplerine, güvenilirlik mekanizmalarına, clustering/HA’ya, mesajlaşma pattern’lerine, performans ayarlarına ve production best practice’lerine kadar derinlemesine, uygulayıcı seviyesinde bir referans.
İçindekiler
- Temel Kavramlar
- Exchange Tipleri
- Kuyruklar: Tipler ve Özellikler
- Mesaj Özellikleri ve Teslimat Semantiği
- Publisher Confirms ve Güvenilir Yayınlama
- Consumer Acknowledgment ve Prefetch (QoS)
- Dead Letter Exchange (DLX)
- TTL: Mesaj ve Kuyruk
- Gecikmeli (Delayed) Mesajlar
- Öncelikli Kuyruklar (Priority Queues)
- Clustering ve Yüksek Erişilebilirlik
- Federation ve Shovel
- Güvenlik
- Yönetim, İzleme ve Gözlemlenebilirlik
- Mesajlaşma Pattern’leri
- Connection ve Channel Yönetimi Best Practice’leri
- Idempotency ve Hata Yönetimi
- Performans Ayarlama
- Sık Yapılan Hatalar
- Hızlı Referans / Cheat Sheet
1. Temel Kavramlar
RabbitMQ, AMQP 0-9-1 protokolünü uygular (MQTT, STOMP ve AMQP 1.0 için eklentiler de mevcuttur). Temel kavramlar:
- Producer (Üretici) — mesajları bir Exchange’e yayınlar (asla doğrudan bir kuyruğa değil).
- Exchange — mesajları kurallara (binding’ler + routing key’ler) göre bir veya birden fazla kuyruğa yönlendirir.
- Binding — bir exchange ile bir kuyruk arasındaki, isteğe bağlı olarak bir routing key veya argümanlarla tanımlanan bağlantı.
- Queue (Kuyruk) — tüketilene kadar mesajları saklayan sıralı bir tampon.
- Consumer (Tüketici) — bir kuyruğa abone olur ve mesajları işler.
- Virtual Host (vhost) — exchange’leri, kuyrukları ve izinleri izole eden mantıksal bir isim alanı (multi-tenancy için).
- Connection — broker’a açılan bir TCP bağlantısı (oluşturması pahalıdır; uzun ömürlü olmalıdır).
- Channel — tek bir TCP bağlantısı üzerinde çoğullanan hafif bir sanal bağlantı (ucuzdur; thread/task başına bir tane).
Altın kural: Producer’lar asla “bir kuyruğa” yayın yapmaz. Her zaman bir routing key ile bir exchange’e yayın yaparlar. Mesajın nereye gideceğine exchange karar verir.
Producer → Exchange → (Binding + Routing Key) → Queue → Consumer
2. Exchange Tipleri
2.1 Direct Exchange
Mesajları, binding key’i mesajın routing key’i ile tam olarak eşleşen kuyruklara yönlendirir.
Exchange: orders.direct
Binding: routing_key = "order.created" → Queue: orders.created.q
Binding: routing_key = "order.cancelled" → Queue: orders.cancelled.q
Kullanım alanı: Hedefin deterministik olduğu görev yönlendirmesi (örn. olay tipine göre yönlendirme).
2.2 Fanout Exchange
Routing key’i tamamen görmezden gelerek bağlı tüm kuyruklara yayın yapar.
Kullanım alanı: Pub/Sub — bildirimler, cache invalidation, birden fazla bağımsız servise olay yayınlama.
Exchange: notifications.fanout
→ Queue: email.service.q
→ Queue: sms.service.q
→ Queue: audit.log.q
2.3 Topic Exchange
Routing key üzerinde wildcard kullanarak pattern eşleştirmesi yapar:
*tam olarak bir kelimeyle eşleşir#sıfır veya daha fazla kelimeyle eşleşir
Routing key formatı: <bölge>.<önem_derecesi>.<servis>
Binding: "eu.*.payments" → "eu.error.payments" ile eşleşir
Binding: "*.critical.#" → "us.critical.payments.timeout" ile eşleşir
Binding: "eu.#" → "eu." ile başlayan her şeyle eşleşir
Kullanım alanı: Esnek olay yönlendirmesi — loglama sistemleri, çok boyutlu filtreleme.
2.4 Headers Exchange
Routing key yerine mesajın header özniteliklerine göre yönlendirme yapar. x-match argümanını kullanır:
x-match: all— tüm header’lar eşleşmelidir (AND)x-match: any— en az bir header eşleşmelidir (OR)
{
"x-match": "all",
"format": "pdf",
"type": "report"
}
Kullanım alanı: Pratikte nadiren kullanılır — routing key pattern’leri (topic) genellikle yeterlidir ve daha hızlıdır. Headers exchange’i yalnızca yönlendirme kriterleri doğal olarak bir string key’e sığmadığında kullanın.
2.5 Varsayılan (İsimsiz) Exchange
Her kuyruğun otomatik olarak, kuyruk adını routing key olarak kullanarak bağlandığı özel bir direct exchange (""). routing_key = "my_queue" ile ""‘a yayın yapmak mesajı doğrudan my_queue‘ya iletir. Basit noktadan-noktaya senaryolar için kullanışlıdır, ancak büyük sistemlerde buna aşırı bağımlı olmaktan kaçının — açık exchange’ler yönlendirme topolojisini görünür ve geliştirilebilir kılar.
2.6 Alternate Exchange (AE)
Hiçbir yere yönlendirilemeyen (eşleşen binding olmayan) mesajları almak üzere yapılandırılmış bir exchange. Sessiz mesaj kaybını önler.
rabbitmqctl set_policy AE-orders "^orders\." '{"alternate-exchange":"orders.unrouted"}' --apply-to exchanges
3. Kuyruklar: Tipler ve Özellikler
3.1 Classic Queues (Klasik Kuyruklar)
Orijinal kuyruk tipi. Tek node veri yapısıdır, artık kullanımdan kaldırılan classic mirrored queues (ha-mode politikası ile mirror edilir) aracılığıyla replike edilir. HA için yerini quorum queue’lara bırakmaktadır.
3.2 Quorum Queues (RabbitMQ 3.8+‘dan itibaren çoğu dayanıklı iş yükü için önerilir)
Raft konsensüs algoritması üzerine kuruludur. Veri güvenliği önceliklidir:
- Her zaman birden fazla node arasında replike edilir (önerilen minimum: 3, tek sayı)
- Classic mirrored queue’ların yaşadığı “flapping” mirror yeniden senkronizasyon sorunları yoktur
- Daha iyi throughput ve öngörülebilir failover
- Şunları desteklemez: mesaj önceliği (3.13 öncesi), erken sürümlerde mesaj başına TTL (artık destekleniyor), exclusive kuyruklar, durable olmayan kuyruklar
# Argümanlarla bir quorum queue tanımlama
channel.queue_declare(queue='orders.q', durable=True, arguments={'x-queue-type': 'quorum'})
3.3 Streams (RabbitMQ 3.9+)
Ekleme-yalnızca (append-only) log soyutlaması (Kafka topic’lerine benzer). Şunları destekler:
- Yıkıcı olmayan okuma (birden fazla bağımsız consumer herhangi bir offset’ten tekrar oynatabilir)
- Fan-out ağırlıklı iş yükleri için yüksek throughput
- Mesajların uzun süreli saklanması
arguments={'x-queue-type': 'stream', 'x-max-length-bytes': 20_000_000_000}
Streams’i şu durumda seçin: replay, geniş fan-out veya event-sourcing benzeri semantiklere ihtiyacınız varsa. Quorum Queues’ü şu durumda seçin: güçlü güvenlik garantileriyle geleneksel work-queue semantiğine ihtiyacınız varsa. Yeni sistemlerde Classic Queue’lardan kaçının — özellikle geçici/exclusive kuyruk semantiğine veya 3.13 öncesi mesaj başına önceliğe ihtiyacınız yoksa.
3.4 Exclusive ve Auto-Delete Kuyruklar
- Exclusive: sadece tek bir bağlantı tarafından kullanılır, o bağlantı kapandığında silinir. RPC yanıt kuyrukları veya client’a özel geçici kuyruklar için kullanışlıdır.
- Auto-delete: son consumer aboneliği bıraktığında silinir.
3.5 Kuyruk Uzunluk Limitleri ve Overflow Davranışı
arguments={
'x-max-length': 100000,
'x-max-length-bytes': 500_000_000,
'x-overflow': 'reject-publish' # veya 'drop-head' (varsayılan)
}
reject-publish, kritik veriler için daha güvenlidir — en eski mesajları sessizce düşürmek yerine yeni yayınları nack’ler.
4. Mesaj Özellikleri ve Teslimat Semantiği
Her zaman bilinçli olarak ayarlanması gereken temel AMQP mesaj özellikleri:
| Özellik | Amaç |
|---|---|
delivery_mode | 2 = kalıcı (kuyruk durable ise broker yeniden başlatıldığında hayatta kalır), 1 = geçici |
content_type | örn. application/json — consumer’ların doğru şekilde deserialize etmesine yardımcı olur |
message_id | Dedup/tracing için benzersiz ID |
correlation_id | Bir isteği bir yanıta bağlar (RPC için gereklidir) |
reply_to | Yanıtlayanın cevabı yayınlaması gereken kuyruk adı |
timestamp | Mesajın oluşturulma zamanı |
expiration | Mesaj başına TTL, ms cinsinden (string olarak) |
headers | Serbest formatlı anahtar-değer metadata (tracing, versiyonlama, yönlendirme) |
app_id / type | Debugging/gözlemlenebilirlik için kullanışlı |
RabbitMQ’nun varsayılan teslimat garantisi “en az bir kez” (at-least-once) şeklindedir (ack’lerle birlikte) — kutudan çıktığı haliyle asla “tam olarak bir kez” (exactly-once) değildir. Consumer’ları idempotent olacak şekilde tasarlayın (bkz. §17).
5. Publisher Confirms ve Güvenilir Yayınlama
Confirm mekanizması olmadan, bir yayın sessizce başarısız olabilir (ağ kesintisi, broker persist etmeden önce çökmesi) ve bunu asla bilemezsiniz.
Publisher Confirms (confirm.select), broker’ın her yayınlanan mesajı güvenli bir şekilde işledikten sonra (durable kuyruklar için diske kaydedildiğinde veya diğerleri için yönlendirildiğinde) asenkron olarak onaylamasını sağlar.
channel.confirm_delivery() # pika senkron yardımcı fonksiyonu
# Asenkron pattern (throughput için önerilir):
channel.confirm_select()
outstanding = {}
def on_ack(frame):
outstanding.pop(frame.delivery_tag, None)
def on_nack(frame):
# yeniden yayınla veya uyar — broker mesajı işleyemedi
handle_failed_publish(frame.delivery_tag)
channel.add_on_return_callback(on_returned) # mandatory=True ile yönlendirilemeyen mesajlar için
Önerilen best practice pattern:
- Confirm mekanizmasını channel başına bir kez etkinleştirin.
- Bekleyen (onaylanmamış) mesajları
delivery_tag‘e göre bir map’te takip edin. - Yönlendirilemeyen mesajları tespit etmeniz gerekiyorsa
mandatory=Trueile yayın yapın (birbasic.returnhandler’ıyla birlikte), ya da bunun yerine bir Alternate Exchange kullanın (yüksek throughput altında mesaj başınamandatorybayrağından daha ölçeklenebilirdir). - Throughput için confirm’leri toplu (batch) işleyin — her mesajın ack’ini tek tek beklemeyin; birçoğunu yayınlayın, ardından ack’leri asenkron olarak takip edin.
- Nack veya timeout durumunda, backoff ile yeniden deneyin veya “onaylanmamış” bir denetim kuyruğuna dead-letter yapın.
Transaction’lar vs Confirms
AMQP transaction’ları (tx.select, tx.commit) mevcuttur ancak çok daha yavaştır (her transaction için senkron round-trip) — publisher confirms modern ve performanslı alternatiftir ve neredeyse her zaman tercih edilmelidir.
6. Consumer Acknowledgment ve Prefetch (QoS)
6.1 Ack Modları
- Manuel ack (
auto_ack=False, önerilen varsayılan): consumer, mesajı başarıyla işledikten sonra açıkçabasic.ackçağırır. Consumer ack’lemeden önce ölürse, mesaj yeniden kuyruğa alınır (requeue) ve tekrar teslim edilir. - Otomatik ack (
auto_ack=True): broker, mesajı kablo üzerinden gönderdiği anda teslim edilmiş sayar — tehlikelidir, consumer işlem sırasında çökerse mesajlar kaybolur. Yalnızca gerçekten sarf edilebilir/düşük değerli veriler için kullanın. - Nack/Reject:
basic.nack(requeue=True/False)— başarısızlığı açıkça belirtir.requeue=Falsegenellikle yapılandırılmışsa bir DLX’e yönlendirir, aksi halde mesajı atar.
6.2 Prefetch (QoS)
basic.qos(prefetch_count=N), bir consumer’ın aynı anda tutabileceği onaylanmamış mesaj sayısını sınırlar. Bu, consumer throughput’u ve adaleti için en önemli tek ayar noktasıdır.
channel.basic_qos(prefetch_count=50)
- Çok düşük (örn. 1): güvenli/adil ama throughput sınırlıdır — consumer bir sonraki mesajın round-trip’ini bekleyerek boşta kalır.
- Çok yüksek: bir yavaş consumer binlerce mesajı elinde tutarken diğerleri açlık çekebilir; ayrıca o consumer çökerse büyük yeniden teslimat fırtınaları riski vardır.
- Genel kural: işleme süresi × hedef throughput’a dayalı olarak (consumer başına istenen uçuş halindeki mesaj sayısı) ile başlayın. Hızlı, tekdüze görevler için 100–300 yaygındır. Yavaş/ağır görevler için (her biri saniyeler sürüyorsa), consumer’lar arasında adalet için düşük tutun (1–10).
- Tek bir channel üzerinde birden fazla consumer varsa, global=False (consumer başına) ayarlanmadığı sürece prefetch aralarında paylaşılır — kanal genelinde paylaşım açıkça istenmedikçe her zaman consumer başına QoS kullanın.
6.3 Manuel Ack Sıralaması
Ack’ler bir channel üzerindeki teslimata göre sırayla gönderilmelidir; multiple=True ile belirli bir delivery tag’e kadar toplu ack gönderebilirsiniz — ancak dikkatli olun: bu, o tag’e kadar (dahil) her şeyi ack’ler, bu yüzden bunu bir channel’ı paylaşan eşzamanlı worker’lardan koordinasyon olmadan asla yapmayın.
7. Dead Letter Exchange (DLX)
Mesajlar şu durumlarda “ölür”:
requeue=Falseile reddedildiğinde (basic.nack/basic.reject)- TTL süresi dolduğunda
- Kuyruk uzunluk limiti aşıldığında (overflow reject)
Ölü mesajları bir DLX’e yönlendirmek için kuyruğu yapılandırın:
channel.queue_declare(
queue='orders.q',
durable=True,
arguments={
'x-dead-letter-exchange': 'orders.dlx',
'x-dead-letter-routing-key': 'orders.failed'
}
)
Standart pattern — DLX + TTL kullanarak backoff ile yeniden deneme:
orders.q --(reddet/süresi dolsun)--> orders.retry.dlx
|
v
orders.retry.q (TTL=5000ms, consumer yok,
DLX geri orders.q'ya işaret eder)
|
(TTL süresi dolduktan sonra, mesaj dead-letter olur)
v
orders.q (tekrar denenir)
Bu “park yeri kuyruğu” (parking lot queue) pattern’i, herhangi bir eklentiye ihtiyaç duymadan gecikmeli yeniden deneme uygular. Header tabanlı bir yeniden deneme sayacı ekleyin (RabbitMQ tarafından otomatik olarak eklenen x-death dizisi) ve yeniden denemeleri sınırlamak ve N denemeden sonra manuel inceleme için son bir poison-message / DLQ (dead-letter queue) kuyruğuna yönlendirmek için x-death[0].count değerini inceleyin.
def handle_message(ch, method, properties, body):
headers = properties.headers or {}
deaths = headers.get('x-death', [])
retry_count = deaths[0]['count'] if deaths else 0
if retry_count >= 5:
ch.basic_publish(exchange='orders.poison', routing_key='', body=body)
ch.basic_ack(method.delivery_tag)
return
# ... işle, DLX üzerinden yeniden deneme tetiklemek için requeue=False ile nack et
Production kuyrukları için her zaman bir DLX yapılandırın. DLX’i olmayan bir kuyruk, reddedilen/süresi dolan mesajları sessizce kaybeder.
8. TTL: Mesaj ve Kuyruk
Mesaj başına TTL:
properties = pika.BasicProperties(expiration='60000') # 60 saniye, ms cinsinden string olarak
Kuyruk başına TTL (kuyruktaki tüm mesajlara uygulanır):
arguments={'x-message-ttl': 60000}
Kuyruk TTL’i (kullanılmayan bir kuyruğu otomatik silme):
arguments={'x-expires': 1800000} # 30 dakika kullanılmadıktan sonra kuyruğu sil
Not: hem mesaj başına hem de kuyruk başına TTL ayarlanmışsa, her mesaj için düşük olan değer kazanır. Ayrıca, classic kuyruklarda TTL süresinin dolması kuyruğun başında tembel (lazy) olarak değerlendirilir — henüz süresi dolmamış bir mesajın arkasında sıkışmış uzun ömürlü bir mesaj, başa gelene kadar dead-letter olmaz (classic kuyruk davranışı quorum/stream’den biraz farklıdır).
9. Gecikmeli (Delayed) Mesajlar
RabbitMQ’da kutudan çıktığı haliyle yerleşik bir “X dakika bekle sonra teslim et” primitifi yoktur. İki yaklaşım vardır:
9.1 Delayed Message Exchange Eklentisi (community plugin)
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
channel.exchange_declare(
exchange='delayed.exchange',
exchange_type='x-delayed-message',
arguments={'x-delayed-type': 'direct'}
)
properties = pika.BasicProperties(headers={'x-delay': 15000}) # 15sn gecikme
Kullanımı basittir, ancak gecikmeli mesajları içeride Mnesia’da saklar; bu da çok yüksek hacimlerde veya çok uzun gecikmelerde iyi ölçeklenmez — orta düzey kullanım için uygundur.
9.2 TTL + DLX “Park Yeri” Pattern’i (native, daha ölçeklenebilir)
§7’deki yeniden deneme pattern’iyle aynı mekanizma: x-message-ttl ayarlı ve consumer’ı olmayan bir kuyruğa yayın yapın, TTL süresi dolduğunda gerçek hedefe geri dead-letter yapılsın. Sadece core primitifleri kullandığı için yüksek ölçekli veya uzun gecikmeli senaryolarda tercih edilir.
10. Öncelikli Kuyruklar (Priority Queues)
channel.queue_declare(queue='tasks.q', arguments={'x-max-priority': 10})
properties = pika.BasicProperties(priority=8)
- 1–255 arası maksimum öncelik seviyesi desteklenir, ancak küçük tutun (örn. 0–10) — her öncelik seviyesi ek dahili overhead getirir.
- Tarihsel olarak sadece classic kuyruklar önceliği tam destekledi; quorum kuyrukları daha sonraki sürümlerde öncelik desteği kazandı — quorum kuyruklarıyla buna güvenmeden önce RabbitMQ sürümünüzün release notlarını kontrol edin.
- Öncelik yalnızca bir birikinti (backlog) olduğunda etkili olur — consumer’lar gerçek zamanlı olarak yetişebiliyorsa, mesajlar geldikleri hızda neredeyse tüketildiğinden öncelik nadiren önemlidir.
11. Clustering ve Yüksek Erişilebilirlik
11.1 Cluster Temelleri
- Bir RabbitMQ cluster’ı, kullanıcıları, vhost’ları, izinleri, exchange’leri ve kuyruk metadata’sını tüm node’lar arasında paylaşır.
- Kuyruk içeriği (mesajlar), quorum kuyruklar/stream’ler kullanılmadığı sürece (bunlar veriyi Raft aracılığıyla kendisi replike eder) belirli node(lar)da yaşar.
- Node’lar Erlang distribution üzerinden iletişim kurar — güvenilir, düşük gecikmeli bir ağ gerektirir (ideal olarak aynı veri merkezi/AZ, WAN değil).
11.2 HA için Quorum Queues (Önerilir)
- Raft kullanarak N node arasında replike edilir;
(N-1)/2node arızasına tolerans gösterir. - 3-node’lu bir quorum kuyruğu 1 node arızasına dayanır; 5-node’lu ise 2’ye.
- Arıza durumunda otomatik lider seçimi — client’lar şeffaf bir şekilde yeni lidere yeniden bağlanır.
- Politika veya declare zamanı argümanı ile yapılandırılır:
rabbitmqctl set_policy ha-orders "^orders\." '{"x-queue-type":"quorum"}' --apply-to queues
11.3 Classic Mirrored Queues (Legacy — yeni dağıtımlarda kaçının)
ha-mode politikaları (all, exactly, nodes) ile kontrol edilir. Ağ bölünmeleri (partition) sırasında split-brain/yeniden senkronizasyon sorunlarıyla bilinir; kullanımdan kaldırılan bir yoldur — RabbitMQ ekibi bunun yerine quorum kuyrukları önerir.
11.4 Client’ları Load Balance Etme
Cluster’ın önüne bir TCP load balancer (HAProxy, cloud LB) veya client tarafında bir node listesi koyun. Herhangi bir node arızalanabileceğinden client’lar backoff ile yeniden bağlanma mantığını uygulamalıdır.
11.5 Ağ Bölünmeleri (Network Partitions)
cluster_partition_handling‘i yapılandırın:
ignore(varsayılan, riskli)autoheal— cluster kazanan bir bölümü seçer ve kaybeden taraftaki node’ları yeniden başlatırpause_minority— azınlık bölümündeki node’lar kendilerini duraklatır (tutarlılık için daha güvenli, production’da yaygın)
11.6 Federation vs Clustering
Clustering = sıkı bağlantı, aynı mantıksal broker, düşük gecikmeli LAN. Veri merkezleri arası/WAN replikasyonu için, bunun yerine Federation veya Shovel kullanın (§12) — asla WAN bağlantıları üzerinden cluster kurmayın.
12. Federation ve Shovel
12.1 Federation
Exchange’leri/kuyrukları ayrı broker’lar/cluster’lar arasında, onları tek bir cluster’a birleştirmeden bağlar. Şunlar için iyidir:
- Çok veri merkezli olay dağıtımı
- Edge cluster’lardan merkezi bir cluster’a olayları toplama
rabbitmqctl set_parameter federation-upstream my-upstream \
'{"uri":"amqp://user:pass@remote-broker","expires":3600000}'
rabbitmqctl set_policy federate-orders "^orders\." '{"federation-upstream-set":"all"}'
12.2 Shovel
Mesajları bir kaynak kuyruk/exchange’den bir hedefe taşıyan daha basit, daha açık bir “pompa” — eklenti olarak çalışır, tek seferlik migrasyonlar veya farklı RabbitMQ sürümleri dahil broker’lar arası basit köprüleme için iyidir.
rabbitmqctl set_parameter shovel my-shovel \
'{"src-uri":"amqp://source","src-queue":"orders.q",
"dest-uri":"amqp://destination","dest-queue":"orders.q"}'
Federation vs Shovel: Federation topoloji farkındadır (exchange binding’lerini dinamik olarak yansıtır); Shovel sabit bir noktadan noktaya borudur. Basit, iyi tanımlanmış köprüler için Shovel kullanın; daha dinamik çok cluster’lı topolojiler için Federation kullanın.
13. Güvenlik
13.1 Kullanıcılar, Vhost’lar ve İzinler
Her zaman en az ayrıcalık ilkesiyle erişimi sınırlayın:
rabbitmqctl add_vhost orders-service
rabbitmqctl add_user orders_app StrongPassword!
rabbitmqctl set_permissions -p orders-service orders_app "^orders\." "^orders\." "^orders\."
# izinler: configure / write / read (regex pattern'leri)
Production’da varsayılan guest kullanıcısını asla kullanmayın — tam olarak bu nedenle varsayılan olarak localhost bağlantılarıyla sınırlıdır; bu kısıtlamayı aşmaya çalışmayın.
13.2 TLS
Loopback olmayan tüm trafik için TLS etkinleştirin:
listeners.ssl.default = 5671
ssl_options.cacertfile = /path/to/ca.pem
ssl_options.certfile = /path/to/cert.pem
ssl_options.keyfile = /path/to/key.pem
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = true
13.3 Kimlik Doğrulama Backend’leri
Dahili kimlik doğrulamanın ötesinde: LDAP, OAuth 2.0 (rabbitmq_auth_backend_oauth2 üzerinden), x.509 client sertifikaları. Büyük organizasyonlarda merkezi kimlik yönetimi için OAuth2/LDAP kullanın.
13.4 Çalışma Zamanı Sıkılaştırması (Hardening)
- İhtiyaç yoksa production’a bakan node’larda kullanılmayan eklentileri ve yönetim UI’sini devre dışı bırakın veya erişimi firewall/VPN ile kısıtlayın.
- Kimlik bilgilerini rotasyona sokun; bunları koda gömmekten kaçının — secret manager kullanın.
rabbitmq_shovel/federation kimlik bilgilerini admin hesapları değil, kapsam belirlenmiş, özel kullanıcılarla etkinleştirin.
14. Yönetim, İzleme ve Gözlemlenebilirlik
14.1 Management Eklentisi
rabbitmq-plugins enable rabbitmq_management
Kuyruklar, bağlantılar, channel’lar, exchange’ler ve politikalar için bir HTTP API ve UI sağlar (varsayılan port 15672).
14.2 Alarm Kurulması Gereken Temel Metrikler
- Kuyruk derinliği / mesaj birikinti büyüme oranı — büyüyen bir kuyruk, consumer’ların yetişemediği anlamına gelir.
- Consumer kullanım oranı (
consumer_utilisationmetriği) — düşük değer, consumer’ların sıklıkla boşta beklediğini gösterir, bu downstream darboğazlarına veya çok düşük prefetch’e işaret edebilir. - Onaylanmamış (unacked) mesaj sayısı — sürekli yüksek değerler, yavaş/takılı consumer’ları veya ack yapmayan çökmüş süreçleri gösterir.
- Broker’da dosya tanımlayıcı / socket kullanımı.
- Bellek ve disk alarmları (
memory_high_watermark,disk_free_limit) — bunlar tetiklendiğinde RabbitMQ tüm publisher’ları bloklar, bu yüzden tetiklenmeden çok önce alarm kurun. - Connection/channel değişim oranı (churn) — sık yeniden bağlanmalar bir client hatasına işaret eder (örn. mesaj başına channel açmak).
14.3 Prometheus Entegrasyonu
rabbitmq-plugins enable rabbitmq_prometheus
Prometheus scraping için 15692 portunda metrikleri açığa çıkarır — Grafana dashboard’larıyla eşleştirin (resmi RabbitMQ Grafana dashboard’ları ekip tarafından yayınlanmaktadır).
14.4 Tracing
rabbitmq_tracing eklentisi debugging için mesaj akışını loglar — gerçek bir performans overhead’i olduğundan yalnızca geçici olarak dev/staging’de etkinleştirin.
15. Mesajlaşma Pattern’leri
15.1 Work Queue (Görev Dağıtımı / Competing Consumers)
Bir kuyruğa bağlı birden fazla consumer; RabbitMQ teslimatları round-robin ile dağıtır (prefetch’e bağlı olarak). Paralelleştirilebilir, bağımsız işler için kullanın (görsel yeniden boyutlandırma, e-posta gönderme).
15.2 Publish/Subscribe
Fanout exchange → her abone servis için bir tane olmak üzere birden fazla kuyruk. Her servis her olayın kendi kopyasını alır.
15.3 Routing (Direct Exchange)
Tam routing key eşleşmesine dayalı seçici teslimat — örn. önem derecesine göre log yönlendirmesi (error, warning, info farklı kuyruklara).
15.4 Topics
Wildcard pattern’ler kullanarak çok kriterli yönlendirme — örn. <bölge>.<servis>.<olay_tipi>.
15.5 RPC (İstek/Yanıt)
# İstek yapan
result = channel.queue_declare(queue='', exclusive=True) # anonim callback kuyruğu
callback_queue = result.method.queue
channel.basic_publish(
exchange='', routing_key='rpc_queue',
properties=pika.BasicProperties(reply_to=callback_queue, correlation_id=corr_id),
body=request
)
# callback_queue'dan tüket, correlation_id'yi eşleştir, sonra yanıtı al
Dikkat: AMQP üzerinden RPC, servisleri senkron olarak bağlar ve gecikme/karmaşıklık ekler. Mümkün olduğunda asenkron, olay güdümlü akışları tercih edin; RPC’yi gerçekten senkron ihtiyaçlar için ayırın.
15.6 Saga Pattern (Dağıtık Transaction’lar)
Dağıtık bir transaction’ı, her biri bir sonraki adımı tetikleyen bir olay yayınlayan bir dizi yerel transaction’a bölün. Başarısızlık durumunda, önceki adımları geri almak için telafi edici (compensating) olaylar yayınlayın.
OrderCreated → PaymentReserved → InventoryReserved → OrderConfirmed
| |
(başarısızlık) (başarısızlık)
v v
PaymentReleased ←──── InventoryReleaseFailed
Basit akışlar için choreography (olay güdümlü, merkezi koordinatör yok) yaklaşımı, görünürlük/kontrol gerektiren karmaşık çok adımlı akışlar için orchestration (özel bir saga koordinatör servisi) yaklaşımı kullanın.
15.7 Transactional Outbox
“Çift yazma” (dual write) sorununu (DB yazımı + mesaj yayınının atomik olmaması) önlemek için, olayı iş değişikliğiyle aynı DB transaction’ında bir outbox tablosuna yazın, ardından RabbitMQ’ya yayınlamak ve outbox satırını gönderildi olarak işaretlemek için ayrı bir relay süreci (polling veya Debezium üzerinden CDC) kullanın. DB durumuyla tutarlı en az bir kez teslimat garanti eder.
15.8 Scatter-Gather
Bir isteği birden fazla servise yayınlayın (fanout), her biri paylaşılan bir correlation-takipli yanıt kuyruğuna yanıt versin; yanıtları bir timeout ile toplayın — paralel zenginleştirme/arama fan-out’u için kullanışlıdır.
15.9 Consumer’larda Circuit Breaker
Consumer’lar içindeki downstream çağrılarını (DB/HTTP) bir circuit breaker ile sarın; tekrarlanan başarısızlıkta, mesajları zorlanan bir bağımlılığı döven sonsuz bir requeue döngüsüne nack’lemek yerine tüketmeyi durdurun (iptal edin/duraklatın).
16. Connection ve Channel Yönetimi Best Practice’leri
- Süreç/servis başına bir uzun ömürlü bağlantı, mesaj başına değil. Bağlantılar pahalıdır (TCP + AMQP handshake + heartbeat).
- Thread/coroutine başına bir channel — channel’lar thread-safe değildir; harici kilitleme olmadan bir channel’ı eşzamanlı thread’ler arasında asla paylaşmayın.
- Mesaj başına asla channel açmayın — bu, RabbitMQ performans şikayetlerinin #1 nedenidir. Channel’ları yeniden kullanın.
- Yüksek eşzamanlılık ortamlarında, istek başına değil eşzamanlılık seviyenize göre boyutlandırılmış bir connection pool kullanın.
- Heartbeat’leri etkinleştirin (varsayılan 60sn) böylece ölü TCP bağlantıları sessizce takılı kalmak yerine hızlıca tespit edilir.
- Üstel backoff + jitter ile otomatik yeniden bağlanma uygulayın — çoğu client kütüphanesi (Spring AMQP, yeniden bağlanma wrapper’larıyla amqplib, aio-pika) bunu doğal olarak veya küçük bir wrapper kodla destekler.
channel.closeolaylarını (örn. var olmayan bir exchange’e yayın yapmak gibi bir protokol hatası nedeniyle) channel’ı yeniden oluşturarak yönetin — kapalı bir channel kalıcı olarak kullanılamaz hale gelir.
17. Idempotency ve Hata Yönetimi
RabbitMQ, normal ack tabanlı çalışma altında en az bir kez (at-least-once) teslimat garanti eder — çoğaltmalar olacaktır (örn. işlendikten sonra ama broker’a ulaşmadan önce ack kaybolması, işlemden sonra ama ack’ten önce consumer çökmesi, ağ yeniden denemeleri). Bunun için tasarlayın:
- Yan etkileri uygulamadan önce, işlem gören ID’leri veritabanınızda/cache’inizde takip etmek için benzersiz bir mesaj ID’si (
message_idveya bir iş anahtarı) kullanın (INSERT ... ON CONFLICT DO NOTHING, RedisSETNX, vb.). - Mümkün olduğunda iş mantığı işlemlerini idempotent hale getirin (örn. “sayacı artır” yerine “durumu X olarak ayarla”).
- İşleme başarısızlığında: geçici hataları (ağ kesintisi, DB timeout —
nack(requeue=True)veya DLX/backoff üzerinden yeniden denemek güvenlidir) kalıcı hatalardan (bozuk payload, iş kuralı ihlali — doğrudan bir poison-message kuyruğuna yönlendirin, sonsuz döngüde yeniden denemeyin) ayırın. - CPU/ağı tüketen sonsuz requeue döngülerinden kaçınmak için yeniden denemeleri her zaman sınırlayın (bkz. §7’deki
x-deathsayacı pattern’i). - Servisler arasında izlenebilirlik için her adımda correlation ID’leri / mesaj ID’lerini loglayın.
18. Performans Ayarlama
- Protokol trafiğini azaltmak için güvenli olduğunda toplu (batch) acknowledgment kullanın (multiple=True) — ancak bunu eşzamanlı olarak yapmadan önce “X’e kadar her şeyi ack’le” semantiğini anlayın.
- Hafif/hızlı görevler için prefetch’i artırın; ağır/yavaş görevler için düşük tutun (bkz. §6.2).
- Sadece gerektiğinde kalıcı (persistent) mesaj kullanın — dayanıklılığın disk I/O maliyeti vardır; kaybın kabul edilebilir olduğu kullanım durumları için (metrikler, geçici olaylar) durable olmayan kuyruklardaki geçici mesajlar çok daha hızlıdır.
- Büyük mesajlardan kaçının (>128KB yaygın bir yumuşak yönerge) — büyük payload’lar için, blob’un kendisi yerine bir referans yayınlayın (örn. S3 URL’si).
- Lazy queue’lar / paging: quorum kuyruklar ve modern classic kuyruklar, bellek baskısı yüksek olduğunda mesajları otomatik olarak diske sayfalar (page) — ancak ağır consumer çekişmesiyle yüksek kuyruk derinliği görüyorsanız, paging devreye girdiğinden disk I/O’yu kontrol edin.
- Cluster’ınızı doğru boyutlandırın: quorum kuyruklar 3–5 node ile parlar ama kuyruk başına çok fazla replika Raft overhead’ini artırır — büyük cluster’lar için bile genellikle 3 replika ideal noktadır (kuyruk liderlerini node’lar arasında eşit dağıtın).
- Bir node’da çok fazla kuyruk/exchange’den kaçının — her biri kendi Erlang süreciyle binlerce kuyruk zamanlama overhead’i ekler; her varlık için bir kuyruk yerine kuyruk sharding veya routing key’lerle konsolidasyonu düşünün.
- Tahmin etmek yerine consumer eşzamanlılığını doğru boyutlandırmak için
consumer_utilisation‘ı izleyin. - Birden fazla çekirdek kullanın: mevcut çekirdekleri doyurmak için yeterli consumer süreci/thread’i çalıştırın — yüksek prefetch’e sahip tek thread’li bir consumer, sahip olmadığı eşzamanlılığı kullanamaz.
19. Sık Yapılan Hatalar
| Hata | Neden Zarar Verir | Çözüm |
|---|---|---|
| Mesaj başına channel açmak | Büyük overhead, bağlantı değişimi (churn) | Uzun ömürlü channel’ları yeniden kullanın |
Her yerde auto_ack=True | Çökme durumunda sessiz mesaj kaybı | Uygun hata yönetimi ile manuel ack |
| DLX yapılandırılmamış | Reddedilen/süresi dolan mesajlar sessizce yok olur | Production kuyruklarına her zaman bir DLX ekleyin |
Sınırsız prefetch (0/çok yüksek) | Bir consumer mesajları biriktirir, adaletsiz dağıtım, çökmede büyük yeniden teslimat fırtınaları | Prefetch’i bilinçli olarak ayarlayın |
| Kritik veride confirm olmadan yayınlamak | Broker/ağ hatasında sessiz veri kaybı | Publisher confirms’ı etkinleştirin |
| RabbitMQ’yu exactly-once gibi ele almak | Tasarım gereği en az bir kezdir (at-least-once) | Idempotent consumer’lar oluşturun |
| Yeni HA ihtiyaçları için classic mirrored queues | Kullanımdan kaldırıldı, split-brain’e eğilimli | Quorum kuyruklar kullanın |
| WAN üzerinde clustering | Yüksek gecikme Erlang distribution’ı bozar, partition’lara neden olur | Bunun yerine WAN üzerinde Federation/Shovel kullanın |
| Kuyruk uzunluğu/TTL limiti yok | Takılı bir consumer’dan sınırsız bellek/disk büyümesi | x-max-length, TTL, x-overflow ayarlayın |
Production’da varsayılan guest kullanıcısını kullanmak | Güvenlik riski | Özel, en az ayrıcalıklı kullanıcılar + TLS |
x-death yeniden deneme sayacını görmezden gelmek | Zehirli (poison) mesajlarda sonsuz yeniden deneme döngüleri | Yeniden denemeleri sınırlayın, poison kuyruğuna yönlendirin |
20. Hızlı Referans / Cheat Sheet
# DLX ve TTL tabanlı yeniden deneme ile durable, quorum kuyruk
channel.queue_declare(
queue='orders.q',
durable=True,
arguments={
'x-queue-type': 'quorum',
'x-dead-letter-exchange': 'orders.dlx',
'x-dead-letter-routing-key': 'orders.retry'
}
)
# Publisher: confirms + kalıcılık
channel.confirm_select()
channel.basic_publish(
exchange='orders.direct',
routing_key='order.created',
body=payload,
properties=pika.BasicProperties(
delivery_mode=2,
content_type='application/json',
message_id=str(uuid4()),
correlation_id=corr_id
),
mandatory=True
)
# Consumer: manuel ack + makul prefetch
channel.basic_qos(prefetch_count=50)
channel.basic_consume(queue='orders.q', on_message_callback=handle, auto_ack=False)
Temel rabbitmqctl komutları:
rabbitmqctl list_queues name messages consumers memory
rabbitmqctl list_connections
rabbitmqctl list_channels
rabbitmqctl cluster_status
rabbitmqctl set_policy <isim> <pattern> '<tanım>' --apply-to queues
rabbitmqctl node_health_check
İleri Okuma
- Resmi dokümantasyon: https://www.rabbitmq.com/docs
- Quorum queues: https://www.rabbitmq.com/docs/quorum-queues
- Streams: https://www.rabbitmq.com/docs/streams
- Güvenilirlik rehberi: https://www.rabbitmq.com/docs/reliability