Kıdemli Tavşan — RabbitMQ

Cihat Solak
lTunes Tribe
Published in
9 min readMar 24, 2021
Bugs Bunny ❤ RabbitMQ

62 den yaptığımız tavşan 🐇 da güzel ve tatlıydı ama üzerinde konuşacağımız tavşan biraz daha bilgili bir abimiz/ablamız. Konuya başlamadan resmî dokümanı tarayıcı üzerinde yeni sekmede açar mısın? İçerik sonunda bence ihtiyacın olacak, hazırda bulunsun. 👍

Nedir?

Açık kaynak olan RabbitMQ, mesaj kuyruk sistemidir. Bir mesaj gelir, zamanı geldiğinde de iletilir.

Şimdi bu terimsel anlam karmaşasını bir kenara bırakalım. Sinemaya 🎞️ gideceksin, haliyle bilete ihtiyacın var ve bilet kuyruğunun en arkasına geçtin Sıranın sana gelmesini bekliyorsun. Hah! İşte RabbitMQ da da mantık budur. Anlaşılacağı üzere işlemler sıraya alınmaktadır.

İlk giren ilk çıkar (First in first out — FIFO)

Bir diğer bakış açısıyla, uygulamadan mesajı alır ve sırası geldiğinde iletir. Bir örnek daha nasıl olur? Bunu aslında kargo firması 📦 gibi de düşünebilirsin. Kargo firmasına bir kargo gelir ve gelen kargonun vakti geldiğinde dağıtıma çıkarılır. Mantık tamamen benzemektedir. Kargo içerisinde ki süreçlerin tamamından da RabbitMQ sorumludur.

RabbitMQ — Advanced Message Queuing Protocol (AMQP)

AMQP nedir arkadaş? Deme, AMQP mesajlaşma 📨 protokolüdür. Ayrıca platform bağımsız! (hadi yine iyisin).. Bu işi yaparken de gönderdiği ya da göndereceği mesajlarda “header, properties ve message” alanlarına sahiptir.

Aslında bu dizinin esas oğlan ve esas kızı producer ve consumer’dır. Exchange ve Queue ise yan karakterleri diyebiliriz. Figüran demiyorum yan karakter diyorum altını çizerim.

Sözlük 🕮

Message Broker: Mesaj kuyruk sistemlerine verilen genel isimdir. RabbitMQ dışında Azure Queue Storage, Azure Service Bus, Kafka, Msmq gibi farklı mesaj kuyruk sistemleri de vardır.

Producer/Publisher: Farklı uygulamalarda ya da yazılarda bu arkadaşa publisher’da denildiğini görebilirsiniz. Producer herhangi bir uygulama olabilir.(Node.js, Java vb.) Kuyruğa yada exchange’e mesaj göndericidir. (Publisher mesajı ister direk kuyruğa isterse kuyruk yerine exchange’e de gönderebilir.)

Exchange: Mesajı karşılar ve varsa routeKey değerine göre ilgili kuyruklara yönlendirir. Birkaç farklı tipi(Fanout, Direct, Topic ve Header) mevcuttur.

Queue: Gönderilen mesajların RabbitMQ tarafında sıraya alındığı ya da tüketici tarafına iletemediği durumlarda depolandığı yer. Bellekte depolanır, sunucu yeniden başlatılırsa haliyle kuyruktaki mesajlar silinir ama endişe etme onun ayarı(durable: true or false) var aşağılarda anlatacağım.

Consumer/Subscriber: Tüketici ya da dinleyici olarak adlandırabiliriz. Kuyruktaki mesajları alan, kullanan ya da tüketendir. Consumerda publisher gibi herhangi bir uygulama olabilir. Consumerlar exchangeleri tanımazlar, kuyrukları tanırlar ve mutlaka bir kuyruğa bağlanırlar. Kuyruğu dinleyen birden fazla consumer da olabilir.

Neden Tercih Edilir? 🤔

RabbitMQ

RabbitMQ çoklu işletim sistemine destek veriyor olması ve açık kaynak kodlu olması onu vazgeçilmez kılan özelliklerinden birkaçı. Peki gerçekten yaptığın her işlemin anlık gerçekleşmesi gerekiyor mu? Word dosyasını pdf’e çevirip müşteriye e-posta atan bir servis adresi olduğunu düşünürsek?

Kullanıcıdan gelen bir isteği anlık ⌛ olarak cevap veremiyorsan ya da anlık olmasa da zaman alan işlerin olabilir. Bu kısımda kullanıcıyı oyalamak yerine bu süreci asenkron bir şekilde halledip, yoğunluğu düşürmen kullanıcının da projenin de sağlığı açısından iyi olur. Peki, aksi takdirde ne olur

loading… loading.. bekle… bekle.. az kaldı… ha bitti… ha bitcek.. bitiyor gibi… sabret… Time Out Exception

Tam da yukarıdaki gibi olur. Response time süresine maruz bırakmanın günahı çok büyük çok, ödeyemezsin. 😇 ALO RabbitMQ hattına bağlan, uzun soluklu işlemlerini kuyruğa gönder ve bu sorumluluktan kurtul. Yapacağın işlem ana uygulamadan farklı bir yerde yaşamına devam edeceği için(asenkron) yoğunluğu minimize etmiş olacaksın, bu da response time’ı canavara dönüştürecek.

Niçin Kullanmalıyım?

  • Request/response süresini azaltmak
  • Zaman alan işlemleri RabbitMQ ile farklı bir process’de gerçekleştirebilmek
  • Microservice mimarilerde asenkron iletişim sağlamak

Örneğin web uygulamanızda veri tabanındaki belirli kriterlere göre verileri listeleyip, excel formatına çeviren bir action metotunuzun olduğu düşünürsek bu ciddi zaman alan bir iş olabilir. Burada kullanıcıyı bekletmek yerine RabbitMQ ile ayrı bir process de işlemi tamamlayıp, kullanıcıya bildirmek iyi bir seçenek olabilir.

Nasıl Kurulur? 💻

İster Windows işletim sistemine, istersen Cloud ☁️ ortama kurabilirsin. Tarafını sen seç.

Windows İşletim Sistemine Kurulum

RabbitMQ, Erlang dili ile yazılmıştır ve çalışma ortamında bu dile ihtiyaç duymaktadır. Kurulumun ilk adımında erlang’ı kuracağız.

4.Madde — RabbitMQ Management
  1. https://www.erlang.org” ve “https://www.rabbitmq.com” adreslerinden .exe dosyalarını indiriyoruz.
  2. İlk olarak erlang kurumunu yapıyoruz ve sonrasında rabbitmq kurulumunu yapıyoruz.(Next, next, next…)
  3. Kurulum sonrasında RabbitMQ Windows servisinin kurulumunun başarılı olup olmadığına “services.msc” adresinden kontrol ediyoruz. Çalışmıyorsa start ediyoruz.
  4. Tarayıcı üzerinden yönetim paneline erişmek için komut penceresinden rabbitmq nun kurulu olduğu dosya dizinininden “sbin” klasörü içerisinde komut penceresini açıyoruz ve “rabbimq-plugins enable rabbitmq_management” komutu çalıştırıyoruz.
  5. Komut çalıştırıldıktan sonra rabbitmq paneline “http://localhost:15672/” adresinden erişebilirsiniz. Kullanıcı adı ve parola varsayılan olarak “guest” gelmektedir.

Cloud Ortama Kurulum

CloudAMQP Kontrol Sayfası
  1. https://www.cloudamqp.com adresine gidiyoruz, kayıt oluyoruz ardından giriş yapıyoruz.
  2. “Create New Instance” diyip gerekli bilgileri doldurduktan sonra kendimize bir instance oluşturuyoruz. Artık hazırız!
RabbitMQ Yönetim Ekranı

Work Queues 🗄️

Kuyruk oluşturma işlemi senaryoya göre publisher/producer ya da consumer/subscriber tarafında oluşturulabilir. Eğer mesaj gönderimi esnasında herhangi bir kuyruk RabbitMQ’da yoksa gönderilen her mesaj havada kalır. Örneğin publisher 10 adet mesajı kuyruk olmadan gönderdi. Gönderimden sonra consumer kuyruğu tanımladı ve dinlemeye başladı. Consumer daha önce gönderilmiş mesajı alamaz ancak yeni gönderilen yani kuyruğu tanımladığı andan itibaren mesajları almaya başlar. Gelen mesajın tutulması için bir kuyruğun mevcut olması gereklidir.

Eğer senaryonuzda kuyruğunuza çok fazla mesaj geliyorsa bu kuyruğu dinleyen birden fazla subscriber edinebilirsiniz. Her bir kuyruğa 3, 5 subscriber tanımlayabilir ya da bir kuyruğa 5 diğer kuyruğa 10 subscriber tanımlayabilirsiniz. Subscriberların yapmış olduğu işlem sadece kuyruğa bağlanıp kuyruktan mesajları almaktır. Subscriberlar exchange’leri tanımaz.

Publisher ise exchange ile ilişkili olabileceği için exchange’i tanır. Publisher senaryoya göre mesajı ister exchange’e gönderir isterse kuyruğa gönderir. Eğer exchange’e bir mesaj gönderirse, exchange kendisine gelen mesajı ilgili kuyruğa yönlendirir. Gelişmiş sistemlerde exchange kullanımı avantajlıdır.

  • Message Durability: Mesajların dayanıklılığı. Bellekte mi yoksa fiziksel disk de mi saklayacaksın?
  • Message acknowledgment: Mesajın alındı haberi
  • Fair Dispatch: Eşit Dağılım
  • Exclusive: Diğer bağlantılar ile kullanılsın mı?
  • Arguments: Exchange ile ilgili parametrelerdir.

Fair Dispatch ⚖️

P(Publisher) — C1, C2 (Consumer)

Örneğin bizim iki tane consumer(c1, c2) miz var. C1 in c2 den daha güçlü bir yapıya sahip olduğu için sürekli c1 kuyruktaki mesajları işliyor. Aralarındaki dağılımın eşit olması için fair dispatch özelliğini kullanıyoruz. Başka bir örnek vermem gerekirse, c1’in işlemcisi ve ram oranı c2 ye göre fazla olabilir yani daha güçlü donanıma sahip olabilir. Bu durumda kuyruktaki mesajlar devamlı olarak c1 tarafından işlenecektir. Bunu dengelemek için fair dispatch’i kullanıyoruz.

Exchange Tipleri

Mesajları exchange’e 💱 göndeririz, exchange’in tipine göre ilgili kuyruklara bu mesajlar iletilir. Bu işlem sonrasında consumer(tüketici)’ler mesajları alıp işler. 4 farklı exchange tipi vardır;

1- Fanout Exchange (Gerçek Zamanlı ⏲️)

2- Direct Exchange (Adrese Teslim)

3- Topic Exchange (Çok Özel Adrese Teslim)

4- Header Exchange (Gizli Teslim 🙊)

Fanout Exchange

Kural Tanımaz

Gelen mesajların tümü routeKey bilgisine bakılmadan, kendisine bağlı tüm kuyruklara (queue) dağıtılır. Hiçbir ayrım söz konusu değildir.(O kuyruğa gitsin bu kuyruğa gitmesin gibi.)

Bu exchange tipinde ne kadar kuyruğa bağlantı sağlamış consumer yani tüketici varsa gelen mesajların hepsi tüm consumer’lara iletilir. Örneğin kuyruğa 10 adet mesaj geldi ve bu kuyruğu dinleyen 5 adet consumer var ise bu 5 consumer da ayrı ayrı 10 adet mesajı alır. ✔️

Örnek olarak hangi senaryolarda kullanılır?

  • Güncel oyun sonuçlarının tüm oyuncalara bildirilmesinde,
  • Hava durumunun haber 📰 kanallarında yayınlanması
  • Güncel oyun durumlarının tüm subcribe olanlara dagıtılması

Bu exchange tipinde kuyruk genelde consumer tarafında oluşturulur. Örneğin borsa durumunu bildirdiğiniz bir publisher’ınız var ve fanout exchange ile mesajları iletiyorsunuz. Bu senaryoda kaç adet kuyruk oluşturulacağını tahmin etmek zordur. Eğer 50 adet kuyruk oluşturursanız ve sizi dinleyen 3 adet consumer varsa kalan 47 adet kuyruk yersiz kaynak tüketecektir. Bu senaryoya göre ideal olan publisherın fanout exchange mesajları göndermesi consumerında kuyrukları tanımlamasıdır. Consumer tarafından bir kuyruk tanımı yapılmazsa mesajlarınız hiçbir yerde tutulmaz. Borsa bilgilerini gönderen bir publisher olduğunu söylediğimiz için geçmiş bilgilerin bir önemi olmadığını varsayıyoruz. Bu durumda dinlemek isteyen consumer’ın kuyruk oluşturup kuyruğu fanout exchange’e tanımlamalıdır. Bu çözümle consumerın dinleme işlemi bitince kuyruk silineceğinden dolayı kuyruk konusunda yersiz kaynak tüketmemiş olacağız.

Direct Exchange

Direct Exchange — RouteKey Değerine Göre Dağıtılır

Birebir gönderme durumudur yani adrese teslim diyebiliriz. Exchange’e mesaj gönderirken, bir tane de routeKey gönderiyoruz. Bu routeKey sayesinde exchange ilgili kuyruklara routeKey durumuna göre yönlendirme yapıyor. Bu exchange tipinde kuyrukları publisher tarafında tanımladığım düşünülürse kuyruklarım silinmeyecek dolayısıyla mesajlarım havada kalmayıp, kuyrukta depolanacaktır. Ancak consumer kuyruğa bağlanıp mesajları okuduğunda mesajlar silinecektir.

Görseldeki örnekten gidelim. Örneğin bir resim 🖼️ göndereceğiz ve exchange tipini de “direct-exchange” yaptık. Mesajlarımızı gönderirken routeKey’i “image_archive” belirttiğinizi düşünelim. Bu durumda consumer yani mesajları tüketecek olan routeKey olarak “image_archive” vermesi durumunda sadece o routeKey’e ait mesajları dinlemeye başlar.

Bir örnek daha vermem gerekirse, images’a(exchange) routeKey olarak “image_resize” verdik, resizer’a(consumer) da routeKey olarak “image_resize” verirsek, consumer Exchange tarafındaki routeKey ‘image_resize’ olan kanalı dinleyecektir, diğer 3 kanalı (görselde görünen) dinlemeyecek yani mesajları almayacaktır.

Topic Exchange

Topic Exchange — RouteKey Değerine Göre Dağıtılır

Mesaj dinleme 👂 olayını biraz daha özelleştirdiğimiz(customize) hale getirdiğimiz exchange tipidir. Daha detaylı bir yönlendirme yapısına sahiptir. Bu exchange tipinde olasılıklar çok fazla olduğu için kuyrukları consumer tarafında oluşturmak daha iyi bir seçenektir. Consumer nasıl bir data istiyorsa o dataya uygun routeKey ile birlikte kuyruğunu oluşturabilir ve kuyruğı ister kalıcı isterse down olduğunda silinsin şeklinde belirleyebilir.

Producer(Publisher) => RoutingKey = “Critical.Error.Warning” routeKey ifadelerini nokta ile birbirinden ayırıyoruz.

(Yıldız = *) : Tek bir noktadaki ifadeyi daha doğrusu noktalardaki tek bir ifadeyi temsil etmektedir.

(Diyez = #) : Örneğin “#.Warning” -> Son noktası Warning olan, başı ne olursa olsun hiç farketmez yeterki son noktası warning ile bitsin.

Consumer => Routing Key = “(yıldız).Error.(yıldız)” : İlk ve son karakter ne olursa olsun ortaki karakter “Error” olmalı

Görselde; BK = *.vegatable -> ilk kelime ne olursa olsun, noktadan sonra vegetable ile bitenleri dinle. BK = # -> routingKey ne olursa olsun dinle. BK = green.# -> ilk kelimesi green olan sonrasında ne olursa olsun kaç tane nokta gelirse gelsin dinle. Önemli olan başlangıcı green olması.

Header Exchange

Header Exchage — Key&Value değerine göre dağıtılır

Topic Exchange de olduğu gibi, burada da yine kuyruktaki mesajlarda seçiciliği artırmak için kullanılan exchange tipidir. Topic de routingKey belirliyorduk, burada da header’da (key,value) şekilde belirleme yapıyoruz yani header da dictionary gönderiyoruz.

Örnek

  • “Metarial” : ”wood”
  • “Type” : “cupboard”

Publisher tarafından gönderilen, consumer tarafından alınan header kısmı:

  • x-match:“any” dersek, key-value çiftlerinden en az birtanesi eşleşirse onu dinle, demektir.
  • x-match:“all” dersek, key-value ların tamamının eşleşmesi beklenir.

Kuyruğu publisher tarafında ya da consumer tarafında oluşturmak tamamen senaryonuza bağlıdır. Eğer kuyruğun sürekli ayakta kalmasını istiyorsanız publisherda, eğer kuyrukla işiniz yoksa consumer kuyruk bind edip dinleme işlemi yapsın dinleme işi bitince de kuyruk silinsin istiyorsanız consumer tarafında tanımlamanız daha uygun olacaktır.

Mesaj Gönderim & Dinleme & Dayanıklılık

RabbitMQda mesajlar byte dizin olarak gönderildiğinden dolayı herşey gönderilebilir. (pdf, word, resim vb.) Gönderim yaparken, exchange kullanmayıp direk kuyruğa gönderim yaparsanız bunun ismi default-exchange olarak geçmektedir. Yani exchange kullanmayan işleme defult exchange denir. Bunanla beraber eğer exchange kullanmayacaksanız basic publish de routeKey’e kuyruğunuzun ismini vermelisiniz.

RabbitMQ’e bağlantı kurarken birden fazla channel oluşturabiliriz. Bağlantı oluşturmak maliyetli bir işlem olduğundan dolayı 1 kere bağlantılı oluşturduktan sonra farklı farklı kanallar üzerinden RabbitMQ ile haberleşebiliyoruz.

Dinleme(Consumer,Subscriber) tarafında consumer ayağa kalkarken eğer dinlemek istediği kuyruk RabbitMQ’da tanımlı değilse hata alırız. Hem consumer hemde publisher tarafında aynı kuyruğu tanımlıyorsanız parametrelerinin birebir aynı olmasına da dikkat etmeliyiz. Bir alternatif de eğer publisher tarafında kuyruk tanımladıysanız veya var olduğuna eminseniz consumer tarafında yeniden kuyruk tanımlamasanızda bir sorun teşkil etmez.

Exchange ya da Queue tanımlarken durable parametresini true geçersek fiziksel bir diske kaydedilir ve RabbitMQ yeniden başlatılsa dahi kaybolmaz. (True: Fiziksel Disk, False: Bellek) Eğer biz mesajlarımızı da kalıcı hale getirmek istiyorsak bunu ayrıca belirtmemiz gerekir. Queue parametrelerinden durable: true işaretlemem kuyruğun içindeki mesajları kalıcı hale getirmez. Durable: true işaretleyip mesajların da kalıcı hale gelmesi için IBasicProperties interfacesinden yararlanmalıyız.

Kuyruğu Kim Yönetecek Biri Söylesin?

Kuyruk yönetimi işin özünde tamamen senaryonuza bağlı bir durumdur. Bazı durumlarda exchange ve kuyruğun yönetimi tamamen publisherda, bazı senaryolarda ise exchange yönetimi publisherda fakat kuyruk üretme işlemide consumerda olabilir.

Kuyruk oluşturma işlemini publishera bırakırsanız o anda consumer ayakta olmasa bile ilgili mesajınız kuyruk oluşacağından dolayı kaybolmaz. Eğer kuyruk oluşturma işlemini consumer tarafına bırakırsanız, mesaj gönderim esnasında ortada bir kuyruk olmadığından dolayı publisherların mesajları havada kalacak yani herhangi bir kuyruğa gitmediğinden dolayı silinecektir.

Mesaj ve Event Arasındaki Fark Nedir?

Eventde publisher göndermiş olduğu mesajın nasıl işleneceğini bilmez ama consumer nasıl işleneceğini bilir. Birden fazla consumer/subscriber aynı eventi dinleyerek farklı işlemler gerçekleştirebilir.

Eventler genellikle büyük veriler barındırmaz, işlenecek olan datayı barındırırlar. Örneğin kullanıcı üye olduğunda event fırlatılarak ve hoşgeldin e-postası gönder denilebilir. Tabi böyle bir senaryoda eventin içinde genellikle kullanıcının id si olur.

Sektörde Neler Oluyor? 🏬

Rakam olarak güncel olmayabilir fakat stackshare verilerine göre 1599 şirket RabbitMQ kullanmakta ve bize tanıdık gelecek bazı isimler;

Hepsiburada, Trivago, Trendyol Group, Reddit, 9GAG

Au revoir 😎

--

--