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 header, properties 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

topicsBu 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 Kurulumurabbitmq

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.octobiwan

Gist