Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

RabbitMQ – Basitçe Kuyruğa Mesaj Gönderme ve Okuma

Merhaba,

Bu içeriğimizde bir uygulama üzerinden kuyruğa mesaj göndermeyi ve başka bir uygulama tarafından bu mesajı okumayı ele alan basit bir RabbitMQ uygulaması üzerine çalışıyor olacak ve böylece RabbitMQ’nun yazılımsal temelleriyle birlikte yapısal prensiplerini de ortaya koyuyor olacağız.

İlk olarak en sade mesaj gönderme senaryosunda kullanacağımız kuyruk yapılanmasını ve temel aktörleri şematik bir şekilde ele alarak başlayalım.
v
Yukarıdaki şemaya göz atarsanız eğer RabbitMQ’ya dair ilk olarak ele aldığımız makalemizdeki(bknz: RabbitMQ Nedir? Ne Amaca Hizmet Etmektedir?) görselde Exchange diye bir yapılanmanın olduğunu lakin bu yukarıdaki şemada ise olmadığını görmekteyiz. İlerideki makalelerimizde bol bol kendisinden bahsedeceğimiz exchange mekanizması basit bir mesaj gönderme işlemi gibi en temel yapılanmalarda kullanılmasına gerek olmayan bir yapılanmadır. Her ne kadar tarafımızca kullanılmasada sistem tarafından olması gereken bu yapılanmanın bu tarz durumlarda varsayılan olarak tanımlanmış olan default exchange’i devreye girmektedir.

Tekrardan şemaya dönersek eğer en temel işlem olan kuyruğa mesaj gönderme işini Publisher, kuyruktan okuma işini ise Consumer üstlenmektedir. Kuyruğa gönderilen mesajlar First In First Out(FIFO) mantığına göre sıralanmakta ve ilk gönderilen mesaj ilk olarak tüketilmektedir.

Şimdi bu teorik bilgilendirmeden sonra pratik uygulama üzerinden örneklendirmeye geçebiliriz.

Publisher ve Consumer Tasarlama

Yukarıda bahsedildiği gibi en temel senaryoda bile sadece Publisher ve Consumer olmak üzere iki aktörümüz mevcuttur. Bizler bu aktörleri birer Console Application olarak oluşturacak ve tasarlayacağız. Tabi ki de sizler gerçek ihtiyaçlarınıza dönük uygulamalar seçebilir ve içerikte ele alacağımız tüm RabbitMQ nimetlerinden faydalanabilirsiniz.

  • Publisher Tasarımı – Kuyruğa Mesaj Gönderme

    Publisher, RabbitMQ servisindeki kuyruğa mesaj gönderen uygulamadır.

    .NET Core platformunda RabbitMQ kullanabilmek için öncelikle ilgili projeye RabbitMQ.Client Nuget paketinin yüklenmesi gerekmektedir.
    v

    • Bağlantı Oluşturma
      Publisher’ın mesaj gönderebilmesi için herşeyden önce RabbitMQ sunucularına bağlantı oluşturmamız gerekmektedir. Bunun için;

              static void Main(string[] args)
              {
                  ConnectionFactory factory = new ConnectionFactory();
                  factory.Uri = new Uri("amqp://hkhjerrt:hcqiavAqll6-co4abXnSqUBh_hHifz-Z@hornet.rmq.cloudamqp.com/hkhjerrt");
                  //factory.HostName = "localhost";
              }
      

      şeklinde çalışabiliriz. Koda göz atarsanız eğer “ConnectionFactory” sınıfı üzerinden bu bağlantının tanımlandığını görebilirsiniz. Burada 4. satır ile 5. satır arasındaki farka değinmem gerekirse eğer; 4. satır CloudAMQP sunucularında barındırılan RabbitMQ instance’ına bağlantı kurabilmek için tarafımıza verilen AMQP URL değeri iken, 5. satırda ise localdeki RabbitMQ sunucuna bağlanmak içindir.

    • Bağlantı Sağlama ve Kanal Açma
      Oluşturulan bağlantı hattı üzerinden bağlantıyı aktifleştirelim ve ardından bu bağlantı üzerinden kullanacağımız kuyruğu oluşturabilmek için bir kanal açalım.

              static void Main(string[] args)
              {
                  ConnectionFactory factory = new ConnectionFactory();
                  factory.Uri = new Uri("amqp://hkhjerrt:hcqiavAqll6-co4abXnSqUBh_hHifz-Z@hornet.rmq.cloudamqp.com/hkhjerrt");
                  //factory.HostName = "localhost";
      
                  using (IConnection connection = factory.CreateConnection())
                  using (IModel channel = connection.CreateModel())
                  {
      
                  }
              }
      

      7. satırda bağlantı sağlanmakta, 8. satırda bir kanal oluşturulmaktadır. Bu işlemden sonra kuyruk oluşturabiliriz.

    • Kuyruk Oluşturma
              static void Main(string[] args)
              {
                  ConnectionFactory factory = new ConnectionFactory();
                  factory.Uri = new Uri("amqp://hkhjerrt:hcqiavAqll6-co4abXnSqUBh_hHifz-Z@hornet.rmq.cloudamqp.com/hkhjerrt");
                  //factory.HostName = "localhost";
      
                  using (IConnection connection = factory.CreateConnection())
                  using (IModel channel = connection.CreateModel())
                  {
                      channel.QueueDeclare("mesajkuyrugu", false, false, true);
                  }
              }
      

      Burada 10. satıra göz atarsanız eğer kanal üzerinden QueueDeclare metodu aracılığıyla bir kuyruk oluşturulmuştur. İlgili metodun parametrelerini izah edebilmek için aşağıdaki görseli ele alalım;
      RabbitMQ - Basitçe Kuyruğa Mesaj Gönderme ve Okuma

      • queue : Oluşturulacak kuyruğun adını belirliyoruz.
      • durable : Normal şartlarda kuyruktaki mesajların hepsi bellek üzerinde dizilirler. Hal böyleyken RabbitMQ sunucuları bir sebepten dolayı restart atarlarsa tüm veriler kaybolabilir. durable parametresine true değerini verirsek eğer verilerimiz güvenli bir şekilde sağlamlaştırılacak yani fiziksel hale getirilecektir.
      • exclusive : Oluşturulacak bu kuyruğa birden fazla kanalın bağlanıp, bağlanmayacağını belirtir.
      • autoDelete : True değerine karşılık tüm mesajlar bitince kuyruğu otomatik imha eder.
    • Kuyruğa Mesaj Gönderme
              static void Main(string[] args)
              {
                  ConnectionFactory factory = new ConnectionFactory();
                  factory.Uri = new Uri("amqp://hkhjerrt:hcqiavAqll6-co4abXnSqUBh_hHifz-Z@hornet.rmq.cloudamqp.com/hkhjerrt");
                  //factory.HostName = "localhost";
      
                  using (IConnection connection = factory.CreateConnection())
                  using (IModel channel = connection.CreateModel())
                  {
                      channel.QueueDeclare("mesajkuyrugu", false, false, true);
                      byte[] bytemessage = Encoding.UTF8.GetBytes("sebepsiz boş yere ayrılacaksan");
                      channel.BasicPublish(exchange: "", routingKey: "mesajkuyrugu", body: bytemessage);
                  }
              }
      

      11. satırda kuyruğa göndereceğimiz mesajımızı oluşturmuş bulunuyoruz. Dikkat ederseniz mesajımızı byte dizisine çeviriyoruz. Bunun nedeni RabbitMQ’nun byte türünde veriyi kabul etmesidir. Basit bir string ifadeden tutun, kendinizi bile kuyruğa gönderecekseniz byte dizisine çevirmelisiniz. 12. satırda ise “BasicPublish” metodu aracılığıyla açtığımız kanal üzerinden mesajımızı kuyruğa gönderiyoruz. İlgili metodu daha da detaylandırırsak eğer;

      • exchange : Eğer exchange kullanmıyorsanız boş bırakınız. Böylece default exchange devreye girecek ve kullanılmış olacaktır.
      • routingKey : Eğer ki default exchange kullanıyorsanız routingKey olarak oluşturduğunuz kuyruğa verdiğiniz ismin birebir aynısını veriniz.
      • body : Gönderilecek mesajın ta kendisidir.
    • Publisher’ı Ayağa Kaldırma ve Kuyruğa Mesaj Gönderme
      Oluşturduğumuz uygulamayı derleyip çalıştırdığımızda tanımladığımız mesaj kuyruğu oluşturulacak ve içerisine gönderdiğimiz mesaj eklenmiş olacaktır. Bunu görebilmek için tekrar CloudAMQP adresi üzerinden instancelarımızı listeleyelim ve ilgili instance üzerinden aşağıdaki gibi RabbitMQ Manager sekmesini seçelim.
      RabbitMQ - Basitçe Kuyruğa Mesaj Gönderme ve Okuma
      Açılan pencere üzerinden “Queues” sekmesini seçtikten sonra ilgili kuyruğun oluşturulduğunu ve içerisine de bir adet mesaj atıldığını göreceksiniz.
      RabbitMQ - Basitçe Kuyruğa Mesaj Gönderme ve Okuma

    Evet… Mesaj üreticimizi bitirmiş bulunmaktayız. Şimdi sıra tüketiciyi oluşturmaya geldi.

  • Consumer(Receiving) Tasarımı – Kuyruktan Mesaj Alma/Okuma

    RabbitMQ - Basitçe Kuyruğa Mesaj Gönderme ve Okuma

    Consumer, RabbitMQ servisinde kuyruktaki mesajları tüketen uygulamadır.

    Consumer uygulamasında da RabbitMQ.Client Nuget paketini kurmalı ve ardından Publisher’da incelediğimiz bağlantı, kanal vs. oluşturma işlemlerini adım adım gerçekleştirmeliyiz.

            static void Main(string[] args)
            {
                ConnectionFactory factory = new ConnectionFactory();
                factory.Uri = new Uri("amqp://hkhjerrt:hcqiavAqll6-co4abXnSqUBh_hHifz-Z@hornet.rmq.cloudamqp.com/hkhjerrt");
    
                using (IConnection connection = factory.CreateConnection())
                using (IModel channel = connection.CreateModel())
                {
                    channel.QueueDeclare("mesajkuyrugu", false, false, false);
                    EventingBasicConsumer consumer = new EventingBasicConsumer(channel);
                    channel.BasicConsume("mesajkuyrugu", false, consumer);
                    consumer.Received += (sender, e) =>
                    {
                        //e.Body : Kuyruktaki mesajı verir.
                        Console.WriteLine(Encoding.UTF8.GetString(e.Body));
                    };
                }
                Console.Read();
            }
    

    Her ne kadar cunsomer kuyruktaki mesajları tüketecek olan bir uygulama olsa dahi 9. satırdaki “QueueDeclare” metodu ile publisher’da ki değerlerle birebir aynı olacak şekilde kuyruk tanımlamalıyız. 10. satırda ise tanımladığımız kuyruktaki mesajları yakalayacak bir event oluşturuyor ve ardından 11. satırda “BasicConsume” metodu ile ilgili mesajları tüketiyoruz. “BasicConsume” metodunun parametrelerini incelersek eğer;

    • queue : Mesajların alınacağı kuyruk adı.
    • autoAck : Kuruktan alınan mesajın silinip silinmemesini sağlıyor. Bazen kuyruktan alınan mesaj işlenirken beklenmeyen hatalarla karşılaşılabiliyor. O yüzden mesajı başarılı bir şekilde işlemeksizin kuyruktan silinmesini pek önermeyiz.
    • consumer : Tüketici.

    12. satıra göz atarsanız eğer “EventingBasicConsumer” tipinden tanımladığımız consumer nesnesinin Received olayı bizlere kuyruktaki mesajları getirecektir. Dikkat ederseniz mesaj byte dizisi olarak gönderildiğinden dolayı haliyle byte dizisi olarak elde edilecek ve biz ilgili dönüşümü yapmak suretiyle mesajı işlemekteyiz.

Derleyip, Test Edelim

Her iki projeyide derleyip sırasıyla Publisher ve Consumer şeklinde çalıştırdığımızda cloud’da ki bağlantı kurulan sunucuda kuyruğa atılan mesajların consumer tarafından tüketildiğini ve tek tek ekrana yazıldığını göreceksiniz.
RabbitMQ - Basitçe Kuyruğa Mesaj Gönderme ve Okuma

Evet… Böylece basit bir mesaj kuyruk uygulaması yapmış bulunmaktayız. Tabi ki de sonraki içeriklerimizde olayı daha da detaylandıracak ve derinlerde anlamlı sistemler oluşturacağız.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

10 Cevaplar

  1. Erdem dedi ki:

    Hocam merhabalar , RabbitMQ API güncellenmiş. Consumerda ki e.Body yerine e.Body.Span kullanılabilir ayrıca kuyruk tanımlarken publisher da autodelete parametresi ‘true’ iken consumer da ‘false’ demiştik o da hata yaratıyor. Yazı için tekrardan emeğinize sağlık.

  2. nihal dedi ki:

    Merhabalar, Rabbitmq yazı diziniz çok faydalı olmuş. Emeğinize sağlık.

    Console.WriteLine(Encoding.UTF8.GetString(e.Body));
    

    satırında hata vermekte, çözüm için;

    "Console.WriteLine(Encoding.UTF8.GetString(e.Body.ToArray()));"
    

    yazabiliriz.
    Teşekkürler.

  3. Taha Öztürk dedi ki:

    Test etmek için iki projeyi aynı solution’a koyup solution properties’inden (solution sağ tık) multiple startup project’i seçip iki projeyi’de start edebilirsiniz. İlk olarak publisher’ın çalışması gerektiğinden üste publisher’ın olduğundan emin olun.

  4. Ugur A. dedi ki:

    Herkese selamlar,
    Öncelikle Gençay hocamın anlatımı gerçekten çok iyi. Yeni bir şey araştıracaksam önce Gençay Yıldız anlatmış mı diye bakıyorum sonra başka kaynaklara gidiyorum, müptelası olduk 🙂 Genel olarak rabbitmq yazı dizisi de bu kalitede. Hiç bilmiyordum şu an rabbitçi olduk 🙂
    Bu yazı dizisini ben bir minimal api da denedim. Hocamızın anlattığı kuyruğa mesaj gönderme kısmı eksiksiz çalışıyor.
    Fakat Consumer (kuyruktan mesaj okuma) kısmında yaşadığım bazı aksaklıklar oldu. Karşılaştığım bu sorunları ve çözümlerini paylaşmak istiyorum:

    Öncelikle kolaydan başlayalım “Console.WriteLine(Encoding.UTF8.GetString(e.Body));” satırındaki kodu “Console.WriteLine(Encoding.UTF8.GetString(e.Body.ToArray());” yapmamız gerekiyor.

    Asıl beni uğraştıran kısıma geliyorum:

    QueueDeclare yani 9. satır benim kodlarımda olunca okuma yapılamıyor. “channel.QueueDeclare(“mesajkuyrugu”, false, false, false);” satırından bahsediyorum. Eğer siz de okumada bir sorunla karşılaşıyorsanız bunu deneyebilirsiniz. Bu durumun sebebini bilemiyorum ama ben böyle çözdüm. Gençay hocamız bu yazıyı şubat 2020 de yazmış ben şu an Ağustos 2023 ten bildiriyorum 🙂 Bellki de ilgili kütüphane güncellenmiştir vs.

    Umarım bu yorum herkesin işine yarar, Gençay bey yazı dizisi için tekrar teşekkür ederim.

    • Gençay dedi ki:

      Bol faydalar olsun 🙂 Vee kütüphanenin güncel kullanımı için verdiğiniz bilgiler için de teşekkür ederiz 🙂
      Tabi bu durumun farkındaydım, o yüzden youtube kanalımızdaki RabbitMQ eğitim serisindeki içerikleri en güncel haliyle yayınlamış bulunuyorum.

      Bol faydalar olsun.

      • Ugur A. dedi ki:

        Bilgilendirme için teşekkürler,
        Sormadan edemeyeceğim Gençay bey, youtube taki seride web api üzerinde background service ile çalışacak şekilde de örnekler var mıdır ? (inşallah cevap evettir) 🙂

  1. 25 Şubat 2020

    […] önceki RabbitMQ – Basitçe Kuyruğa Mesaj Gönderme ve Okuma başlıklı yazımda RabbitMQ servisine mesaj gönderme ve gönderilen mesajları okuma eylemlerini […]

  2. 28 Şubat 2020

    […] RabbitMQ – Basitçe Kuyruğa Mesaj Gönderme ve Okuma […]

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir