Hayat Kurtaran RabbitMQ

Adil Deveci
Doğuş Teknoloji
Published in
6 min readSep 30, 2021

Tavuk mu yumurtadan, yumurta mı tavuktan çıkar bilmem ama RabbitMQ’da ilk giren ilk çıkar :) Sizlere RabbitMQ kullandığımız çözümde başımıza gelen bir sorunu ve hangi stratejiyi izleyerek bu problemin üstesinden geldiğimizi anlatmak istiyorum. Dilerseniz öncesinde RabbitMQ nedir, yenir mi içilir mi bir bakalım.

Açık kaynak dünyasının en popüler mesajlaşma sistemlerinden birisi olan RabbitMQ, Earlang dili ile 2007 yılında LShift ve CohesiveFT arasında bir ortak girişim olarak başlayan Rabbit Technologies Ltd. tarafından geliştirilmiştir. Kullanım amacı temelde bir kaynaktan alınan mesajın sırası geldiğinde başka bir kaynağa aktarılmasını sağlamaktır.

RabbitMq temel çalışma mantığı

AMQP (Advanced Messaging Queue Protocol)

RabbitMQ, AMQP protokolünü destekleyen bir platformudur. AMQP; Gelişmiş bir mesajlaşma protokolüdür. Platform bağımsız olarak uygulamaların kuyruk modeline göre birbiriyle haberleşmesini sağlamaktadır. AMQP destekli bir sunucunuz varsa sizi dinleyen istemcilerin(.net, java, python, nodejs vb) tamamı ile haberleşme sağlayabilirsiniz.

AMQP, uygulamalar için yararlı olan bazı temel özellikler sağlar. Hızlı ve garantili mesaj teslimatının yanı sıra güvenilirlik ve onay mekanizması protokolün temel özelliklerindendir. Bu kabiliyetler çok istemcili bir ortamda mesajların dağıtılmasına, zaman alan görevlerin devredilmesine ve bir sunucunun acil istekleri daha hızlı çözmesine yardımcı olur.

Bilinmesi Gereken Temel Terimler

İlerlemeden önce bilinmesi gereken bazı terimlerden de bahsedelim.

  • Publisher-Producer: Kuyruğa mesajı gönderen kaynak yani uygulamadır.
  • Consumer: Kuyruğu dinleyen ve beklediği mesajları alıp kullanan uygulamadır.
  • Queue : Gelen mesajların hedefe ulaştırılmadan önce girdiği kuyruktur.
  • Exchange: Gelen mesajı alan ve seçilen yönlendirme stratejisine göre ilgili kuyruğa gönderen mekanizmadır. Kullanım amacına göre farklı exchange tipleri vardır.
  • Routing key: Gelen mesajın hangi kuyruğa gideceğinin belirlenmesinde kullanılır.
  • Fifo: Kuyruğa gelen mesajların işlem sırasını ifade ederi. First in first out anlamındadır, yani kuyruktan ilk çıkan mesaj ilk giren mesajdır.

Mesajlar varsayılan olarak bellek üzerinde tutulur ancak bu seçenecek değiştirilerek disk üzerine yazılması da sağlanabilir.

Mesajı gönderen eden uygulama ile kuyruktaki mesajları dinleyen uygulama aynı da olabilir, ayrı ayrı 2 farklı uygulama da olabilir. RabbitMQ dağıtık sistemler ile çalışma imkanı sağladığı için kuyruğu dinleyecek uygulama sayısı birden fazla olabilir.

RabbitMq consumer
Yaptığımız çalışmada consume eden servis 2 pod olarak hizmet vermekte

Mesajların başarılı şekilde işlenememesi durumunda ilgili mesajları tekrar kuyruğa almak için bir retry mekanizması kurulmalıdır ki yazının sonunda bunun bir örneğini bulabilirsiniz.

RabbitMQ’yu sisteminize doğrudan kurup deneyebilir ya da hiç uğraşmadan docker imajı ile çalışabilirsiniz. Eğer doğrudan kurulum yapacaksanız sisteminizde Erlang dili ile ilgili bağımlılıkların da yüklenmiş olması gerekmektedir. Kurulum ile alakalı daha fazla bilgi için burayı ziyaret edebilirsiniz.

# for RabbitMQ 3.9, the latest series
docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.9-management

RabbitMQ’ya alternatifi olarak Apache Kafka, gRPC, ActiveMQ, Redis vb. bir çok araç mevcuttur ancak yazımızın amacı dışında kaldıklarından onlara şimdilik değinmeyeceğiz.

Exchange Çeşitleri

RabbitMQ gelen mesajların yönlendirilmesi ile alakalı olarak bazı değiş tokuş stratejileri kullanır. Bu stratejiler Exchange enstrümanı tarafından yönetilir. Gelin bu stratejilerin neler olduğuna kısaca bakalım.

Direct Exchange

Direct Exchange kendisine bağlı kuyruk/kuyruklara doğrudan belirtilen routing key değeri ile ulaşır. Routing key değeri mesaj yayıncısı(publisher) tarafından net olarak belirtilmelidir.

Örnek olarak bir pdf dönüştürme sistemi düşünelim, içerisine word ve html türünde dosya alacak ve bunları pdf olarak kaydedecek. Bu işlemi 1 kuyruk kullanarak da yapabiliriz, 2 kuyruk kullanarak da yapabiliriz. Mesela ben html to pdf işlemleri Rotativa ile yapıyorum, word to pdf işlemlerini Aspose ile yapıyorum. Bu senaryo için 2 ayrı kuyruk olacak şekilde direkt exchange kullanmak mantıklı olacaktır.

RabbitMq — Direct Exchange
RabbitMQ — Direct Exchange örneği

Header Exchange

Bu yapıda Routing-Key yerine mesajın başlık bilgisi(header) içindeki key’lerden yararlanılarak doğru kuyruğa iletilmesi sağlanır. Buna karar verirken publisher başlık içine x-match isminde bir değer koyar.

  • x-match: All varsayılan değerdir, bu kullanımda tüm key-value’lar kuyruktakiler ile eşleşmelidir.
  • x-match: Any başlıktaki herhangi bir key-value kuyruktaki ile eşlesmesi yeterli

Örnek olarak bir bildirim sisteminde uygulanabilir, gelen mesajın başlığında bildirimin hangi kanallardan yapılması gerektiği yönetilebilir.

RabbitMq — Header Exchange örneği

Fanout Exchange

Gelen mesaj Exchange’e bağlı tüm kuyruklara iletilir. Bu teknik daha çok broadcast formatlı yayınlar için uygundur. Örnek bir senaryo düşünelim; Haber paylaşımı yapan bir sisteminiz var diyelim, yeni bir içerik yazıldığında bağlantılı olduğunuz tüm kanallara bu haberi göndermek istiyorsunuz, bu ve benzeri senaryolar için Fanout Exchange kullanılabilir.

RabbitMq — Fanout Exchange

Topic Exchange

Gelen mesajın Routing-Key değeri nokta operatörü kullanılarak formatlanır ve bu formattaki isimlerde yapılan filtrelemelere uygun düşen kuyruklara gönderimi sağlanır. Routing-Key pattern yapısı .# ve .* ile tanımlanır ya da direkt olarak routing-key isimlendirmesi yapılabilir.

.* için noktadan sonra bir belirteç geleceği garanti edilir.

.# için noktadan sonra herhangi bir şey gelebilir veya gelmeyebilir.

NOT: Topic Exchange için herhangi bir route key pattern belirtilmez ise Fanout Exchange gibi çalışır, exchange bağlı tüm kuyruklara mesaj taşınır. Yine pattern kullanılmadan net bir route key verilerek kullanılır ise Direct Exchange gibi çalışmaktadır.

RabbitMQ — Topic Exchange örneği

Örnek Vaka Çalışması

Biz şirket içi dolaşımdaki dijital dokümanların maskelenmesi ile ilgili bir süreçte RabbitMQ’dan yararlandık. Gelin bunu nasıl yaptığımızdan bahsedeyim…

Gelen dokümanları alıp öncelikle bellek üzerinde konumlandırdığımız bir kuyruğa soktuk ve sırası geldikçe bunları maskelemek üzere başka bir servise yönlendirdik. Direct Exchange yapısını kullandık. Buraya kadar her şey basit, gelen dokümanı kuyruğa sok ve sırası geldiğinde ilet.

Sistem ilk etapta gayet düzgün işledi ancak bir zaman sonra bazı dokümanların maskelenmediğini gördük. İşi yapan servis zaman zaman çöküyor ve mesaj kuyrukta işlenmeden kalıp ömrünü tamamlıyordu. Bunun üzerine işlenmeyen dokümanların yeniden maskelenmesini sağlamak için bir tekrar mekanizması(retry) uygulamaya karar verdik.

Başarılı işlenemeyen dosyalar için ayrı bir kuyruk açtık ve ilgili dosyaları buraya yönlendirdik. Bu gibi olumsuz durumlarda Publisher sürekli mesaj gönderecek fakat bu mesajlar Consumer tarafından tüketilemeyeceği için çok fazla mesaj yığılması yaşanacak ve doğal olarak ciddi bir kaynak sorunu oluşacaktır. Bu nedenle biz ana kuyruğumuzu bellekte tutarken hata alanlar için yaptığımız kuyruğu fiziki diski kullanması için ayarlamalar yaptık. Aşağıdaki grafikte bu tasarımı görebilirsiniz.

RabbitMq retry mekanizması

Başarısızlar kuyruğunda bekleyen dosyaların tekrar ana kuyruğa alınıp aynı sürece sokulması için ise exchange mekanizmasının TTL(Time To Live) özelliğinden faydalandık. Belirlediğimiz süreye göre bir süre başarısızlar kuyruğunda bekleyen dosyalarımızın tekrar işleme alınmasını sağladık.

Nihayetinde yeni strateji taktiğimiz epey işe yaradı. Bir gün dosya karartma işlemini yapan servis kendi içindeki veritabanı problemleri nedeni ile birkaç gün hizmet veremedi. Bu süre zarfında sistemimizde neredeyse beş yüzden fazla işlenmemiş dosya birikti. Eğer mesajları bellekte tutsaydık çok kısa sürede sistemin çökmesi kaçınılmazdı ama yığılan mesajlar için diski kullanmak bizi kurtardı. İlgili servis çalışır duruma geldiğinde tüm mesajlar başarılı şekilde işlenerek veri kaybı olmadan durumu atlatmış olduk :)

Böylece yazımızın sonuna geldik. Mutlaka birilerine faydasının dokunacağını düşünüyorum hoşça kalın :)

--

--