RabbitMQ Nedir ve Mesaj Kuyruğu Mimarisine Giriş
Üretim ortamında bir servis çöktüğünde, binlerce kullanıcının işlemi kayboluyorsa, ya da bir e-ticaret sitesinde sipariş onay e-postaları bazen gidiyor bazen gitmiyor gibi tuhaf bir durum varsa, büyük ihtimalle eksik olan şey iyi tasarlanmış bir mesaj kuyruğu sistemidir. RabbitMQ, bu tür sorunları çözmek için tasarlanmış, yılların sınavından geçmiş, üretim ortamlarında güvenilirliğini kanıtlamış bir mesaj aracısıdır. Bugün hem teorik arka planını hem de pratik kurulum ve kullanım detaylarını ele alacağız.
Mesaj Kuyruğu Neden Gereklidir
Klasik monolitik mimarilerde servisler birbirleriyle doğrudan konuşur. Servis A, Servis B’yi çağırır, sonuç bekler, devam eder. Bu yaklaşım küçük sistemlerde gayet iyi çalışır. Ancak sistem büyüdükçe sorunlar kaçınılmaz olarak ortaya çıkar.
Düşünün: Bir kullanıcı sipariş verdi. Sipariş servisi bu siparişi veritabanına yazdıktan sonra şunları yapmak zorunda:
- Stok servisini güncelle
- Kargo servisine bildirim gönder
- Müşteriye e-posta at
- Muhasebe sistemine kayıt düşür
- Analitik platformuna event gönder
Eğer bu işlemlerin hepsini senkron olarak yaparsanız, kargo servisi 2 saniye geç cevap verdiğinde kullanıcı sipariş ekranında 2 saniye bekler. Muhasebe sistemi geçici olarak down olduğunda sipariş başarısız görünür. Bu yaklaşım hem performans hem de güvenilirlik açısından felakettir.
Mesaj kuyruğu bu noktada devreye girer. Sipariş servisi tek bir mesaj yayınlar: “Sipariş #12345 oluşturuldu.” Diğer tüm servisler bu mesajı asenkron olarak, kendi kapasitelerine göre tüketir. Sipariş servisi ise kullanıcıya anında cevap döner.
RabbitMQ’nun Mimarisi
RabbitMQ, AMQP (Advanced Message Queuing Protocol) protokolünü temel alan bir mesaj aracısıdır. Erlang diliyle yazılmıştır; bu tercih tesadüf değil, Erlang’ın hata toleransı ve concurrent işlem konusundaki üstünlüğü bilerek kullanılmıştır.
Temel kavramları anlamadan RabbitMQ’yu verimli kullanamassınız:
Producer (Üretici): Mesaj üreten uygulama. Bir sipariş servisi, bir kullanıcı kayıt sistemi, bir log toplayıcı olabilir.
Consumer (Tüketici): Mesajları işleyen uygulama. E-posta gönderen servis, stok güncelleyen servis gibi.
Queue (Kuyruk): Mesajların geçici olarak depolandığı yer. Consumer hazır olana kadar mesajlar burada bekler.
Exchange (Değiştirici): Producer’ın mesajları direkt kuyruğa göndermediği, bunun yerine exchange’e gönderdiği bileşen. Exchange hangi mesajın hangi kuyruğa gideceğine karar verir.
Binding: Exchange ile kuyruk arasındaki ilişki. Routing key ile birlikte mesajların doğru kuyruğa yönlendirilmesini sağlar.
Virtual Host (vHost): RabbitMQ içinde mantıksal izolasyon. Farklı uygulamalar veya ortamlar için ayrı vHost’lar oluşturabilirsiniz.
Exchange Tipleri
Bu kısım çoğu kaynakta yüzeysel geçiliyor ama gerçek hayatta en çok kafayı yaktığım noktalardan biri exchange tiplerini yanlış seçmekti.
Direct Exchange: Mesaj, routing key tam eşleşmesiyle kuyruğa gönderilir. Bire bir yönlendirme için idealdir. “error” routing key’li mesaj sadece “error” binding’li kuyruğa gider.
Fanout Exchange: Routing key’i tamamen görmezden gelir, bağlı tüm kuyruklara mesaj kopyalar. Broadcast senaryoları için mükemmeldir. Bir sipariş oluşturulduğunda bağlı 5 farklı servise aynı anda bildirim gitmesi gibi.
Topic Exchange: Routing key’de wildcard desteği sunar. order.created.tr mesajı order.*.tr veya order.# pattern’larıyla eşleştirilebilir. Esnek yönlendirme gereken sistemlerde vazgeçilmezdir.
Headers Exchange: Routing key yerine mesaj header’larına bakarak yönlendirme yapar. Pratikte çok nadir kullanılır ama bazı kompleks senaryolarda işe yarar.
Kurulum
Ubuntu/Debian Üzerinde Kurulum
# Erlang deposunu ekle
curl -1sLf 'https://dl.cloudsmith.io/public/rabbitmq/rabbitmq-erlang/setup.deb.sh' | sudo -E bash
# RabbitMQ deposunu ekle
curl -1sLf 'https://dl.cloudsmith.io/public/rabbitmq/rabbitmq-server/setup.deb.sh' | sudo -E bash
# Kurulum
sudo apt-get update
sudo apt-get install -y rabbitmq-server
# Servisi başlat ve enable et
sudo systemctl enable rabbitmq-server
sudo systemctl start rabbitmq-server
# Durumu kontrol et
sudo systemctl status rabbitmq-server
Management Plugin Aktifleştirme
# Web arayüzü için management plugin'i aktifleştir
sudo rabbitmq-plugins enable rabbitmq_management
# Yönetici kullanıcı oluştur
sudo rabbitmqctl add_user admin guclu_parola_buraya
sudo rabbitmqctl set_user_tags admin administrator
sudo rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
# Varsayılan guest kullanıcısını sil (güvenlik için kritik!)
sudo rabbitmqctl delete_user guest
Management UI’a http://sunucu-ip:15672 adresinden erişebilirsiniz. Bu arayüz gerçekten çok değerli; kuyruk derinliklerini, mesaj hızlarını, bağlantı sayılarını canlı olarak izleyebilirsiniz.
Docker ile Hızlı Başlangıç
Geliştirme ortamı için Docker kullanmak hayat kurtarır:
docker run -d
--name rabbitmq
--hostname rabbitmq-node1
-p 5672:5672
-p 15672:15672
-e RABBITMQ_DEFAULT_USER=admin
-e RABBITMQ_DEFAULT_PASS=admin123
-v rabbitmq_data:/var/lib/rabbitmq
rabbitmq:3.12-management
İlk Mesajı Gönderme
Kurulum sonrası hemen bir şeyler denemek istiyorsunuz, gayet normal. rabbitmqadmin aracını veya Python ile basit bir örnek yapalım.
Python ile Producer
import pika
import json
from datetime import datetime
# Bağlantı parametreleri
credentials = pika.PlainCredentials('admin', 'guclu_parola_buraya')
parameters = pika.ConnectionParameters(
host='localhost',
port=5672,
virtual_host='/',
credentials=credentials
)
connection = pika.BlockingConnection(parameters)
channel = connection.channel()
# Kuyruk oluştur (idempotent - zaten varsa hata vermez)
channel.queue_declare(
queue='siparis_kuyruğu',
durable=True # Sunucu yeniden başlasa bile kuyruk kaybolmaz
)
# Gönderilecek mesaj
siparis = {
'siparis_id': '12345',
'musteri_id': '789',
'tutar': 1250.00,
'olusturma_zamani': datetime.now().isoformat()
}
# Mesajı gönder
channel.basic_publish(
exchange='', # Default exchange - direkt kuyruğa gönder
routing_key='siparis_kuyruğu',
body=json.dumps(siparis),
properties=pika.BasicProperties(
delivery_mode=2, # Mesajı persistent yap
content_type='application/json'
)
)
print(f"Sipariş gönderildi: {siparis['siparis_id']}")
connection.close()
Python ile Consumer
import pika
import json
def siparis_isle(ch, method, properties, body):
siparis = json.loads(body)
print(f"Sipariş işleniyor: {siparis['siparis_id']}")
# Burada gerçek iş mantığı olurdu
# Örneğin: e-posta gönder, stok güncelle vs.
# İşlem başarılı, RabbitMQ'ya bildir
# Bu satır olmadan mesaj tekrar kuyruğa döner!
ch.basic_ack(delivery_tag=method.delivery_tag)
credentials = pika.PlainCredentials('admin', 'guclu_parola_buraya')
parameters = pika.ConnectionParameters('localhost', credentials=credentials)
connection = pika.BlockingConnection(parameters)
channel = connection.channel()
channel.queue_declare(queue='siparis_kuyruğu', durable=True)
# Bir seferde kaç mesaj alacağını sınırla
# Bu ayar olmadan RabbitMQ tüm mesajları consumer'a yığar
channel.basic_qos(prefetch_count=1)
channel.basic_consume(
queue='siparis_kuyruğu',
on_message_callback=siparis_isle
)
print("Mesaj bekleniyor...")
channel.start_consuming()
Dead Letter Queue Kurulumu
Gerçek üretim sistemlerinde mesajlar bazen işlenemez. Bozuk format, geçersiz veri, bağımlı servisin down olması… Bu mesajları kaybetmek istemezsiniz. Dead Letter Queue (DLQ) tam bu ihtiyaç için vardır.
# RabbitMQ CLI ile dead letter exchange ve kuyruk oluştur
sudo rabbitmqctl eval '
rabbit_channel:do(
fun(Ch) ->
amqp_channel:call(Ch, #'"'"'exchange.declare'"'"'{exchange = <<"dlx_exchange">>, type = <<"direct">>}),
amqp_channel:call(Ch, #'"'"'queue.declare'"'"'{queue = <<"dead_letter_queue">>}),
amqp_channel:call(Ch, #'"'"'queue.bind'"'"'{queue = <<"dead_letter_queue">>, exchange = <<"dlx_exchange">>, routing_key = <<"siparis_kuyruğu">>})
end
).
'
Python ile daha temiz şekilde yapalım:
import pika
credentials = pika.PlainCredentials('admin', 'guclu_parola_buraya')
connection = pika.BlockingConnection(
pika.ConnectionParameters('localhost', credentials=credentials)
)
channel = connection.channel()
# Dead letter exchange oluştur
channel.exchange_declare(
exchange='dlx_exchange',
exchange_type='direct',
durable=True
)
# Dead letter kuyruğu oluştur
channel.queue_declare(
queue='dead_letter_queue',
durable=True
)
# DLQ'yu exchange'e bağla
channel.queue_bind(
queue='dead_letter_queue',
exchange='dlx_exchange',
routing_key='siparis_kuyruğu'
)
# Ana kuyruğu DLX ayarlarıyla oluştur
channel.queue_declare(
queue='siparis_kuyruğu',
durable=True,
arguments={
'x-dead-letter-exchange': 'dlx_exchange',
'x-message-ttl': 3600000 # 1 saat sonra DLQ'ya taşı
}
)
print("Dead letter queue yapılandırması tamamlandı")
connection.close()
Gerçek Dünya Senaryosu: E-posta Bildirim Servisi
Soyut örnekler yeterli, somut bir senaryoya bakalım. Bir e-ticaret platformunda şu akış var:
- Kullanıcı sipariş verir
- Sipariş servisi işlemi tamamlar
- E-posta servisi bildirim gönderir
E-posta servisi zaman zaman yavaş çalışıyor ve bu siparişleri yavaşlatıyor. Çözüm: RabbitMQ ile asenkron hale getir.
# siparis_servisi.py - Producer tarafı
import pika
import json
class SiparisServisi:
def __init__(self):
credentials = pika.PlainCredentials('admin', 'guclu_parola_buraya')
self.connection = pika.BlockingConnection(
pika.ConnectionParameters('localhost', credentials=credentials)
)
self.channel = self.connection.channel()
self.channel.exchange_declare(
exchange='bildirim_exchange',
exchange_type='topic',
durable=True
)
def siparis_olustur(self, musteri_email, tutar):
# Veritabanı işlemleri burada...
siparis_id = "SIP-" + str(hash(musteri_email))[:6]
# Bildirim mesajını kuyruğa gönder
mesaj = {
'tip': 'siparis_onay',
'siparis_id': siparis_id,
'musteri_email': musteri_email,
'tutar': tutar
}
self.channel.basic_publish(
exchange='bildirim_exchange',
routing_key='bildirim.email.siparis',
body=json.dumps(mesaj),
properties=pika.BasicProperties(delivery_mode=2)
)
# Kullanıcıya hemen cevap dön, e-postayı bekleme!
return {'durum': 'basarili', 'siparis_id': siparis_id}
def kapat(self):
self.connection.close()
RabbitMQ İzleme ve Yönetim Komutları
Üretim ortamında sadece kurmak yetmez, izlemek de kritik:
# Tüm kuyrukların durumunu listele
sudo rabbitmqctl list_queues name messages consumers memory
# Belirli bir vHost'taki kuyruklara bak
sudo rabbitmqctl list_queues -p /uretim name messages
# Aktif bağlantıları gör
sudo rabbitmqctl list_connections user peer_host peer_port state
# Consumer bilgilerini al
sudo rabbitmqctl list_consumers
# Kuyruktaki bekleyen mesaj sayısını kontrol et
sudo rabbitmqctl list_queues name messages_ready messages_unacknowledged
# Exchange listesi
sudo rabbitmqctl list_exchanges name type durable
# Binding'leri göster
sudo rabbitmqctl list_bindings
Bir kuyruk aniden büyümeye başladıysa, büyük ihtimalle consumer’ınız sorun yaşıyordur. messages_unacknowledged değeri yüksekse consumer aldığı mesajları işleyip acknowledge etmiyor demektir; bu ciddi bir uyarı işaretidir.
Yaygın Hatalar ve Çözümleri
Yıllar içinde hem kendim hem de beraber çalıştığım ekipler belirli hataları tekrarlıyor. Bunları baştan bilmek çok zaman kazandırır.
Durable ve Persistent’ı karıştırmak: Kuyruğu durable=True yapmak sunucu yeniden başladığında kuyruğun kaybolmamasını sağlar, ama mesajların kaybolmamasını değil. Mesajların da delivery_mode=2 ile persistent olması gerekir. İkisini birden ayarlamayı unutmayın.
Prefetch count’u ayarlamamak: Varsayılan olarak RabbitMQ consumer’a ne kadar mesaj varsa o kadar gönderir. Eğer consumer bir mesajı işlemekte zorlanıyorsa ve 1000 mesaj bellekte bekliyorsa, sisteminiz çöke bilir. basic_qos(prefetch_count=1) ile başlayıp sisteminize göre optimize edin.
Acknowledge etmeyi unutmak: Consumer mesajı aldı ama işleyip basic_ack çağırmayı unuttu. Mesaj unacknowledged durumunda kalır. Consumer bağlantısı kapandığında mesaj kuyruğa geri döner. Bu aslında iyi bir davranış, ama kasıtsız yapılırsa döngüye girersiniz.
Connection ve channel’ı karıştırmak: Her thread için ayrı channel açın, ama connection’ı paylaşabilirsiniz. Connection’ı her mesajda açıp kapatmak ciddi performans sorununa yol açar.
Sonuç
RabbitMQ, doğru anlaşıldığında ve doğru yapılandırıldığında dağıtık sistemlerde gerçekten güçlü bir araç haline gelir. Burada anlattıklarım teorinin ve pratiğin kesişim noktası; ancak her sistemin kendine özgü yükü, iş mantığı ve hata toleransı gereksinimleri var.
Başlamak için en sağlıklı yol Docker ile lokal bir kurulum yapıp önce tek bir basit akışı mesaj kuyruğuna taşımak. Herşeyi bir anda refactor etmeye çalışmayın. Önce en kritik, en yavaş veya en kırılgan noktayı belirleyin, oradan başlayın.
Management UI’ı düzenli kontrol etmeyi alışkanlık haline getirin. Kuyruk derinlikleri, mesaj hızları ve consumer sayıları size sistemin sağlığı hakkında çok erken uyarı verir. Bir alarm kurmadıysanız, messages_ready değeri belirli bir eşiği geçtiğinde uyarı alacak şekilde Prometheus/Grafana entegrasyonunu da planlamanızı öneririm. Ama o başka bir yazının konusu.
