Mikroservis + RabbitMQ uygulaması - 5

Bu yazıda mikroservis projemize eklediğimiz rabbitmq 'yu nasıl kullandığımızı analiz edeceğiz. Kurguladığınız sistem her ne olursa olsun rabbitmq gibi bir aracı düzgün kullanmak için önceki yazılarda belirttiğim gibi özelliklerini iyi bilmeniz ve aracı iyi tanımanız gerekiyor.

Sistemimizde spring cloud kütüphanelerini kullanırken bu kadar detaylı incelemek zorunda değildik. Gateway ve eureka kullandık ve bu araçlar da her ne kadar güzel özellikleri barındırsa da temel işlevleri üzerine yoğunlaşmış araçlar. Message broker 'lar bir adım daha geniş bir alana hitap edebiliyorlar çünkü aracı hizmet oldukları için bağladıkları sistem parçaları bambaşka işler yapabiliyor. Şunu kastediyorum: Göndericiden ve alıcıdan bağımsız çalışmak zorunda olan kuyruk yapıları, gönderici ve alıcının sorunlarını ve yükünü en aza indirmeye çalışıyor.

Nasıl kullandık

Bizim sistemimizdeki rabbitmq nasıl çalışıyordu: MVC uygulamasından gelen mesajları mail servisine kuyrukla iletiyor ve iletilemeyenleri deadletter 'a atıp işini bitiriyordu. Büyük resim her zaman olduğu gibi burada dursun.

Message broker tarafında herhangi bir backup veya load balance işlemi gerçekleştirmedik. İtiraf etmek gerekirse rabbitmq içindeki node management işlerini pek sevmedim ve anlayamadım :) Mail servisi de tek bir servis olarak dinleyicisi oldu. Rabbitmq 'nun iki tarafında da sistemimiz içerisindeki parçalar mevcut. Yani gönderici ve alıcı dış sistemlerde değil tamamen bizim yönetimimizde. Bu böyle olmayabilirdi. Mail servisi üçüncü parti sağlanan veya bizim kontrolümüzde olmayan bir docker imajı olabilirdi örneğin. Bu durumda ne zaman cevap dönecek, nasıl cevaplar dönmesi muhtemeldir gibi sorularla da uğraşmamız gerekecekti.

3 işlev için 3 exchance ve 3 kuyruk kullandık. Satın alma işlemi gerçekleştiğinde ve etkinlik iptalinde mesajlar gönderdik. Bu kısımda pek kritik bir case yok. Daha karmaşık işlemler için nasıl kuyruklar ve exchange yapıları oluşturmanız gerektiğini iyi düşünmeniz gerekecektir. İleride bunları değiştirmek çok maliyetli olacaktır çünkü. Biz farklı bir konuya odaklandık. İptal mailleri gitmesi sırasında sistemin bir şekilde hata verebileceğini simüle ettik ve bu hataları deadletter kuyruğuna yönlendirdik. Rabbitmq 'nun deadletter özelliğini kullandık ve bu sorumluluğu ve gerekli konfigürasyonları tamamen rabbitmq 'ya yükledik.

Yani aslında çok temel işlevler için kullandık. Exchange 'ler ve kuyruklar için ekstra özelliklere hiç girmedik, ki benim bilgim bile yok o konularda. Topic yapısını mecbur olmadığımız halde çalışma yapısını anlamak için kurguladık. Dinleyici servisimizdeki metodların başında rabbitlistener yazması dışında olağan dışı hiçbir işlevleri yok. Düşündüğümüzde aslında MVC tarafı da mail servisi tarafı da son derece ilkel. Sadece mvc uygulamasında erişeceğimiz kuyrukların ve exchange 'lerin konfigürasyonunu bilmemiz gerekiyor. Rabbitmq biraz kritik bir duruma gelmedi mi?

Olası sorunlar ve önlemler

Aklıma gelen ilk sorundan başlayayım. Rabbitmq gibi message broker 'ları asenkron işlemlerde kullanıyoruz genelde. Öyle ki bu işlemler herhangi bir dış sistemden cevap bekleyen işlemler de olabilecektir. Kendi elimizle network 'ü handle eden kodlar yazmak yerine mesaj bekleyecek aracı olarak rabbitmq kullanabiliyoruz. Bu durumda herhangi bir hata oluştuğunda zaten asenkron çalışan bir sistem olduğu için "rabbitmq bunu neden yaptı" veya "bunun sebebi rabbitmq mu yoksa dış sistem mi" sorularına yanıt bulmak zorlaşıyor. Message broker aracı hangisi ne olursa olsun çok iyi bir loglama ve monitoring gerektiriyor gibi.

Yine benzer bir durum fırlattığımız hata için de geçerli. Mail servisinde rastgele hata fırlatıp deadletter kuyruğunun çalıştığını görmek istedik ve çalıştı da. Fakat MVC tarafında bu hatadan haberimiz bile yok. Deadletter kuyruğunda hata biriktiğinden de haberimiz yok. Birilerinin bir raporlama fonksiyonu hazırlaması (mümkünse rabbitmq içerisinde veya ayrı bir serviste) ve çalıştığından emin olması gerekiyor. Hatta oluşan hataları da bir yerde loglamamız gerekiyor mikroservis mimarisinin doğası gereği hatalar da dağıtık çünkü. Bu şekilde deadletter 'a mesaj atmanın bir anlamı olacaktır. Biz sadece çalıştığını gördük, ideal çalışma mantığını kurgulamadık.

Deneyip gördüğüm bir durum daha: RabbitMQ sunucusu durdurulduğunda Connection refused: connect hatası alıyor sistem ve bu hatayı handle edecek arada herhangi bir resilience sağlayıcısı olmadığı için sistem bileti satmış olsa da çöküyor :) Bu da 3. sorun, yani rabbitmq 'nun kendisinin bir şekilde durmuş olması. Burada ilgili kuyruğa mesajların gitmemesi gibi bir durum söz konusu değil, yanlış routing key yazıldığında nasıl çalıştığına da ayrıca bakacağız.

Burada mesajları iletmek için spring amqp bir http isteği yapıyor ve connection sağlayamazsa hata alıyor. Bunu da bir şekilde loglamak gerekiyor ama bu yetmeyecek. Gidecek olan mesajı da saklayıp tekrar göndermeniz gerekecek. Bu şekilde rabbitmq ayağa kalktığında haberinin bile olmadığı mesajları tekrar almak zorunda. Bu soruna bir çözüm önerisi getiremedim ama sanırım event sourcing gibi kavramlar bu noktalarda işin içine giriyor. Sonuçta mikroservisin 2 parçası arasında bir etkileşim gerçekleşiyor ve bunu da bir yerlere kaydedip tekrar hayata geçirebiliriz belki.

Rabbitmq konfigürasyonu yapıldıktan sonra dökümante edilmemişse ve zamanla yenilenmiş ise bundan her iki tarafın da haberinin olması lazım. Hem gönderici hem alıcı metodlar nereye mesaj gideceğini ve nereden geleceğini bilmek zorunda. Bu da ekstra bir iş yükü demek aslında. Ayrıca sistemde etkinlik iptalinde direct exchange kullanmıştık. Bunun için göndermemiz gereken routing key 'i yanlış yazdığımızda herhangi bir hata almıyoruz :) Bu da birilerinin rabbitmq 'da konfigürsayon değişikliği yaptığını söylemeyi unutması ve hatanın 3-4 gün sonra fark edilmesi ve konfigürasyon değişikliğinin unutulması ve saatlerce sorunun rabbitmq 'da aranması demek. Routing key ile kuyruğa ulaşmadığımız için deadletter kuyruğunda da göremiyoruz. Eğer yanlış anlamadıysam, mesajımız buhar olup uçuyor. Bu durumu fark edebilmek için ReturnHandler kullanılabiliyormuş sanırım ama yine mesajınızı saklamanız ve tekrar gönderebiliyor olmanız gerekiyor. Sanırım yine event sourcing.

Tabi ki bu sorunları muhtemelen rabbitmq geliştiricileri de düşünmüştür ve belki çözümleri de mevcuttur. Developer seviyesindeki bilgimle bu kadar sorgulayabiliyorum ama şu bir gerçek: Bu tool bir acayip dostum.

Proda hazır mı?

Belki bazılarınızın kafasında "ben şimdi rabbitmq 'yu alsam direkt canlıda kullanabilir miyim?" gibi bir soru vardır. Hatalarla ve olgunlaşmamış bir araçla uğraşmak istemiyorum diyebilirsiniz. 10 yıldır birileri rabbitmq üzerine çalıştığı halde web sitesinde verdikleri öreklerin altında şöyle bir not bırakmışlar:

Production [Non-]Suitability Disclaimer
Please keep in mind that this and other tutorials are, well, tutorials. They demonstrate one new concept at a time and may intentionally oversimplify some things and leave out others. For example topics such as connection management, error handling, connection recovery, concurrency and metric collection are largely omitted for the sake of brevity. Such simplified code should not be considered production ready.

Yani sisteme çok önemli bir parça ekliyorsunuz ve kendi başına çok iş gerçekleştiriyorsa, bunu alıp canlıda kullanırım diyemezsiniz. En basit işlemler için bile reliability yani güvenilirlik konularını göze almanız ve hatta bu alt başlıklara göre yeni geliştirmeler bile yapmanız gerekebilir. Aracın olgunlaşmış olması sıfır araba gibi binip gidebileceğiniz anlamına gelmiyor. Rabbitmq ile reliability kavramı ile ilgili guide olması için bu sayfaya bakabilirsiniz.

Mesajım ulaşmıştır umarım :)

Benim vermeye çalıştığım mesajlar size de bir kuyruktan mı gidiyor bilemiyorum tabi :D Bana sorarsanız dış sistemlerle etkileşime geçen asenkron işlemler dışında rabbitmq kadar büyük bir aracı kullanmayabilirim. Yani rabbitmq 'yu iyi seviyede bilecek birileri yoksa elimde, basit fonksiyonlar için çok fazla iş yükü ve risk alıyormuşum gibi geliyor. O zaman neden yazdın 5 tane koskoca blog yazısı? Hem öğrendim, hem de özümsedim. Başkaları kullanıyor diye kullanılacak birşey olmadığını fark ettim. Eğer inceliklerine hakimseniz, rabbitmq gayet başarılı bir araca benziyor. Özellikle mikroservislerde parçalar arası etkileşimleri re-define ediyor yani yeniden tanımlıyor diyebilirim. Umarım doğru anlamış ve anlatmışımdır. Mikroservis mimarisinde rabbitmq kullandığımız yazı dizisinin sonuna geldik. Bir sonraki yazıda görüşmek üzere :)


Bir yorum yazabilirsiniz