Merhaba,
Günümüzde uygulamalar artık tek bir proje olarak çalışmıyor. Bir kullanıcı Sipariş Ver butonuna bastığında arka planda birden fazla servis devreye giriyor. Ödeme işlemi başlatılıyor, stok güncelleniyor, fatura oluşturuluyor ve kullanıcıya e-posta gönderiliyor. Peki tüm bu işlemler aynı anda ve birbirini beklemeden nasıl gerçekleşiyor? İşte bu noktada mesajlaşma sistemleri devreye giriyor. Bugün sizlerle RabbitMQ’nun ne olduğunu, nasıl kurulduğunu ve .NET Core ile gerçek dünya senaryosunda nasıl kullanıldığını inceleyeceğiz.
RabbitMQ Nedir?
RabbitMQ, açık kaynaklı bir mesaj kuyruğu sistemidir. Özellikle dağıtık sistemlerde ve mikroservis mimarilerinde, servisler arası iletişimi asenkron ve güvenli şekilde gerçekleştirmek için kullanılır. Günümüzde uygulamalar tek bir yapıdan oluşmadığı için, servislerin birbirinden bağımsız ama koordineli çalışması büyük önem taşır. RabbitMQ tam olarak bu ihtiyaca çözüm sunar.
Temel çalışma mantığında bir uygulama mesaj üretir ve bu mesajı RabbitMQ’ya iletir. Mesaj, ilgili kuyruğa yönlendirilir ve başka bir uygulama tarafından uygun zamanda alınarak işlenir. Bu yaklaşım sayesinde servisler birbirini beklemek zorunda kalmaz; böylece sistem daha esnek, daha dayanıklı ve daha ölçeklenebilir hale gelir.
RabbitMQ mesajlaşma altyapısı olarak çoğunlukla AMQP (Advanced Message Queuing Protocol) protokolünü kullanır. AMQP, mesajların güvenilir bir şekilde iletilmesini, doğru kuyruklara yönlendirilmesini ve sistemler arasında standart bir iletişim kurulmasını sağlayan bir protokoldür.
Neden RabbitMQ Kullanılır?
Modern yazılım mimarilerinde, özellikle mikroservis yaklaşımının benimsendiği sistemlerde servislerin birbirine doğrudan bağımlı olması ciddi problemlere yol açabilir. Bir servisin yavaşlaması ya da geçici olarak erişilemez olması, ona bağlı diğer servisleri de etkileyerek zincirleme hatalara neden olabilir. Bu nedenle servisler arası iletişimin daha esnek ve dayanıklı bir yapı üzerinden yürütülmesi gerekir.
İşte bu noktada RabbitMQ devreye girer. RabbitMQ, servisler arasında gevşek bağlılık sağlar. Üretici servis yalnızca mesajını kuyruğa bırakır ve işine devam eder; tüketici servis ise uygun zamanda bu mesajı alarak işler. Bu yaklaşım sistemin asenkron çalışmasını mümkün kılar ve performans açısından büyük avantaj sağlar.
Ayrıca RabbitMQ, yoğun anlarda gelen istekleri kuyrukta tutarak yük dengeleme sağlar. Ani trafik artışlarında sistemin çökmesini engeller ve mesajların güvenli bir şekilde saklanmasına yardımcı olur. Eğer tüketici servis geçici olarak çalışmıyorsa mesajlar kaybolmaz; servis tekrar ayağa kalktığında işlenmeye devam eder. Bu da hata toleransını artırır ve sistemi daha dayanıklı hale getirir.
Örneğin bir e-ticaret sisteminde sipariş oluşturulduktan sonra e-posta gönderilmesi, fatura kesilmesi, stok güncellenmesi ve kullanıcıya bildirim iletilmesi gibi birçok işlem tetiklenir. Bu servisleri doğrudan HTTP çağrılarıyla birbirine bağlamak yerine RabbitMQ üzerinden bir mesaj yayınlamak çok daha sağlıklı bir mimari oluşturur. Sipariş servisi yalnızca OrderCreated mesajını yayınlar ve diğer servisler bu olaya abone olarak kendi işlemlerini bağımsız şekilde gerçekleştirir. Böylece sistem hem ölçeklenebilir hem de daha sürdürülebilir bir yapıya kavuşur.
RabbitMQ Mimarisi
RabbitMQ mimarisi, mesajların üreticiden tüketiciye güvenli ve kontrollü şekilde iletilmesini sağlayan birkaç temel bileşen üzerine kuruludur. Bu yapı ilk bakışta basit görünse de, doğru kurgulandığında oldukça esnek ve güçlü bir mesajlaşma altyapısı sunar.
Süreç her zaman bir üretici (producer) ile başlar. Producer, oluşturduğu mesajı doğrudan kuyruğa göndermez; bunun yerine mesajı önce bir exchange’e iletir. Exchange, sistemin yönlendirme mekanizmasıdır. Mesajın hangi kuyruğa ya da kuyruklara gideceğini belirleyen katman burasıdır. Bu sayede üretici, mesajın tam olarak hangi servis tarafından işleneceğini bilmek zorunda kalmaz.
Exchange’in davranışı türüne göre değişir. Direct exchange, mesajı belirli bir routing key ile eşleşen kuyruğa iletir. Fanout exchange, gelen mesajı bağlı tüm kuyruklara dağıtır ve genellikle broadcast senaryolarında tercih edilir. Topic exchange, routing key üzerinde desen eşleştirmesi yaparak daha esnek bir yönlendirme sağlar ve mikroservis mimarilerinde sıkça kullanılır. Headers exchange ise yönlendirme kararını mesaj başlıklarına göre verir.
Exchange tarafından yönlendirilen mesajlar kuyrukta (queue) tutulur. Kuyruk, mesajların tüketici tarafından işlenene kadar saklandığı yapıdır. Eğer tüketici servis geçici olarak çalışmıyorsa mesajlar kaybolmaz; sistem yeniden ayağa kalktığında işlenmeye devam eder. Bu durum dağıtık sistemlerde dayanıklılık açısından kritik öneme sahiptir.
Son aşamada consumer devreye girer. Consumer, ilgili kuyruğu dinler ve gelen mesajları alarak işleme sürecini başlatır. Mesaj başarıyla işlendiğinde onay (acknowledgement) mekanizması devreye girer ve mesaj kuyruktan silinir. Böylece veri kaybı ve çift işleme gibi problemler kontrol altına alınmış olur. Bu mimari sayesinde RabbitMQ, üretici ile tüketici arasındaki doğrudan bağı koparır ve araya akıllı bir yönlendirme katmanı koyarak sistemi hem esnek hem de ölçeklenebilir hale getirir.
Windows Üzerine RabbitMQ Kurulumu
RabbitMQ’yu Windows ortamında kurmak oldukça basittir. Ancak RabbitMQ, Erlang dili ile geliştirildiği için çalışabilmesi adına sistemde öncelikle Erlang kurulu olmalıdır. Kurulum iki aşamadan oluşur.
Erlang Kurulumu
İlk olarak Erlang’ın Windows installer dosyasını indirip standart kurulum adımlarını tamamlamanız gerekir
RabbitMQ Kurulumu
Erlang kurulduktan sonra Windows için RabbitMQ installer dosyasını indirerek kurulumu başlatabilirsiniz. Kurulum tamamlandıktan sonra RabbitMQ servisi Windows servisleri arasında otomatik olarak çalışır duruma gelecektir. Yönetim arayüzünü kullanabilmek için management plugin’ini aktif etmemiz gerekir. CMD veya PowerShell’i yönetici olarak açıp aşağıdaki komutu çalıştırmanız yeterlidir:
rabbitmq-plugins enable rabbitmq_management
Bu işlem sonrasında RabbitMQ’nun web tabanlı yönetim paneli aktif hale gelir.
Management Paneline Erişim
Tarayıcı üzerinden aşağıdaki adrese giderek yönetim paneline erişebilirsiniz:
Varsayılan giriş bilgileri:
-
Kullanıcı adı: guest
-
Şifre: guest
Başarılı giriş yaptıktan sonra kuyrukları, exchange yapılarını ve bağlantıları görsel arayüz üzerinden yönetebilirsiniz.
Gerçek Dünya Senaryosu
Sipariş Oluşturma ve Stok Düşme
Senaryomuz şu şekilde: Bir kullanıcı sipariş oluşturduğunda, Order servisi bir OrderCreated event’i yayınlayacak. Inventory servisi bu mesajı dinleyerek ilgili ürünlerin stoklarını düşecek. Burada önemli nokta şudur ki: Order servisi, Inventory servisini doğrudan çağırmaz. HTTP isteği atmaz. Sadece bir mesaj yayınlar. Böylece servisler birbirinden tamamen bağımsız çalışır.
Mimarinin Genel Akışı
Gerçek bir mikroservis senaryosu düşünelim. Kullanıcı bir sipariş oluşturduğunda Order servisi siparişi kaydeder ve bir OrderCreated event’i yayınlar. Burada kritik nokta, Order servisinin Inventory servisini doğrudan çağırmamasıdır. Herhangi bir HTTP isteği atmaz; sadece bir mesaj üretir. Inventory servisi bu olayı dinler ve siparişe ait ürünlerin stoklarını düşer. Böylece servisler birbirinden bağımsız çalışır. Sonuç olarak sistem asenkron ilerler ve servisler arasında gevşek bağlı bir yapı oluşur.
Öncelikle boş bir solution oluşturarak başlıyoruz. Ben solution adı olarak RabbitMQEcommerce ismini verdim. Şu anda elimizde tamamen boş bir yapı var ve bu yapıyı adım adım inşa edeceğiz. Bu solution içerisine yeni bir Web API projesi ekliyoruz. Proje sürümü olarak .NET 10’u seçelim ve proje adını RabbitMQEcommerce.OrderService olarak belirliyoruz. Böylece sipariş işlemlerini yönetecek olan servisimizin temelini atmış oluyoruz.
Proje oluşturulduktan sonra sıradaki adım mesajlaşma altyapısını eklemek. Bunun için projeye RabbitMQ.Client paketini dahil ediyoruz. Bu paket sayesinde uygulamamız, mesaj broker olarak kullanacağımız RabbitMQ ile iletişim kurabilecek ve event yayınlama işlemlerini gerçekleştirebilecek hale geliyor.
Event Tasarımında Veri Güvenliği ve Hassasiyet
Mesaj kuyruğuna bir veri attığınızda, bu veri artık ağda dolaşan ve belki de broker üzerinde saklanan bir kopyadır. Bu nedenle her veriyi event içine dahil etmemelisiniz. Event-driven mimarilerde en kritik konulardan biri mesajın içeriğidir; çünkü servisler arası iletişim tamamen bu event’ler üzerinden gerçekleşir.
Mesajlar mümkün olduğunca sade ve amacına uygun tasarlanmalıdır. Unutmayın ki bir event sadece bir veri taşıma aracı değil, sistemde gerçekleşmiş bir olayı temsil eden bir bildiridir. Örneğin; OrderCreated event’i siparişin oluştuğunu ifade eder. Siparişle ilgili tüm gereksiz domain bilgilerini veya diğer servisleri ilgilendirmeyen detayları taşımak doğru bir yaklaşım değildir.
Güvenlik ve Performans Kuralları:
-
Hassas Bilgiler: Kredi kartı numarası, parola veya kişisel kimlik bilgileri gibi riskli veriler asla event içine konulmamalıdır.
-
Büyük Veriler: Dosyalar, görseller veya büyük veri blokları yerine bu verilerin referansları (bir ID veya URL gibi) taşınmalıdır.
Doğru tasarlanmış mesaj yapıları, mikroservis mimarisinde gevşek bağlılığı (loose coupling) korur. Bu noktada karşımıza iki temel yaklaşım çıkar:
-
Fat Event (Şişkin Mesaj): Servislerin işini yapabilmesi için gereken tüm detayların (Ürün listesi, adres, toplam tutar vb.) mesajın içinde yer almasıdır. Bu yöntem servisleri birbirine bağımsız kılar; ancak mesaj boyutu büyür ve veri güvenliği riskleri artar.
-
Thin Event (Zayıf Mesaj): Mesajın içine sadece bir olayın gerçekleştiği bilgisini ve o olayın benzersiz kimliğini (ID) koymaktır. Örneğin; "123 nolu sipariş oluşturuldu" bilgisi bir Thin Event'tir. Mesajı alan servis detaylara ihtiyaç duyuyorsa, asıl servisin API'sine güvenli bir kanal üzerinden giderek veriyi talep eder.
Not: Thin Event yaklaşımında her mesaj geldiğinde servislerin birbirine HTTP isteği atması, ana servisi yorabilir. Eğer saniyede binlerce sipariş geliyorsa, ana servis bu detay talebi isteklerine cevap yetiştiremeyebilir. Projenizin güvenlik gereksinimlerine ve trafik yoğunluğuna göre bu iki yaklaşımdan size en uygun olanı seçmelisiniz.
OrderCreated Event Modeli
Sipariş oluşturulduğunda yayınlayacağımız mesajı temsil edecek olan OrderCreated event modelini tanımlıyoruz. Gerçek hayata daha uygun olması için siparişin yalnızca tek bir üründen değil, birden fazla üründen oluşabileceğini varsayıyoruz. Bu nedenle event modeli, ürün listesini de içerecek şekilde tasarlanıyor.
Order servisi yalnızca mesaj yayınlar. Stok düşme işlemini kimin yapacağını bilmez, Inventory servisini çağırmaz ve ondan bir yanıt beklemez. Sipariş oluşturulduğu anda event’i publish eder ve kendi işini tamamlar. Mesaj, arka planda RabbitMQ üzerinden ilgili servislere iletilir. Bu sayede sistem asenkron çalışır. Servisler birbirine bağımlı değildir ve biri yavaşladığında ya da geçici olarak devre dışı kaldığında diğerini doğrudan etkilemez. İşte event-driven mimarinin en güçlü tarafı tam olarak budur.
Inventory Service
Uygulama ayağa kalktığında background servis otomatik olarak devreye girer ve RabbitMQ üzerindeki inventory-queue kuyruğunu dinlemeye başlar. Böylece sistemin iki ucu da aktif hale gelir. Order servisi mesajı yayınlar, Inventory servisi arka planda bu mesajı yakalar ve stok düşme sürecini başlatır. İki servis birbirini doğrudan çağırmaz; aralarındaki tek iletişim noktası mesajdır. Bu da yapıyı doğal olarak asenkron ve gevşek bağlı hale getirir.
Bu süreçte aslında şunlar gerçekleşir: Inventory servisi order-exchange exchange’ine bağlanır ve özellikle order.created routing key’i ile gelen mesajları dinler. Mesaj kuyruğa düştüğünde içerik deserialize edilerek nesneye dönüştürülür. Sipariş içindeki ürünler tek tek işlenir ve stok düşme işlemi simüle edilir. İşlem başarıyla tamamlandığında BasicAck gönderilerek mesajın işlendiği onaylanır. Eğer bu onay gönderilmezse mesaj kuyrukta kalmaya devam eder. Bu mekanizma, sistemin güvenilir çalışmasını ve hata durumlarında mesajın kaybolmamasını sağlar.
Bu mimarinin bize kazandırdığı en önemli avantaj, servisler arası bağımlılığın ortadan kalkmasıdır. Order servisi, Inventory servisini bilmez ve onun durumuna göre hareket etmez. Inventory servisi geçici olarak kapalı olsa bile mesaj broker mesajı saklar ve servis yeniden ayağa kalktığında işlem kaldığı yerden devam eder. Trafik arttığında ise Inventory servisini yatayda çoğaltarak aynı kuyruğu birden fazla instance ile dinletebilir ve yükü dağıtabiliriz.
Sonuç olarak sistem daha dayanıklı, ölçeklenebilir ve sürdürülebilir hale gelir. Bu yaklaşım, gerçek anlamda event-driven çalışan bir mikroservis mimarisinin temelini oluşturur.
Sonuç
Bu yazıda RabbitMQ’nun ne olduğunu, nasıl konumlandığını ve .NET Core ile gerçek bir mikroservis senaryosunda nasıl kullanılabileceğini adım adım gördük. Order servisi yalnızca bir mesaj yayınladı; Inventory servisi ise bu mesajı dinleyerek stok düşme işlemini gerçekleştirdi. İki servis de birbirini doğrudan çağırmadan, hatta birbirinin varlığından haberdar olmadan çalıştı.
Bu yaklaşım sayesinde servisler gevşek bağlı hale gelir ve sistem doğal olarak asenkron çalışır. Trafik artışlarında ilgili servisi yatayda çoğaltarak sistemi daha kolay ölçekleyebiliriz. Ayrıca geçici hatalarda mesajlar kuyrukta tutulduğu için veri kaybı yaşanmaz ve işlem güvenliği korunur.
Kısacası RabbitMQ, özellikle mikroservis mimarilerinde dayanıklı, ölçeklenebilir ve sürdürülebilir sistemler kurmak için oldukça güçlü bir çözümdür.
Diğer Bloglarımda Görüşmek Üzere 👋