SAGA mikroservis uygulaması - 2

Bu yazıda mikroservis mimarimizi kodlamak için yardım aldığımız Spring Cloud teknolojileri nedir ve hangi araçlarını kullandık sorularına bakacağız. Serinin ilk yazısında nasıl bir servisimiz olacağına karar vermiş ve sebeplerini açıklamıştık. Şimdi sıra geldi alet edevat kısmına ve elimizi kirletmeye. Fakat önemli bir not olarak belirtmem gerekiyor. Bu yazıdan itibaren Restful web servisleri ile içli dışlı olacağız. HTTP metodları ile veri alışverişi yapan bu yapılarla ilgili temel seviyede bilgi edinmeniz konuyu daha rahat anlamanız adına gerekli olacaktır.

Mikroservislerin birbirinden bağımsız minik servisler şeklinde çalışabileceğini biliyoruz. Bu bir bakıma teknoloji bağımlılığını da ortadan kaldırabilen bir özellik aslında. Normalde bir uygulamada kullandığınız kütüphaneler o uygulamanın dili ile yazılmış olmak zorunda. Örneğin bir Java projesi ".jar" kütüphaneleri kullanabiliyor doğal olarak. Fakat birbirinden ayrı çalışan modüller ve servisler başka başka dillerle ve teknolojilerle çalışabilir. Tabi ki bir projede kullanılan teknoloji sayısı başarı göstergesi değildir eğer bilinçsiz kullanılmışsa. Bizim amacımız karmaşık olan mikroservis mimarisini özümsemek ve doğru kodlamış olmak istiyoruz. Bunun için java 'ya bağımlı kalarak bu ekosistemde kullandığımız Spring Cloud teknolojilerine yöneliyoruz. İsterlerimiz, kısıtlarımız ve hedeflerimiz doğrultusunda kullanılacak teknolojilere karar verilmesine tecrübeli bir arkadaşın yardımcı olması bu noktada tavsiye edilir tabi :) Resimimiz aşağıda dursun.

Bu resim ne ifade ediyor?

Konu mikroservisler olunca işin içine web servisler giriyor. Tasarımımız da bu servisler arasında bazı etkileşimleri zorunlu kılıyor. Bu mimariyi aşağıdan yukarı tanıtarak ilerleyeceğim. Bu şekilde anlaşılması daha kolay olacaktır. Ben karar verme aşamasında da böyle bir yaklaşım izledim ve bu kafamdaki bulutların daha kolay dağılmasını sağladı.

En alttaki 3 servis, veritabanlarına bağlanan ve basic işlevleri olan servisler. Kullanıcılar, etkinlikler ve ödeme sistemi ile ilgili işlemleri yerine getiriyorlar. Bu seviyede sepete atma, kullanıcılara hatırlatma maili atma, faturalama gibi işlevlerimiz için de servisler geliştirebilirdik. Onları da siz ekleyiverin :) Bu servisler normalde java dilinde yazıp çağıracağınız bir class 'ın projeleşmiş hali aslında. Gidip veritabanına ulaşan (her birisi ayrı ayrı veritabanları olabilir, bunda hiçbir engel yok) ve sonuçları döndüren minik minik metodlar barındırırlar. Özellikle minik diyorum çünkü bu servislerde iş kurallarına ve domaine ait kontroller bulunmuyor.

Örnek: Kullanıcı servisi giriş yapıldığında kullanıcının bütün bilgisini alıp döndürmüyor başka bir endpoint ile kullanıcı bilgileri alınıyor. Veya ödeme işleminde koltuk kontrolü yapılmıyor. O ayrı bir metod. Yani biraz da single responsibility kavramına yakınsıyoruz. Bu şekilde hem geliştirilmesi hem de bakımı kolay oluyor.

Ayrıca her birinde "+ failover" ifadesi geçiyor. Bu da bu minik servisin ayakta olmaması durumunda başvurulacak servisi ifade ediyor. Burada kastedilen yedek servis değil ! "Lütfen bekleyiniz" servisi aslında. "Kusura bakmayın kullanıcılara erişemiyoruz" diyebilmek için ayakta tutulan servis. Çünkü kullanıcı servisi düşerse veya güncelleme yapılırsa bir süre erişilemeyebilir. Bu durumda erişilen bir afet bilgi merkezi gibi failover 'lar da bulunmaktadır. Bu servisin yedeğini projeyi çalıştırırken 2 kere ayağa kaldırarak oluşturacağız serinin 6. yazısında.

Eureka

Küçük servislerin üstündeki Eureka, kullanacağımız spring cloud teknolojilerinden ilki oluyor. Spring dediğimiz ise bir framework yani belli bir kütüphaneler bütünü. Spring boot ise bu framework 'ü kullanan proje altyapısı. Bunun mikroservis ile bir alakası yok henüz. Normalde java ile sürekli tekrar tekrar yazdığımız bazı kodları yazmaya gerek kalmadan ve hatta belli başlı kütüphaneleri ara bul projeye ekle derdine düşmeden geliştirme yapabilmemizi sağlıyor. Spring boot projesi olarak bir web servis oluşturabiliyorsunuz. Bu yazıda spring boot ile temel seviyede bir proje yapmıştım.

Eureka bir kayıt ve yük dengeleme servisidir. Registry ve load balance işlemleri yapıyor. Sistemde kullanıcılar ile ilgili bir servisin hayatta olduğunu bilmemizi ve bu servisten birden fazla örneğin ayakta olması durumunda, bunlar arasında yükü dengelemeyi sağlıyor. Bir web projesi gibi ayağa kalkıyor ve resimde aşağıdaki 3 servisin hayatta olduğundan haberdar oluyor. Ayrıca 2 adet kullanıcı servisi hali hazırda bekliyorsa gelen istekleri sırası ile dağıtıyor.

Gateway

Eureka 'dan önce gelen gateway ise sistemin giriş kapısı görevini görüyor. Bu mikroservis sisteminde direkt olarak kullanıcı servislerine erişmiyoruz. Bu servislerin önünde Eureka koyduk. Fakat kullanıcı servisleri ayakta olmazsa failover servislere nasıl yönlendireceğiz? Bu özelliğe "circuit breaking" deniyor ve Eureka bize bunu sağlamıyor. Bu yüzden route işlemi yapabilmek için Spring Cloud Gateway kullanıyoruz. Bu gateway kendisine gelen istekleri ne ile alakalı olduğuna bakarak Eureka 'ya iletiyor. O da load balance ile servisleri çağırılmasını sağlıyor. Bu projede "user servisine ulaşamazsan git failover 'dan cevap al" gibi bir yönlendirme yapıyoruz. Failover nerede diye sorarsanız onu da Eureka biliyor registry sayesinde. Netflix tarafından geliştirilen Zuul projesi gateway, Hystrix projesi ise circuit breaking görevi görebiliyordu. Fakat bu projeler Spring Cloud bünyesine geçtiği için Spring Cloud Gateway kullanarak iki işlevi birden yerine getirebiliyoruz.

Web uygulaması

En üstteki uygulama ise web MVC uygulamamız ve koordinatörümüz. Ekrandan girip bilet aldığınız uygulama bu uygulamadır. Kullanıcı girişi butonuna tıklandığında gateway 'e isteği gönderir. Gateway bunun kullanıcı işlemi olduğunu bildiği için Eureka 'ya "bu requesti kullanıcı servisine gönder" şeklinde emir verir. Eureka da kullanıcı servisinin kim olduğunu ve nerede olduğunu bildiği için ona yönlendirebilir. Sonuçta MVC uygulaması dönen cevabı alır ve cevaba göre işleyişi belirler. Başarılı giriş yapıldığında kullanıcı bilgilerini almak için tekrar gateway 'e istek gönderir. Ve böyle böyle işleri koordine etmiş olur.

MVC uygulaması bambaşka bir mobil uygulama da olabilirdi. Burada da bir kısıtımız bulunmuyor aslında. Sadece başı ve sonu olan her bir hikayenin, yani saga'nın, nasıl işletileceğini bilmemiz gerekiyor. Bunu bildikten sonra şu servisi çağır sonra diğer servisten şu işlemi çağır gibi kodları yazmamız yetiyor. Bir önceki yazıda bu kodları nereye yazacağım sorusu vardı ise şimdi bu biraz oturmuştur umarım. İlerleyen yazılarda bu kodları da açıklamaları ile yazacağım.

Config Server

Son olarak orada bir configserver var. Bunu şimdilik anayasa gibi tanımlayayım. Spring Cloud teknolojileri içerisinde Config Server isimli bir proje var. Herkesin bilmesi gereken bilgileri bir merkezde toplayabiliyorsunuz bu sayede. Bütün küçük servisler (eureka ve gateway dahil) bazı bilgileri merkezi bir konfigürasyon servisinden alabiliyor. Bu da bir gün veritabanı bağlantısı gibi bir ayarın değişmesi durumunda uygulamayı deploy etmeden ayarları değiştirebilmemizi sağlayabiliyor.

Spoiler olarak vereyim. Elimizde 10 adet spring boot projemiz olacak. 3 minik servis + 3 failover + eureka + gateway + config server + MVC uygulaması. Bu yapıyı yani mimariyi ne yapmak istediğimizi bilerek tasarladık. Fakat bu tool 'lardan haberimiz olmasaydı veya hangisini neden kullanacağımızı bilmiyor olsaydık nasıl olurdu? Bunu biraz inceleyelim. Farz edin kafanızda monolitik olarak yazılmış bir projeyi bölmeye çalışıyorsunuz. Çünkü öncesinde dağıtık bir sistem tasarımı yapmadınız hiç. Bu durumda kullanıcı servisine bir istek gönderip oradan belki de event servisine iletilmesini oradan da ödeme servisine iletilmesini düşünebilirsiniz. Her birisinin bağımsız çalışacağını düşünürsek bu gayet olası bir tasarım. Burada şu soru çıkıyor karşınıza: 1 request karşısında 1 response beklenir teknik olarak. Benim response 'um nereden gelecek ve bunu nasıl bileceğim sistemde? Sistemde birşeyler yanlış gider ve failover 'a yönlendirirse ne olacak? Veya bir try catch yapısı gibi bir exception oluşması durumunda requestin gidişatı değişirse?

O işler öyle olmuyor :)

Size saçma gelebilir bu soru ama internetteki örnek kodlara ve udemy 'de yapılan projelere bakarsanız bu sorunun cevabı genelde yok. Verilen örneklerde çoğunlukla mesaj tabanlı servisler geliştiriliyor. Bizim sistemimizden çok farklı. Merkezde laf taşıyıcı görevi gören bir message queue yapısı oluyor ve her bir servis bu taşıyıcıdan gelecek haberi bekliyor. Mediator pattern gibi. Bu durumda client uygulaması da bir işlem yaptığında genelde reaktif bir servisi çağırıyor. Mesaj taşıyıcıdan cevap geldiğinde reaktif servis "tamamdır işim bitti" diyerek bildirim yapabiliyor. Bu "bana içerden falanca kişiyi çağır" emri gibi bir bakıma. Herkes birbirine "falancayı gördün mü" diye soruyor ve sonunda o kişi geliyor. Sistemimiz bu kadar karmaşık değil. Bu yüzden internetteki messsage queue bazlı yaklaşımlar işimize yaramıyor.

Ayrıca şu da var: Bazı örnek uygulamalar sadece konsola mesaj yazdırıyor ve buna iş gereksinimi tamamdır şeklinde onay verip geçebiliyor. Yani aslında gerçek bir sisteme oturtulamayacak kodlar olabiliyor. Sadece "bak bunu böyle yazarsan bu da böyle çalışır" diyerek geçiştirenler var. Bu yüzden ben de küçük servislerin birbirini çağıracağını düşünerek ilk hatayı yaptım. Bu tasarımı gerçekleştirebilmem zaten mikroservis teknolojilerini bilmeden imkansız olacaktı.

Sonra servislerin gateway üzerinden haberleşmesi gerektiğine kanaat getirdim fakat bu şekilde de servisler iş akışları ile şişecekti. Ve yine MVC uygulamasının bir şekilde gelecek cevabı dinliyor veya bekliyor olması gerekecekti. Bunun üzerine bütün iş mantığını gateway üzerinde routing 'lerle yapmayı düşündüm. Bu şekilde minik servisler gateway 'e "şu işi yapabilir misin" gibi istekler gönderebilirdi, döngüsel bir yapı gibi. Bu durumda da koordinatör olarak routing yazmanın çok karmaşık olacağını fark ettim. Gateway sadece güvenlik ve yönlendirme amaçlı kullanılmalı.

Sonunda bloglardaki meşhur koordinatörü MVC uygulaması içerisinde business akışlarına ve sagalara çevirmeye karar verebildim. Belki de cahilce başladığım için süreç böyle uzadı. Belki de şu anda verdiğim kararlar da çok yerinde olmayabilir. Yaptığım uygulamalar veya çözümler optimal veya ideal olmayabilir. Bunların hepsi mikroservislerin kişiye ve koşullara göre değişebilen avantaj ve dezavantajlarından kaynaklanıyor. Bu konudaki yorumlarınızı da aşağıda beklerim tabi :)

Tamam söz, bir sonraki yazıda kod yazacağız

Önemli not: Şu aşamada temel Java, REST Api, Spring Framework ve Spring Boot, Maven ve Github, bir Java IDE 'si kullanma tecrübenizin olması gerekiyor. Ayrıca bilgisayarınızda Java 11 'in bulunması ve Maven environment variable bilgilerinin hazır olması da gerekiyor. Bir de Mysql sunucusu ve veritabanı kurulu olması gerekecek. Evet bunlar 2 yıllık bir junior developer seviyesinde bilinebilecek bilgiler ama zaten iş hayatına yeni giriyorsanız bu konularda bilgi sahibi olmadan direkt olarak mikroservislerden girmeniz pek mantıklı olmayacaktır.

Bir sonraki yazı Eureka, Gateway ve Config Server konusunda detaylı bilgileri içerecek. Şu veya bu şekilde mimari tasarımı çıkardık. Teknolojilere karar verdik. Artık kendi bilgisayarınızda ayağa kaldırabileceğiniz kodları yazabiliriz. Projenin bütün kodlarını (config repository hariç) Github adresinde bulabilirsiniz fakat çalıştırmanız için bazı konfigürasyonlar da gerekecek. Bu yüzden yazı dizisi ile beraber takip ederek ayağa kaldırmanızı tavsiye ederim. Ben öğrenme aşamasında parça parça ilerlediğim için hatalarım olmuştu ama sizin olmayacaktır diye düşünüyorum. Bu hatalardan bahsetmeye devam edeceğim. Bir sonraki yazıda görüşmek üzere :)


Bir yorum yazabilirsiniz