Mikroservis mimarisine geçmek cesaret işidir.

“Artık monolithic uygulamalar devri geçti, mikroservis yapısına geçmeliyiz.”

Netflix, mikroservis yapısına geçmek istediğinde sene 2008 idi. Tamamıyla mikroservis yapısına geçtiğinde ise sene 2016…Şirketin yönetici takımının “başarısızlık” olarak etiketleyebileceği, mühendislik ekibinin yöneticilerini yargılayabileceği, cezalandırabileceği kadar uzun bir süre.

Fakat, Netflix’te çalışan kişilerin anlattıklarına göre şirket, bu süreyi başarısızlık olarak değerlendirmemişti çünkü 2008 yılında yaşadıkları veritabanı sıkıntısı yüzünden 4 gün boyunca hizmet verememeleri, onları çok ciddi şekilde önlem almaya motive etmişti.

Koskoca sistem, tek bir veritabanının eline bakıyordu ve bu veritabanındaki bir sıkıntı, sistemi tamamen alaşağı edebiliyordu. Bu tarz bir “single point of failure”, bu ölçekteki bir sistem için yüzkarasıdır. Aşil tendonu nasıl ki Achilles için bir yüz karasıysa…

“Artık monolithic uygulamalar devri geçti, mikroservis yapısına geçmeliyiz.” sözünü söyleyen bir şirket, gerçekten böyle bir sıkıntıyı hesaplayarak, ne yaptığını bilerek mikroservis yapısına geçmek istiyorsa gerçekten müthiş bir iş yapıyor. Fakat, sırf trende uymak için mikroservislere geçmek istiyorsa ve mikroservislerle en baştan yazmak için, monolithic uygulamalarını yazdıkları kadar bir süre gerekeceğini düşünüyorlarsa büyük hata içindeler.

Çünkü, mikroservise geçmek aşağıdaki üç özellik arasında seçim yapmak, denge kurmak demek: Yenilikçilik, sistemin sağlamlığı, kod geliştirme sürecinin ve üretim ortamının ne kadar etkili çalıştığı…

Yenilikçilik, sistemin hatasız çalışması ve kısa zamanda büyük geliştirmeler yapmak arasında seçim yapmalısınız. Ortadaki üçgenin alanını sahip olunan yazılım ekibi olarak düşünüp doğru seçimi yapmalısınız. Mikroservis mimarisine geçilecekse, verimlilik(efficiency) tarafına pek gidilemez, sistem mutlaka monolithe göre daha yavaş olacaktır.

Mikroservise geçiş bir zihniyet değişimini, hatta organizasyonel değişimi beraberinde getirir. Örneğin, mikroservislere geçince artık, dağıtık transaction yönetimini bir servise bırakamazsınız, kullanıcının oturum yönetimini, sunucuların “sticky sessionları” sayesinde yapamazsınız, bir RDBMS kullanıyorsanız, çoğu sorgunuzu değiştirmek zorunda kalırsınız çünkü tabloları ve şemaları mikroservis bazında ayırcağınızdan tablo joinlerini istediğiniz gibi yapamazsınız. Açıkçası, kaybedeceğiniz şey sadece join sorguları da olmayabilir, RDBMS’ten tamamen vazgeçmek zorunda da kalabilirsiniz. Mesela Netflix RDBMS’ten Cassandra’ya geçti. Çünkü, mikroservis bazında ayrılan NoSQL dökümanlarını otomatik olarak replike etmeleri gerekiyordu. Sizin de etmeniz gerekecek haliyle…

Asenkron yapılar içindeki orkestrasyonu sağlayabilmek için yeni teknolojileri entegre etmeli, eskilerini atmalı, test süreçlerine en baştan başlamalısınız. Bütün birim ve entegrasyon testlerini en baştan yazmalısınız. Hatta test mantığınızı değiştirmeli, bağlantı sorunları gibi fiziksel hataları da hesaba katarak test yapmaya başlamalısınız, Netflix’in Chaos Monkey’si gibi otomatize testleri kullanmaya başlamalısınız.

Artık yukarı ölçeklendirme yapamazsınız, yani, sunucunun CPUsu tavana vurduğunda 8 yerine 16 çekirdekli CPU koyalım olsun bitsin diyemezsiniz. Yatay ölçeklendirme kafasına girmeli, buna göre sanallaştırma mimarileri kullanmalısınız. Klasik load balancer’ınızı ise elastik bir load balancer ile değiştirmelisiniz ki, servislerden biri aşırı yüklenildiğinde, metrikleri toplayarak yeni instancelar yaratılabilsin ve trafik onlara yöneltilebilsin ya da gereğinden fazla instance varsa yine metrikler toplanarak fark edilsin ve kapatılsın. Çünkü her instance para yazıyor, fatura kabartıyor.

Bunların üzerine, bir süre, hem eski sistemin ihtiyaçlarını karşılamaya devam etmeli, hatalarını çözmeli, yeni geliştirmeleri yapmalısınız hem de yeni sistemi geliştirmeye devam etmelisiniz. İkisi farklı zihniyetler gerektirdiği için birinden diğerine gidip gelebilmelisiniz. Bu gidip gelmeler aşikar bir şekilde zaman kaybettirecektir. Aşağıdaki gibi konforlu hissedebilecek bir yazılım ekibine sahip olmak şart olacak bu durumda.

Otomatik test araçları, esnek load balancerlarlar, sanallaştırma mimarilerinin kurulumu, bunların güvenliğinin sağlanması derken çok fazla dev-ops işi çıkıyor ki çoğu geliştirici bunu sevmez. Bunun için bir bulut sistemine geçmek gerekiyor. Sistemlerinizi alıp AWS gibi bir platforma geçmek çok akıllıca oluyor. Tabi sisteminizi taşıma maliyetlerini de işin içine katmak gerekecek.

Kısaca, “mikroservis kullanan bir sistem”e dönüşmek, korkunç faturası olan bir süreç…

Deneysel olarak mikroservislere geçiş

Bir girişimimiz için küçük çapta bir mikroservis mimarisine geçmeye çalıştık. Dev-ops işlerine çok fazla yoğunlaşmadan, yukarıda bahsedilen geçiş aşamasını birebir tecrübe edebilmek amacıyla senkron servislerden oluşan monolith uygulamamızı mikroservise çevirdik.

Biliyorum, monolith artık bir aşağılama sözcüğü gibi kullanılıyor ama bir yazılım geliştirirken monolith yapıdan vazgeçmek de ilk tercihimiz değil. Bu çelişkiyi aşıp, cesaret edip bu işe giriştik.

Şu anki geleneksel yapımız, bizim büyük çaresizliğimiz

Şu anki yapımızda birden fazla WAR dosyamız var. Bir tanesi telefonlara push gönderiyor, bir tanesi podcast dinleme istatistiklerini tutuyor. Bir tanesi uygulama içi satın almaları yönetiyor. Bir diğeri de kullanıcı yönetiminden sorumlu. Böyle yapmamızın sebebi, olur ya, yarın bir gün tek başına kullanırız, mesela uygulama içi satın alma yöneticisini ayrı olarak satarız ya da başka bir girişime entegre ederiz diye düşünmekti.

Bütün WAR’larımız tek bir DB’ye bakıyor. WAR dosyalarının her biri 8–10 tane REST endpoint açıyor. Uygulamalar birbirlerine REST endpoint üzerinden istek atarak senkron olarak haberleşiyor. Basit bir akış için herkes birbirini bekliyor ve tek bir noktadaki tıkanma bütün sisteme yayılıyor. Yani hem DB üzerinde hem diğer tüm entegrasyon noktalarında “single point of failure” riskimiz var.

Kullanıcı oturumları, Weblogic üzerindeki sticky sessionlar ile sunucular arasında kopyalanıyor.

Yani, basitçe, ilkel bir SOA mimarimiz var. Bunu mikroservise çevirebilmek için şunları yapmamız gerekiyor:

Çözmemiz gereken sorunlar

1- Yalnızca, mobil uygulamalar gibi gerçek istemcilerin (yani kendi sistemimiz içindeki alışverişler hariç) HTTP üzerinden HTTP istekleri atmalarını sağlamak.

2- Servisler arasındaki isteklerin asenkron olarak atılmasını sağlamak. Bunun için bloklayıcı IO içeren mesaj kuyrukları olan JMS de kullanabiliriz, bloklamayan IO içeren Akka’nın aktör yapısını da.

Biz JMS (ActiveMQ) kullandık ve kullandığımız sürece aklımıza takılan soru şu idi: JMS kuyruklarını yöneten servis ölürse ne olacak? Bu da single point of failure değil mi?

Biz önce JMS yapısını seçtik fakat zamanla Akka aktörlerine geçiş yaptık. İlerleyen satırlarda anlatacağım.

3- Her servisin tablolarını ayırıp sorguları da buna göre yeniden yazmak. Basit bir joinli sorgu için birden fazla servise istek atıp bunları birleştirmek zorunda kalacağız.

4- Her mikroservisin datasını diğerlerinden izole ettiğimiz zaman transaction bütünlüğünü tekrar sağlamamız gerekecek. Gerçi zaman içerisinde transactionlar hakkındaki fikirlerimiz de değişti.

5- Her servisin örnek (instance) sayısını dinamik olarak kontrol etmek için birden fazla sunucu kullanmak ve gerekirse her sunucu üzerinde sanallaştırma yaparak birden fazla sunucu ayağa kaldırmak ve CPU’yu mümkün olduğunca etkili ve yoğun kullanmak. (Zor bir dev-ops işi)

6- Varolan servisleri biraz daha parçalamak ve her biri için yeni bir repository yaratmak. Bu kadar küçük ölçekteki bir proje için bile 10–12 tane repository yaratmış olmak içimize hiç sinmeyecekti. Gerçekten tuhaf bir durum. Bunu, Uber’de de yaşamışlar. Toplamda 8000 adet repository açılmış ki inanılmaz bir sayı. (Şuradan bakabilirsiniz: https://www.youtube.com/watch?v=kb-m2fasdDY )

7- Geliştiricilerin hepsi DB katmanından devops işi olan deployment, ölçeklendirme aşamalarına kadar her şeyi öğrenmeliydi. Daha önce Docker’ı bile efektif kullanmayan kişiler olarak zorlanacaktık.

Linkedin’den Heiko Seeberger’ın dediği gibi, mikroservis kafasına girmek demek, organizasyon şemasını da değiştirmek demekti. Artık farklı takımlardaki geliştiriciler tek bir WAR paketi oluşturup, ardından teste gönderip teste gönerdikten sonra deployment için kalite ya da devops ekibine göndermeyecekti. Geliştiriciler bizzat her aşamada bulunacaktı. (https://www.youtube.com/watch?v=nL4XoH2_Lew)

#direndeveloper

Biz ise hala “sunucu” kafasındaydık. WAR dosyasını al, bir uygulama sunucusuna yükle (deployment), ölçeklendirmek istediğinde ya aynı makinaya başka portta çalışan başka bir sunucu kur ya da başka makinada yeni bir sunucuya aynı WAR’u yükle… Yeni WAR geldiğinde git bütün sunucularda tek tek değiştir. Ve bunu kendin yapma, bilen birisi yapsın. “Ben geliştiriciyim abi, ne işim olur deploymentla load balancerla” de sıyrıl…

Ama mikroservis mimarisine geçmiş bir takımda işler artık böyle yürümüyordu. Takım baştan sona, mikroservisin her şeyiyle ilgilenmek zorundaydı ve her aşamasından haberdar olmalıydı. Bu yüzden değişime de direniyorduk. Hem değişmek istiyorduk hem direniyorduk. Gerçekten ilginç bir dinamik…

En solda, geliştiriciler her aşamadan sorumludur. Ortada, her takım, kendi mikroservisini geliştirmeli ve her aşamasını kendi halletmelidir. Takımlar uçtanuca her şeyi kendi başına halletmelidir. En sağda, geliştirici, kodunu repository’e koyduktan sonra buglar kendisini dönene kadar sorumluluk sahibi değildir. Bugları çözüp paketi verdikten sonra da sorumlu değildir.

Conway’ın kanunu:

Bu kanun diyor ki:

Topluluğun yönetim şeması ve organizasyon yapısı ne ise, ortaya çıkarttığı yazılım da aynı yapıda olur. Çok farklı bir mimari kullanmaya çalışıyorsa, eninde sonunda mimariyi, kendi organizasyon yapısına uyduracak şekilde eğip büker.

Biz, freelancer üzerinden çalıştığımız geliştiricilerin aslında her şeyden haberdar olmasını istemiyorduk. Fakat mikroservis geliştirmek için gerekli JMS kuyruğunun düzgün çalıştığı sandbox ortamını kuramamıştık. Ayrıca, docker örnekleri üzerinde çalışan DBlerin güvenliklerini ve izolasyonunu da tam sağlayamamıştık. Bu yüzden, HTTP üzerinden diğer servislere istek atmaya devam etmelerini istemiştik. Dolayısıyla mikroservis mimarisini büktük ve bu noktada başarısız olduğumuzu itiraf etmek isterim.

Sanallaştırma

Bir mikroservisin mikroservis olması için şart olmasa da, eğer bir makina üzerinde birden fazla mikroservis yüklenecekse mutlaka kullanmamız gereken bir teknoloji sanallaştırma teknolojisi.

Çünkü mikroservisin tanımında şu var:

“Bir mikroservisin kendisine ait bir yaşam döngüsü olmalı; sadece bu mikroservisi etkileyecek şekilde uygulamayı başlatıp durdurabilmeliyiz, güvenlik protokolleri, diğerlerini etkilemeden değiştirilebilmeli, DBsi diğerlerini etkilemeden yedeklenebilmeli, dağıtılabilmelidir…”

Dolayısıyla birden fazla docker imajına farklı docker imajlarımızı yükledik, statik olarak IP’lerini belirledik ve öyle kaldı. Bir sonraki aşama olan “servis bulma” işlerini kendi başımıza yapamadık. yani hem gerektiğinde açılıp kapanan sanal makina yapısını kuramadık, kursak bile yeni kurulan sanal makinalar için route tanımlamalarını yapamadık. Yatay dinamik ölçeklendirme, sanallaştırma ve servis keşfi işlemlerini kendimiz yapamadığımız için işi ehline bıraktık ve EC2'ye geçtik.

Veritabanlarını ayırmak

Kural şu: Mikroservisler birbirlerinden tamamen izole olmalıdır. Buna DBler de dahildir. Yani hiçbir mikroservis, başkasının DBsine direk istek atamamalı, bunun yerine servisten istemelidir.

Tablolaları ve şemaları mikroservis bazında ayırınca, tabi, joinli sorgularımız çalışmaz oldu. Bu yüzden sorguları daha atomik hale getirdik. Bu sefer haliyle, bir join ile çözülebilecek işler için defalarca, farklı farklı mikroservislere event atıp istekte bulunmak zorunda kaldık. Örneğin, hiçbir podcasti 1 aydır açmamış kişilere push atmak istiyoruz. Normalde yapacağımız analitik tabloları, kullanıcı tablosu ve cihaz tablosunu joinleyip basit bir sorgu atmak. Gelen cevabı push mikroservisine göndermek…

Mikroservis yapısı yüzünden bu kadar kolay olmuyor.

Önce kullanıcı istatistiklerini almak için podcast dinleme analizleri servisine istek atıyoruz. (Analitik servislerini de bounded context mantığıyla önce birleştirip sonra çok büyüdüğü için tekrar ayırmıştık).

Elimize 10bin tane kullanıcı geliyor. Bu kullanıcılar, ORM kullandığımız için baya baya, tüm bilgileri dolu bir kullanıcı nesnesi. Sadece ID’lerini kullanmamız yetmesine rağmen bu kadar gereksiz bilgi çektik ilk önce. Sonra gittik, daha rafine bir cevap olsun diye sadece IDleri dönen başka bir servis yazdık.

Aynı şeyi yapan fakat farklı cevap yapıları dönen servisler yazmaya başlamıştık ki neredeyse her işlem için bunu tekrarlamaya başladık. Bunları bir de REST endpointi olarak açmaya başladığımızda URL yapıları da saçmalaşmaya, standarttan uzaklaşmaya başladı.

1obin kullanıcı IDsini aldıktan sonra bunları cihaz mikroservisine atıyoruz ve kullanıcıların sahip oldukları cihazların IDlerini çekiyoruz. Daha sonra bu idleri push mikroservisine atıyoruz. Tabi hiçbirini direkt olarak atmıyoruz, JMS kuyruğuna aktarıyoruz.

İşi göz göre göre yavaşlatıyoruz yani.

Bu noktada, kendimizi trendlere kaptırıp yanlış bir yola girip girmediğimiz sorgulamaya başladık. İlk grafikteki üçgeni hatırlıyor musunuz? İşte oradaki “efficiency” alanımız maalesef merkeze çok yaklaşmıştı.

Dağıtık transactionlar

Ayarladığımız RDBMS DB transactionları da manasını yitirmeye başladı.

Diyelim ki bir kullanıcının kullanıcı bilgisini, cihaz bilgisini ve şimdiki konumunu kaydetmek istiyoruz. Bu transactional bir işlem bizim için. Biri olmazsa işimiz görülmüyor. Biz, monolith uygulamamızda bu transaction yapısını gayet rahat kurabiliyorduk. Fakat şimdi, birbirinden bağımsız veritabanları üzerinde bunu yapmak zorundaydık. E bir Oracle RAC clusterımız yok ki dağıtık transaction işini hallediverelim.

Sonra bir toplantımızda şunu sorduk kendimize: “Abi, biz RDBMS kullanıyoruz ama normalde insanlar zaten NoSQL veritabanı kullanmıyor mu? Onlar bu transaction işini nasıl hallediyor?”

Araştırıp bulabildiğimize göre, mikroservise döndüysek, bildiğimiz manadaki dağıtık transactionları unutmamız gerekiyor.

“Ya hep ya hiç” transaction yapısı o kadar da elzem bir şey değil. Transaction’ın ne olduğu anlatılırken kullanılan “havale” örneği var ya, aslında o bile öyle işlemiyor. Paranızın gerçekten sahipsiz kaldığı bir zaman dilimi var. Mesajlaşmalar doğru giderse hedef hesaptaki para arttırılıyor, bir şeyler ters giderse para kaynak hesaba iade ediliyor.

Ve, bir transaction %60 başarılı %40 başarısız da olabilir. Yeter ki, ilerideki bir zamanda kalan o yüzde 40 da tamamlanabilsin. Event sourcing yaparak bu tipte bir transaction yapısı kurabiliyoruz. İleride bahsedeceğim gibi, bunu Akka Persistence kullanarak hallettik.

Birleşik Bağlam (Bounded Context)

Yukarıda bahsettiğimiz gibi, 10bin tane IDyi değiştire değiştire oradan oraya taşıdık. Sistem belki biraz daha güvenilir oldu ama kesinlikle daha yavaş oldu.

Bunu çok kişi yaşadığı için bounded-context mantığı mikroservis mimarisinde de kullanılıyor. Biz de kullandık. Teknik düzeyde ayırdığımız mikroservislerimizi iş mantığı seviyesinde tekrar tek parça haline getiriyoruz. İşte bu noktada çok dikkatli davranmak gerekiyor. Başladığımız noktaya geri dönerek her şeyi tek parça haline getirebiliriz. Mutlaka bir noktada durmamız gerekiyor. (Building Microservices — Sam Newman)

Bounded contextleri yöneten bir de yönetici olmak zorunda ki, atılacak isteklerini sırasını, kimin kimden gelecek veriyi kullanacağını belirlesin.

İş icabı bir araya gelmiş mikroservisler, yapmaya çalıştığımız şeyi bozmaya başlıyor. Bir yerden sonra hangi mikroservis, hangi bağlamın içindeydi, neyi kullanıyordu, bu küçük ölçekli projede bile karıştırmaya başlamıştık. Şunun gibi bir iletişim ağımız vardı:

Resim şuradan: https://articles.microservices.com/an-alternative-way-of-visualizing-microservice-architecture-837cbee575c1#.exdnpysg5

Mikroservisler arasındaki iletişimde kullanılacak nesneler

Bir servisten gelen cevabı deserialize edebilmek için bir sınıfa ihtiyacımız var. Bu biraz da, derlenen dillerin sıkıntısı sanırım. Dinamik sınıf yapısı olmadığı için…

Aşikar ki eğer mikroservis-2, mikroservis-1'in cevabını kullanacaksa, mikroservis-1 bağlamındaki cevap sınıfının, mikroservis-2 içerisinde de bulunması gerekiyor. (Json’dan nesneye çevrilirken ya da JMS mesajı deserialize edilirken)

Dolayısıyla ya aynı sınıfı ikisine de koyacağız ya da bu sınıfları ayrı bir modül içerisinde tanımlayarak ilgili mikroservislere enjekte edeceğiz.

Peki hangi nesne yapılarının hangi mikroservislerde kullanılacağını nasıl öngöreceğiz? Bunu öngörmek kolay olmadığı için, mikroservisler arasında taşınacak nesne yapılarının hepsini bir modüle koyup, bütün mikroservisler içerisine bu modülümüzü bağlıyoruz.

Oyy oyy oyy…

Yani, gerekli gereksiz bütün sınıflar bütün mikroservislerin kod tabanında dolaşmaya başlıyor. Daha da kötüsü, ortak kullanılan modüldeki bir nesne yapısını değiştirdiğimiz zaman gidip bütün mikro-servisleri derleyip yeniden başlatmamız gerekiyor ki değişiklikler uygulansın. Nerede kaldı mikroservislerini otonomluğu? Kalmadı.

Mikroservisler arası asenkron iletişim

Mikroservislerimiz birbirlerine istek atıyor, çünkü başka servislerdeki verilere ihtiyaçları var. Eski yapımızda servisler cevapları birbirlerinden senkron olarak bekliyor. Yani diğerinden veri gelene kadar bloklanıyor. “Bu oluyorsa monolithic yapıdan neden çıktık ki” diye soruyoruz kendimize. Spring MVC ve Servlet 2.0 kafasıyla yazdığımız end-pointler haliyle bloklayıcı bir yapıda…

Mikroservisler arasındaki iletişimi senkronize bir protokol olan HTTP üzerinden yapmak bir antipatterndir. (Jonas Boner Typesafe CTOsu)

Bu sorundan kurtulmak için Java içerisinde Future, CompletableFuture, Promise gibi sınıflar zaten çoktan eklendi. Bunlarla asenkron iletişim sağlayabilirdik fakat Java’nın callback yapısı sentaks olarak gerçekten çok sıkıntılı olduğu için ortalığı cehenneme çeviriyordu.

JMS’e geçmek bu sorunu çözer fakat eninde sonunda bir noktada senkronizasyon gerekeceği için ekstra iş çıkacak.

Servislerin orkestrasyonu ve Akka

Bu noktada artık Akka’ya geçmek elzem oluyor. Bir yönetici, bizim için orkestrasyonu sağlayacak. Biz ise orkestrasyon kodunu bir fonksiyon içinde yazacağız ve kalan her şeyi Akka’nın aktörleri halledecek. İşlemlerden biri başarısız olursa yeniden deneyecek, orkestratör aktör ölürse, yeniden ayağa kalktığında kaldığı yerden devam edecek.

Reaktif manifestoyu defalarca okumuş olmamıza, içinde onlarca kez resilyant, risponsif, elastik kelimeleri geçen makaleler okumuş olmamıza rağmen reaktif programlama için harika bir kütüphane olan Akka’nın güzelliğini, yavaş yavaş, sorular sorarak, bazen cevap bulamayarak anlamak, Akka’ya karşı bir hayranlık beslememize neden olmuştu.

Akka HTTP

Akka HTTP kullanarak mikro-servislerin de birbirleriyle asenkron olarak haberleşmesini sağlamaya karar verdik. Burada bir diğer kafa karışıklığı yaşadık. JMS sistemimiz (ActiveMQ) ne olacaktı?

ActiveMQ sayesinde event-based bir sistemimiz vardı. Akka ile ActiveMQ birbirlerinin yaptıkları işi de yapmış oluyorlar mıydı yoksa birbirlerini mi tamamlıyorlardı?

Akka aktörleri ile bir JMS implementasyonunun benzedikleri tek nokta şu:

İkisi de başka aktörler ile (JMS için üretici ve tüketici) asenkron haberleşir ve bunun için “mesaj” mekanizması kullanır. Bu kadar.

Dolayısıyla tek derdimiz asenkron olarak haberleşmek ise birbirlerinin yerine kullanılabilirler. Fakat başka dertleriniz varsa birbirlerini tamamlamaya başlarlar. Çünkü birbirlerine “üstün” oldukları, birbirlerini tamamladıkları noktalar da mevcut…

Aktörlerin en güzel yanı, bir süpervizör sayesinde yönetilebiliyor olması. Eğer aktörlerden biri ölürse, yeni bir aktör ayağa kaldırılarak, ya da varolanlardan bir başkası yeniden başlatılarak yapılacak iş ona devredilebiliyor. Aktörler bir süpervizörün güdümüyle birlikte çalışabilecek hale getirilebiliyor.

JMS’in güzelliği ise mesajların dosya sistemine yazılması sayesinde, mesajları, bütün sistemler kapatılıp yeniden başlatıldıktan sonra bile işlenebilir hale getirmesi. Bir diğer güzelliği ise, kendi sistemimize, dışarıdan da mesaj gönderilmesini sağlayarak infrastructure’ımızı genişletebilecek olmamız.

JMS’in artı tarafları bizi çektiği için, JMS yapısını aktör yapısıyla tamamen yer değiştirmedik. Fakat zaman içinde, JMS’in artıları diye gördüğümüz noktaların da işimize yaramadığını görerek yavaş yavaş tamamen aktör yapısına döndük. (Başka projelerde böyle bir süreç olacak diye bir kural yok tabii ki)

Akka Persistence

Akka artık “Persistence” modülüne sahip ki sonradan tanıyıp aşık olduğum bir modül diyebilirim. Kaydedilen “event”leri, yani geleneksel yapıda transaction’ın bir parçası olarak tarif edebileceğimiz CRUD işlemlerini başlamadan evvel bir jurnale yazılıyor. Herhangi bir adımda başarısız olunursa ya da aktör ölürse, aktör ayağa tekrar kalktığında, başarız olunan andaki hale dönmek için başarılı eventler tekrar oynatılıyor ve kalan eventler olduğu gibi işlenmeye devam ediliyor. Bu, hem JMS’in “durable” mesajlarının yerine kullanılabiliyor hem de dağıtık transaction yerine…

Cassandra’yı transaction jurnalimiz olarak kullanarak bu yeni transaction mantığına geçiş yapmaya başladık ve reaktif bir mikroservis yazma serüveninde adım adım ilerliyorduk. Açıkçası bu kısım, teknik olarak en çok zorlandığımız kısım oldu çünkü ciddi bir zihniyet değişimi yaşadık.

Sonraki yazıda, biraz daha teknik detaya girmeyi düşünüyorum. Bu yazının amacı yaşadığımız zihin değişikliklerini ve çelişkileri anlatmaktı.


Takipte kalmak için www.twitter.com/talhaocakci