Mikroservisler Mimarisi
The term “Microservice Architecture” has sprung up over the last few years to describe a particular way of designing software applications as suites of independently deployable services. — Martin Fowler
Monolitik mimarinin büyük veya büyümekte olan projelerde neden olabileceği sorunları bir önceki yazımda örneklerle açıklamıştım. Orada belirttiğim nedenlerden dolayı son yıllarda servislerin ayrı ayrı tasarlanıp geliştirildiği Service Oriented Architecture (SOA) ve Mikroservisler mimarileri daha yaygın şekilde tercih edilmeye başlandı. Peki bu mimari yaklaşımlar ile servisler birbirinden bağımsız şekilde nasıl geliştiriliyor? Bu servisler arasında iletişim nasıl sağlanıyor? Bu yapıların geliştirme ve deployment süreçlerinde sağladığı avantajlar tam olarak neler?
Herkese merhaba, bu yazımda Mikroservisler mimarisinin ne olduğundan bahsedip, uygulama geliştirme sürecinde nasıl kullanıldığını açıklamaya çalışacağım. Keyifli okumalar :)
Konular:
- Service Oriented Architecture (SOA)’ya Hızlı Bakış
- Mikroservisler Mimarisi
- Servisler Arası İletişim
- Monorepo ve Polyrepo Kavramları
- Mikroservisler Mimarisi Dezavantajları
Service Oriented Architecture (SOA)’ya Hızlı Bakış
Microservisler mimarisinin ilk çıkış noktası Service Oriented Architecure’dır. Microservisler, SOA üzerine kurulmuş bir mimaridir.
Service Oriented Architecture (SOA) birden fazla servisin ayrı ayrı tasarlandığı, servislerin hem kendi aralarında hem de farklı database’ler ile haberleşebildiği mimaridir. Bu mimari yaklaşım ile farklı yazılımlar bir bütün olarak çalışabilir.
SOA ile amaç, yazılımların tekrar kullanılabilirliğini (reusability) ve esnekliğini (flexibility) arttırmaktır.
Loose Coupling: Loose coupling, yazılımların birbirleriyle bağımlılıklarının az olduğu bir tasarım prensibidir. Bu prensibe göre, bir yazılım bileşeni diğer bileşenlere mümkün olduğunca az bağımlı olmalıdır.
Flexibility — Scalibility — Reusability
Loose Coupling prensibi ile SOA gibi servis odaklı mimarilerde yazılımlar arasındaki bağımlılık azaltılır, bir yazılımın değiştirilmesi veya güncellenmesi diğer yazılımlar üzerinde minimum etkiye sahip olur. Böylece yazılımların daha esnek (flexible), ölçeklenebilir (scalable) ve tekrar kullanılabilir (reusable) şekilde geliştirilmesi hedeflenir.
Mikroservisler Mimarisi
Mikroservisler bir yazılım uygulamasında belirli fonksiyonları, işlevleri sağlayan, tek bir amaca hizmet eden, birbirinden bağımsız yazılım servisleridir.
While there is no precise definition of this architectural style, there are certain common characteristics around organization around business capability, automated deployment, intelligence in the endpoints, and decentralized control of languages and data. — Martin Fowler
Martin Fowler, bu mimari yaklaşım için tek bir kalıp halinde bir tanımın olmadığını, Mikroservisler Mimarisi ile belli prensiplerin uygulanmaya çalışıldığını söyler. Bu yaklaşım ile geliştirilen projelerde mimarinin uygulanış biçimi anlamında farklılıklar olsa da, ortaya konan temel karakterestiğin aynı olduğunu ekler.
Mikroservisler Mimarisi ile uygulamamızı küçük ve bağımsız servisler (microservices) halinde tasarlayıp geliştiririz. Bu mimari yaklaşım ile geliştirilen bağımsız servislerin bir araya gelerek bir bütün olarak çalışması amaçlanır.
Separation of Concerns (Modularity): Mikroservislerin hedeflediği şey, sadece 1 küçük işi çok iyi yapmaktır. Her mikroservis belirli bir işlevi yerine getirmek için tasarlanır.
Flexibility: Servisler, farklı programlama dilleri, farklı teknolojiler ve database’ler kullanarak geliştirilebilir ve farklı server’lar üzerinde çalışabilir. Bu durum yazılım geliştirme sürecinde proje için esnekliği (flexibility) arttırır.
Self-Contained and Independent: Servisler birbirinden bağımsız olarak geliştirilir ve bir serviste yapılan değişikliklerin diğer servisler üzerinde minimum etkisi olur. Her bir servis ayrı olarak geliştirilip bağımsız şekilde deploy edilebilir. Bu da loose coupling yaklaşımıdır. Örneğin Payment servis üzerinde değişiklik yaptığımızda sadece Payment servisi build ve deploy ederiz ve uygulamadaki diğer servisler bu durumdan etkilenmez.
Highly Maintainable and Testable: Bir servisin bozulması, uygulamadaki diğer servisleri etkilemez. Bu durum da maintanace ve test aşamalarında kolaylık sağlar.
Bu mimari ile uygulamayı daha küçük parçalar halinde bölerek bağımsız servisler şeklinde geliştirdiğimizi söylemiştim. Yine bir online alışveriş sitesi örneği üzerinden gidelim. Mikroservisler Mimarisi ile online alışveriş uygulaması geliştirmek istiyoruz. Burada uygulamamızı daha küçük parçalara bölmemiz gerekiyor. Bu işlem için best practice denen yöntem bussiness functionalities odaklı olarak uygulamayı bölmektir. Bu örnek için mikroservisler şu şekilde olabilir:
- User
- Product
- Cart
- Checkout
- Payment
- Order
- Seller
- Search
- Notification
Seperation of Concerns mantığıyla mikroservislerimizi bu şekilde belirleyebiliriz. Burada her bir servis bağımsız olarak geliştirilecek ve belirli fonksiyonel işlemlere odaklanacak.
Servisler Arası İletişim
Burada da aynı örnek üzerinden devam edelim. Isolated ve self-contained şekilde birbirinden bağımsız olarak geliştirdiğimiz servislerin uygulama akışı içerisinde birbirleriyle iletişime geçmesi gerekecektir. Örneğin Checkout servisine bağlı olan Checkout sayfasında sipariş ilerleme durumu gösterilir. Orada sepet bilgisinin de yer alması için Cart servisten gelecek olan sepet data’sına ihtiyaç duyulabilir. Aynı şekilde ödeme ekranında Payment servisin, ödemenin tamamlanabilmesi için gerekli olan kullanıcı bilgilerine User servisten erişmesi gerekir. Bu gibi durumlarda servisler birbirleriyle iletişime geçer.
API Calls ile İletişim (Senkron)
Her servisin, diğer servislerden gelen request’leri kabul ettiği bir endpoint’i vardır.
Servisler birbirlerine burada gözüktüğü gibi ilgili API endpoint’ler üzerinden request gönderererek iletişime geçebilir. Bu yöntem synchronous (senkron) communication yöntemdir. Senkron iletişimde bir mikroservis, işlemlerini yaparken farklı bir servisten bir data ihtiyacı olduğunda o servise istek atar ve yanıtı (response) bekler. Gelen cevap sonucunda ise işlemlerine devam eder.
Bu yöntem için en uygun protokol HTTP’dir. HTTP, REST veya SOAP ile uygulanabilir.
Senkron iletişimde, başka servislere rest çağrıları yapıyor olmak, ilgili servisin işlemlerine devam edebilmesi için istek attığı servise olan bağımlılığını gösterir. Bir rest çağrısı yapılırken cevap beklenir ve bu da işlemin bloklanmasına neden olur. Ayrıca senkron iletişimde client her bir servise tek tek istekte bulunmak durumundadır. Bu gibi durumlar bu yöntemin dezavantajlarına örnek gösterilebilir.
Bir Message Broker İle İletişim (Asenkron)
Mikroservisler mimarisinde, bir message broker kullanarak servisler arasında asynchronous communication şeklinde adlandırılan asenkron yöntemle iletişim sağlanır.
Asenkron işlemlerde bir servis, hizmet aldığı servisten yanıt beklemez. Böylece servisler bağımlı hale gelmez. Bu da loose coupling olarak adlandırılır ve mikroservisler mimarisinde istediğimiz durumdur.
Bu yöntem, bir servisin diğer servislere doğrudan istek göndermek yerine, mesajlarla iletişim kurmasını sağlar. Mesajlar, bir message broker tarafından taşınır ve alıcılara teslim edilir.
Uygulama örneğimiz üzerinden devam edelim. Örneğin kullanıcı siparişi tamamladı. Bu durumda Order servisi, siparişi işler ve bir mesaj oluşturarak, message broker’a gönderir. Message broker, sipariş tamamlandı mesajını diğer servislerin abone (subscribe) olduğu kuyruğa (queue) iletir. Payment servisi, sipariş tamamlandı mesajını dinler ve kullanıcının kredi kartından ödeme yapmasını isteyen bir mesaj gönderir. Ödeme başarılı mesajı, message broker aracılığıyla Order servisine gönderilir ve sipariş durumu “ödendi” olarak güncellenir.
Burada mesaj kuyrukları, bir mikroservisin diğerine bir mesaj göndermesine ve diğer servisin bu mesajı alıp işlemesine olanak tanır. Böylece mikroservisler arasındaki iletişim asenkron hale gelir ve servislerin işlemlerini gerçekleştirmelerine izin verirken birbirlerine bağımlılıklarını en aza indirir.
RabbitMQ, Apache Kafka gibi tool’lar mesaj kuyrukları ile iletişim yöntemini uygulamak için kullanılabilirler.
Burada mikroservisler arası iletişim için senkron ve asenkron yöntemlere birer örnek verdim. Bu konuyu daha detaylı inceleyebilmek için bu yazıyı okuyabilirsiniz.
Monorepo vs Polyrepo Kavramları
Mikroservisler mimarisi ile temel amacımızın uygulamayı daha küçük parçalar halinde bölerek bağımsız servisler şeklinde geliştirmek olduğundan bahsetmiştim. Peki bu yaklaşım ile geliştirdiğimiz projemizi git repository’si halinde en iyi şekilde nasıl yönetiriz?
Bunun için aslında 2 seçeneğimiz mevcut: Monorepo ve Polyrepo
- Monorepo (Single Repository): Monorepo, bir projedeki tüm kaynak kodların tek bir repository’de (codebase) tutulduğu bir yazılım geliştirme yaklaşımıdır.
- Polyrepo (Multi-repositories): Bir projedeki kaynak kodların farklı repository’lerde saklandığı bir yazılım geliştirme yaklaşımıdır.
Burada Gitlab örneği üzerinden ilerlemek istiyorum. Projemiz için kaynak kodlarımızı Gitlab’da tutup geliştirme sürecini orada yönettiğimizi düşünelim. Mikroservis mimarisi ile geliştirdiğimiz projenin Monorepo şeklinde olduğu senaryoda Gitlab üzerinde yalnızca bir repository’miz bulunacak. Bu durumda en yaygın kullanılan yapı Mikroservisler için ayrı folder’lar oluşturup, servisleri o klasörlerde geliştirmektir. Bizim örneğimizde Mikroservislerimiz şu şekildeydi:
- User
- Product
- Cart
- Checkout
- Payment
- Order
- Seller
- Search
- Notification
Burada klasörlerimizi de bu isimlerle oluştururuz ve her bir mikroservisi birbirinden bağımsız olacak şekilde geliştiririz.
Monorepo, tüm kodların bir arada tutulması nedeniyle değişikliklerin kolayca takip edilmesini sağlar. Bu, hataların daha hızlı tespit edilmesine ve düzeltilmesine yardımcı olur. Ayrıca farklı takımlar ve farklı yazılım arasında kod paylaşımını kolaylaştırır.
Diğer taraftan baktığımızda ise Monorepo bazı sorunlara yol açabilir. Uygulamamızı Gitlab üzerinde tek bir repository’de tutuyoruz ve geliştirmeye devam ediyoruz. Proje plansız ve beklenmedik şekilde büyüdüğünde update, build ve deploy süreçleri uzar, cloning, fetching ve pushing işlemleri yavaş gerçekleşir ve probleme dönüşme ihtimali artar.
Sonuç olarak, Monorepo yaklaşımı, tüm kaynak kodların tek bir repository’de yönetildiği bir yazılım geliştirme yaklaşımıdır.
Şimdi ise Polyrepo kavramını inceleyelim. Polyrepo’nun, bir projedeki kaynak kodların farklı repository’lerde saklandığı bir yazılım geliştirme yaklaşımı olduğunu söylemiştim. Bu yaklaşımda her bir mikroservis için ayrı git repository’leri oluşturup, her servisi ayrı codebase’lerde geliştiririz.
Gitlab Groups: GitLab’da proje yönetimi için kullanılan bir özelliktir. GitLab Groups, projelerin birlikte çalışmasını ve bir grup içindeki projeler arasında sorunsuz bir şekilde geçiş yapabilmemizi sağlar. Polyrepo tercih edildiği durumda bu özellik kullanılabilir.
Independent distribution (Polyrepo için): Her mikroservis ayrı bir repo’ya sahip olduğu için, bir servisin hatalı bir şekilde güncellenmesi veya başka bir mikroservis ile çakışması durumunda diğer mikroservislerin etkilenmemesi sağlanır.
Easy Maintanance (Polyrepo için): Her mikroservis ayrı bir repo’da olduğu için, bakım işlemleri de kolaylaşır. Bir mikroservisin bakımı sırasında diğer mikroservisler etkilenmez ve güncelleme işlemleri daha hızlı ve sorunsuz gerçekleştirilebilir.
Diğer taraftan baktığımızda ise, aynı anda farklı servisleri geliştirmeye veya incelemeye devam etmek istediğimiz durumda Polyrepo kullanımı zaman kaybına ve karışıklığa neden olabilir.
Sonuç olarak, Polyrepo yaklaşımı, bir projedeki farklı servislerin farklı repo’larda saklandığı bir yazılım geliştirme yaklaşımıdır. Bu yaklaşım, servislerin bağımsız olarak geliştirilmesine ve dağıtılmasına olanak sağlarken servisler arası bağımlılıkların yönetiminde sorun oluşturabilir.
Mikroservisler Mimarisi Dezavantajları
Monolitik yapıdaki codebase’den distributed systems olarak adlandırılan daha geniş kapsamlı yapı olan Mikroservislere geçtiğimizde bazı sorunlar ortaya çıkabilir.
Exponential infrastructure costs: Proje doğru bir şekilde planlanmadığı zaman, hosting, monitoring ve her servis için test maliyetleri istenmeyen boyutlara ulaşabilir.
Debugging challenges: Bir hata olduğu zaman tespit edilmesi Monolitik yapıya göre daha uzun sürebilir. Yani mikroservisin büyümesi ile beraber fault management kolay olmayabilir.
Development sprawl: Mikroservisler mimarisi, monolitik yapıdaki tek codebase’li projeye göre daha karmaşık yapıdadır. Geliştirme süreci düzgün bir şekilde yönetilmezse geliştirme hızı yavaşlayabilir.
Added organizational overhead: Ekiplerin, güncellemeleri koordine etmek için daha sıkı iletişim ve işbirliği içerisinde olması gerekir.
Bu yazımda Mikroservisler mimarisinin olduğunu ve nasıl kullanıldığını örneklerle açıklamaya çalıştım. Bir sonraki yazımda ise bir API management tool olan API Gateway yapısını ve bu mimaride nasıl kullanıldığını ele alacağım. Görüşmek üzere :)
Referanslar:
Kablosuz Kedi: https://www.youtube.com/watch?v=IGUJKGskaOE&t=430s