Amqp ve RabbitMQ

Amqp (Advanced Message Queuing Protocol)  gelişmiş bir mesajlaşma protokolü. Eğer benim gibi Java geliştiricisiyseniz, mesajlaşma denildiğinde aklınıza ilk gelen JMS (Java Messaging Service) oluyor. JMS her ne kadar ağırlıklı olarak mesajlaşma tabanlı mimarilerde kullanılsada, multi-thread uygulama geliştirmeden tutunda, sadece işlemleri asenkron hale getirmeye kadar farklı bir çok alanda da kullanılıyor. Bu sebepten JEE uygulaması geliştirdiyseniz kıyısından köşesinden de olsa JMS’e bulaşmış olma ihtimaliniz bir hayli yüksek. Bundan dolayı AMQP’yi, JMS’ten yola çıkarak anlatmaya çalışacağım. Daha sonra ise sıfır bir makineye nasıl RabbitMQ kurulumu yapacağımıza değineceğiz. Bir sonraki yazımda ise Spring ile AMQP protokolünü kullanarak nasıl uygulama geliştireceğimize bakacağız.

AMQP vs JMS

En temel farklılık herhalde JMS’in bir API, AMQP’nin protokol oluşundan kaynaklanıyor. Bu açıdan bakıldığında AMQP ile JMS’in karşılaştırılması çok mantıklı değil. Benimde asıl amacım ikisini karşılaştırmak değil zaten. Sadece AMQP’yi JMS’e kıyasla anlattığımda daha rahat anlatabileceğimi düşünüyorum.

AMQP bir protokol, tamam da protokol oluşu bize ne sağlıyor? En basitinden, AMQP kullanarak platform bağımsız olarak haberleşebilirsiniz. Sizin AMQP destekli bir sunucunuz varsa istemciniz ister .NET’te ister Pyton’da geliştirilmiş sizi hiç ilgilendirmiyor. Fakat JMS bir Java API genelde sunucular ve istemciler Java platformuna ait oluyor. Yine de JMS bir API olduğundan alt tarafta istediği protokolden konuşabilir.  Tabi Sadece Java konuşulan bir ortamda AMQP’ye dönüş yapılması biraz abartı olabilir. Sonuçta çok rahatlıkla java byte koduna dönüş yapıp bu byteları diğer jvm’e aktardığınızda haberleşmeyi tamamlamış oluyorsunuz. Sadece TCP bile size yetecektir.

Diğer bir farklılık, AMQP sadece ikilik sistemde konuşabilirken, JMS kullandığınızda 5(Text, Object, Map, Byte, Stream*) farklı veri tipinde haberleşme yapabiliyorsunuz. Eğer kendi nesneleriniz üzerinden haberleşme sağlayacaksanız, geliştirici olarak bunu çok fazla hissetmeyeceksiniz. Kullanacağınız kütüphaneler sizi bu işlemlerden hemen hemen yalıtıyor olacak. Örneğin Spring AMQP kullandığınız doğrudan Jackson adaptörü kullanarak JSON formatında nesnelerinizi gönderebilirsiniz.

Mesaj yapılarını inceleyecek olursak, bir JMS yapısında headerproperties ve messaj alanları bulunurken, AMQP’de paketlerde header, properties ve mesaj alanlarının yanı sıra bir de footer alanı bulunmaktadır. Özellikle AMQP’de çoğu ayarlamalarınızı bu alanlar üzerinden yapıyorsunuz. Örneğin gönderdiğniz mesajınız için bir ‘son kullanma tarihi’ ayarlamak istiyorsanız, ya da son kullanma tarihi geçen mesajların hangi kuyruklara yönlenmesini ayarlamak istiyorsanız, bu alanları sıkça kullanıyorsunuz.

Mesajlasma Mimarileri

AMQP, JMS’e ek olarak mesajlaşma mimarinizi kurabileceğiniz farklı haberleşme yapıları sağlıyor. Temel mesajlaşma yapıları olduğundan ve her iki yapı tarafından da desteklendiğinden, öncelikle point-to-point ve publish-subscribe yapılarından bahsetmek istiyorum.

point-to-point (Direct Exchange)

Bir mesaj üreticisi tarafından (producer, sender) üretilen mesajın sadece bir tüketiciye (consumer, receiver) iletildiği yapıdır. Burada birden fazla tüketicinin olmasında bir sakınca yoktur. Fakat tek bir mesaj sadece tek bir tüketici tarafından işlenir. Bu yapı JMS’lerde kuyruklar (queue) üzerinden sağlanırken, AMQP’de ise Direct exchange ile sağlanıyor. AMQP’de mesaja exchange ile birlikte gideceği kuyruğun key’inide veriyoruz.

publish-subscribe (Fanout Exchange)

Bir mesaj üreticisi tarafından üretilen mesajın tüm tüketicilere iletildiği yapıdır. Bir nevi observer pattern’in başka uygulamar arasında dağıtılmış hali gibi düşünebilirsiniz. Bir event oluştuğunda tüm dinleyenler tetikleniyor. Bu yapı JMS’te topic’ler le sağlanırken, AMQP’de fanout exchange’ler ile sağlanıyor. AMQP’de ki yapı burada biraz daha farklı. AMQP’de bir exchange’e farklı kuyruklar bağlanıyor. Fanout exchange, gelen mesajı bağlı olan tüm kuyruklara dağıtıyor.

Temel iki yapımızdan bahsettiğimize göre şimdi de biraz farklı olan yapılara değinelim. Öncelikle AMQP’de exchange ve kuyruk yapısı farklı farklı. Siz mesajınızı exchange’e belirli bir key ile gönderiyorsunuz. Exchange ise farklı verilere bakarak, bu mesajı farklı kuyruklara dağıtıyor. Örneğin, point-to-point yapısını sağlarken,  Direct exchange kullanıyoruz. Direct exchange ise mesajı gönderdiğiniz key’e göre dağıtıyor. Bu sebepten de mesajınız sadece bir kuyruğa gidiyor. Fakat publish-subscribe yapısını sağlarken, Fanout exchange kullandık. Fanout exchange ise gönderdiğiniz key’i göz ardı edip, kendisine bağlı olan tüm kuyruklara mesajı iletiyor.

AMQP protokolünde toplam 4 adet exchange tipi var. Direct, Fanout, Topic ve Header. Direct ve Fanout’u ne için kullandığımızı anlatmaya çalıştım. Şimdi diğer iki yapıyı kullanarak nasıl bir mesajlaşma mimarisi kurabileceğimize bakalım.

Topic Exchange

Bu tip bir mesajlaşma kullandığınızda mesajınız farklı kurallara göre point-to-point ya da publish-subscribe şeklinde davranabiliyor. Bu exchange tipinde sıradan bir key yapısı kullanamazsınız. Key’inizin belirli özelliklerde olması gerekiyor. Eğer JSF yazdıysanız bir nevi el notasyonu olarak düşünebilirsiniz. Örneğin orange.rabbit, orange.cat ve white.rabbit şeklinde 3 farklı key’de mesaj üretildiğini düşünün. Bu mesajların nasıl dağıtılacağına ayarlarınıza göre karar veriliyor. Örnğin yandaki resimdeki gibi bir ayarlama yapacaksanız, orange.rabbit ve orange.cat mesajlarınız Q1 kuyruğuna giderken, orange.rabbit ve white.rabbit ile gönderilen mesajlar Q2 kuyruğuna gidiyor.

Header Exchange

Ne yazıkki her kontrol etmek istediğiniz parametre, key tanımlamak kadar kolay olmayabilir. Bu sebepten siz kontrol etmek istediğiniz bilgileri mesajınızın header’ında tanımlayabiliyorsunuz. Topic’te olduğu gibi hem point-to-point hem de publish-subscribe şeklinde davranabiliyorsunuz.

Son olarak bu yapıların detaylarına buradan ve buradan erişebilirsiniz.

Mesaj yapılarındaki farklılıklara değindiğimize göre, sıra son farklılığımıza geldi. Genelde çoğu JMS sunucusu, Java dilinde geliştiriliyor. Bu sebepten kendi uygulama sunucusunuza kurabildiğiniz gibi isterseniz uygulamanıza da gömebilirsiniz. Fakat AMQP için ne yazık ki böyle bir genelleme yapamıyoruz. Örneğin, en çok kullanılan AMQP destekleyen sunucu RabbitMQ Erlang’da yazılmıştır. Bu sebepten Java uygulamanız içerisine gömmeniz pek mümkün değil. Şimdi development ortamnız için RabbitMQ sunucunun nasıl kurulacağına bakalım.

RabbitMQ Kurulumu

Development mankinesine kurulum yapacağım için kurulumun nasıl yapılacağını Vagrant kullanarak anlatacağım. Vagrant, yazılım ekibinizin aynı ortamda geliştirme yapmasına olanak sağlayan bir araç. Alt tarafta isteğinize bağlı olarak VirtualBox ya da vmware kullanarak sanallaştırma yapıyor. Eğer sisteminizde vagrant kurulu değilse kurulum yapmanızın tam zamanı.

Vagrant kurulumunu yaptıktan sonra geliştirme ortamınızda yeni bir klasör yaratın. Örneğin ben ubuntu-rabbitmq isminde bir klasör yarattım. Daha sonra bu klasörün içerisine girin ve aşağıdaki komutu çalıştırın.

Bu komut klasörünüzü vagrant için hazırlayacak. Bu işlem sonunda klasörünüzde Vagrantfile isminde bir dosya yaratıldığını göreceksiniz. Şimdi bu dosyayı açıp aşağıdaki şekilde dosyayı değiştirin.

Vagrantfile

Kısaca bu dosyanın neler yaptığından bahsedeyim.

  • config.vm.box: Hangi sistem image’ini kullanarak sanal makinenizi ayağa kaldıracağınızı belirtiyor. Ben ubuntu/trusty64’i seçtim yani Ubuntu 14.04 LTS sürümünü kullanıyorum. Eğer başka bir image seçmek istiyorsanız bu adresten bakıp istediğinizi seçebilirsiniz.
  • config.vm.network: Bu satırda oluşturacağımız sanal makine ile ne şekilde bağlantılı olacağımızı belirliyoruz. “private_network” diyerek kendi makinemiz(Host) ile sanal makine(Guest) arasında yeni bir ağ kurulmasını istediğimizi belirtiyoruz. Bu ağda sadece iki makine bulunacak. Bu yeni makinenin ip’sini de 192.168.33.10 olarak ayarlıyoruz. Bu arada normalde yerel ağda 10.x.x.x şekilnde ip’ler kullanılıyor. Burada vereceğiniz ip yerel ağınızla ilişkili değil.
  • config.vm.provision: Bu şekilde başlayan satırlarda da makine kurulduktan sonra ne yapmak istediğimizi belirtiyoruz. Örnekteki Vagrantfile’da, önce bir shell çalıştıracağımızı, sonra bir file-upload yapacağımızı ve en son yeni bir shell daha çalıştıracağımızı belirtmişiz. İlk shell script’inde öncelikle rabbitmq paket sunucularını ekleyip, rabbitmq-server kurulumunu yapıyoruz. Kurulum yaparken normal rabbitmq sunucusunun yanısıra yönetimsel işlemlerin yapıldığı eklentiyi de aktif hale getiriyoruz. Kurulum bittikten sonra file-upload kısmı başlıyor. Burada upload ettiğimiz file kurduğumuz sunucuya ilişkin ayar dosyası. Birazdan bu ayar dosyasının ne olduğuna ve neleri ayarladığımıza değineceğiz. Ayar dosyamızı yükledikten sonra son shell script’imiz çalışıyor. Bu shell script ise, yüklediğimiz ayar dosyasını ilgili dizine kopyalıyor ve rabbitmq-server uygulamasını çalıştırıyor.

Şimdi ise yükleyeceğimiz ayar dosyasına bakalım. Vagrantfile ile aynı dizine rabbitmq.config isminde bir dosya yaratıp içeriğini aşağıdaki gibi yapıyoruz.

rabbitmq.config

Burada temel olarak,  rabbitmq sunucunun hangi port üzerinden dinleme yaptığını (5672) ve hangi kullanıcı adı ve şifre ile bağlanılması gerektiğini (guest/guest)  ayarlıyoruz. Dosyayı Vagrantfile ile yüklemeden önce çalışan script’te, yönetimsel eklentileri aktif etmiştik. Bu eklenti ise 15672 portundan ayağa kalkıyor.

Şimdi tüm hazırlığımızı yaptığımıza göre vagrant ile sanal makinemizi ayağa kaldırabiliriz. Bunun için aşağıdaki komutu çalıştırıyoruz.

Vagrant öncelikle kullandığımız image’ı (ubuntu/trusty64) indirecek. Bunu tek seferlik yapacak. Yani eğer başka bir uygulama için yine bu image’a ihtiyaç duyarsa, bir daha indirmeyecek. Sonra makineyi bizim verdiğimiz ayarlarda çalıştırıp ilgili işlemleri yapacak. Burada config.vm.provision ile verdiğimiz işlemleri (Script_1, file-upload, Script_2) sadece makinemizi ilk çalıştırdığımızda yapacak. Daha sonra makinemizi kapatıp açtığımızda bu işlemleri bir daha yapmayacak.

Şimdi http://192.168.33.10:15672 adresine giderek uygulamamızın çalışıp çalışmadığına bakalım. Eğer herşeyi düzgün ayarlandıysa sizi, yönetimsel eklentinin (rabbitmq-management-plugin) arayüzü karşılayacak. Login bilgilerimizi (guest/guest) girerek sisteme giriş yapabiliriz.

Son

Böylelikle kısaca AMQP’nin ne olduğuna ve çokça tercih edilen RabbitMQ sunucusunun nasıl kurulacağına değinmiş olduk. Unutmadan belirteyim, ayağa kaldırdığınız sanal makineyi kapatmak için aşağıdaki komutu kullanabilirsiniz.

Bir sonraki yazımda, Spring-Amqp kütüphanesini kullanarak RabbitMQ üzerinden haberleşme yapan bir uygulamayı nasıl geliştirebileceğimize bakacağız. Bu bölümde bahsettiğimiz dosyalara aşağıdaki gist adreslerinden de erişebilirsiniz.

Gist

 

  • Mesut Özen

    Elinize sağlık,çok faydalı bir makale.

    Not : Belirtilen gist adreslerine erişemiyorum 🙁

    • bhdrkn

      Teşekkür ederim Mesut Bey

      Bu arada gist adreslerini düzeltim.

  • Yakup

    Slm bahadır hocam. Gecenin bir yarısı nette birşeyler araştırırken bloguna denk geldim ve amazon sürecini soluksuz okudum. Çok güzel bir blogun var çok teşekkür ediyorum paylaşımların için. Sormak istediğim sorulara gelince. RabbitMQ yu kendi server’uma kurmak istiyorum fakat bildiğin gibi server’un desktop arayüzü olmadığı için rabbitmq nun management arayüzüne erişemem. Lan dan değilde Wan’dan erişmem gerekiyor, bunun için ne yapabilirim ? spring web uygulamam aynı server da koşuluyor. springden rabbitmq’ya nasıl erişim sağlayabilirim ? son olarakda rabbitmq’nun mqtt eklentisiyle kullanmayı düşünüyorum çünkü android’den erişim sağlamam gerekiyor, araştırmalarım sonucu mqtt’nin google cloud messaging in iyi bir alternatifi olduğu sonucuna vardım. Bu konuda tavsiyelerini merak ediyorum. Paylaşırsan memnun olurum.

    • bhdrkn

      Merhaba Yakup,

      Öncellikle yorumların için çok teşekkür ederim. Böyle yorumlar almak gerçekten çok hoşuma gidiyor.

      Elimden geldiğince sorularını yanıtlamaya çalışayım.

      Arayüz konusunda sıkıntın olmasın. RabbitMQ bir web arayüzü sağlıyor. Ağ üzerinden istediğin gibi erişebilirsin. Ubuntu tabanlı bir sunucun varsa aşağıdaki komutlarla kurabileceğini düşünüyorum. Zaten vagrant’ta bu komutlar çalıştırılıyor.

      $ echo ‘deb http://www.rabbitmq.com/debian/ testing main’ >/etc/apt/sources.list.d/rabbitmq.list
      $ wget http://www.rabbitmq.com/rabbitmq-signing-key-public.asc
      $ apt-key add rabbitmq-signing-key-public.asc
      $ apt-get update
      $ apt-get install -q -y rabbitmq-server
      $ service rabbitmq-server stop
      $ rabbitmq-plugins enable rabbitmq_management
      |Config dosyanı güncelleyip gerekli kullanıcı ve izinleri tanımlamalısın, yazı da örnek config dosyası var|
      $ service rabbitmq-server start

      Sonrasında http://sunucu_ip:15672 üzerinden, aynı ağdaki istediğin makineden erişebilirsin.

      MQTT kısmında söyleyebileceğim çok fazla şey yok. Ben AMQP üzerinden ve spring kullanarak geliştirdim uygulamalarımı. Ama MQTT ve Android ilgi çekici bir konuya benziyor. Güzel bir yazı olabilir düşüncesindeyim. 🙂 Araştıracağım.

      Eğer Amqp ile Spring uygulaması geliştirmeyi düşünürsen, Spring AMQP kullanmanı öneririm. Belki zaten denk gelmişsindir ama bu konu ile ilgili şöyle (http://www.bahadirakin.com/spring-amqp-ve-rabbitmq/ ) bir yazım daha var belki işine yarar bişeyler çıkar.

      Ufak bir not: Disqus Linkleri kafasına göre kısaltıyor. Yukarıda verdiğim komutları çalıştırırken linkleri tekrardan kopyalaman gerekebilir.

  • Seymur Asadov

    Iyi Gunler.. spring security kullaniyorum.. tam mantigi otuzduramadim , ozur dilerim..Simdi benim db-de userim var ..ve bir userden digerine mesaj gondermek istiyorum , user_id uygun olarak..Yani 2 numarali user 4 numarali usere mesaj gonderecek.Ve bu mesajlar da db-e yazmaliyim.Bunun icin point to point kullanacagim biliyorum,ama nasil ? Bir yol gosterirseniz mutlu olurum))