Mikroservis Desenleri
Mikroservisler Arasında İletişim
Mikroservisler arasında iletişim genellikle 2 ana yöntemle gerçekleşir:
- Senkron iletişim, anında yanıt gerektiğinde (örneğin, bir kullanıcı doğrulama API’si) tercih edilir. Telefon görüşmesinde her cümlenin karşı taraftan cevabını elimizde telefonla bekleyişimiz örnek olur.
- Asenkron iletişim, işlem sürekliliği ve ölçeklenebilirlik gerektiğinde (örneğin, e-posta bildirimleri veya büyük veri işleme) kullanılır. Mektupla haberleşmede mektubu gönderip PTT’de cevabın gelmesini beklemeyişimiz buna örnek olur.
- Hibrit iletişim, servislerin bir kısmı senkron bir kısmının asenkron haberleşmesidir. Bir devlet dairesinde bir dizi işlemi masa masa gezip evrak tamamlandıktan sonra evinize gitmeniz ve bir gün posta ile sonucu almanız.
Bu üç yöntem, sistem tasarımına, ihtiyaçlara ve performans gereksinimlerine göre seçilir.
- Senkron İletişim: Genellikle HTTP/REST veya gRPC gibi protokollerle bir mikroservis başka bir mikroservise istek gönderdiğinde, yanıt alınana kadar bekler. Eğer çağrılan servis yanıt vermezse, zaman aşımı olur. Bu, bir çağrının sonucunun hemen gerektiği durumlarda kullanılır. Çağrılar sırasında hata yönetimi ve izleme daha basittir.
- Asenkron İletişim: Zamanlaması önemli olmayan işlerde bir mikroservis başka bir mikroservise mesajı bir kuyruk üzerinden gönderir ve alıcılar bu mesajları işler yanıtı kuyruğa gönderir ancak istemci mikroservisten yanıt beklemez. Mesaj kuyrukları veya olay tabanlı sistemler (Kafka, RabbitMQ, AWS SQS, vb.) bu iletişim türünde sıkça kullanılır. Servisler arasında doğrudan bir bağımlılık olmayıp mikroservisler birbirlerinin çalışır durumda olup olmadığını bilmez. Çok sayıda mesaj, birden fazla tüketici tarafından işlenebilir.
Bu akış diyagramında önce senkron iletişimi görelim. Dışarıdan gelen istek kullanıcı doğrulama ve anında yanıt istiyor. Bu yüzden A hizmetine ve ondan veritabanı hizmetine senkron gidiliyor. Ancak bir yayın/abonelik işi gelince acilen bitmesi gerekmediği için asenkron olacak şekilde kuyruk mekanizması kullanılarak iş yürütülüyor.
Transaction
Transaction, bir dizi işlemin bir bütün olarak ele alındığı ve ya tamamen başarılı olması ya da tamamen başarısız olması gereken bir süreçtir.
Transaction farklı alanlarda farklı gibi anlaşılır. Hepsinin ortak anlamı en küçük işlem olacak.
- İktisadi mânâda: En yaygın anlamı, para transferi olarak kabul edilir. Örneğin, bir mağazada alışveriş yaparken kredi kartınızı kullanarak ödeme yaptığınızda bu bir transaction’dır. Banka hesapları arasında para gönderme, fatura ödeme veya hisse senedi alım satımı gibi işlemler de transaction kapsamına girer.
- Veri İşlemi: Bilgi teknolojisi ve yazılım alanında, “transaction” terimi, bir veritabanında bir dizi işlemi ifade eder. Örneğin, bir online bankacılık uygulamasında para transferi yaparken, işlem sırasında bir hata olursa, tüm işlemin geri alınması gerekir.
- Ticari İşlem: Genel olarak ticaret veya iş yapma anlamında da kullanılabilir. İki taraf arasında mal veya hizmet alışverişi yapmak, bir transaction olarak düşünülebilir.
- Sözleşme veya Anlaşma: Bazen “transaction”, iki taraf arasında yapılan resmi bir anlaşmayı veya sözleşmeyi de ifade edebilir. Örneğin, bir gayrimenkul alım satımında taraflar arasında yapılan anlaşmalar.
Yerel ve dağıtık işlemler olarak (yani ‘transaction’ ve ‘distrubuted transaction’) iki türü vardır.
Yerel transaction, bir veritabanı veya sistem üzerinde gerçekleştirilen işlemleri ifade eder. Kendi bilgisayarınızdaki bir veritabanı üzerinde yaptığınız işlemi düşünebilirsiniz.
Dağıtılmış transaction, birden fazla veritabanı veya sistem üzerinde gerçekleştirilen işlemleri ifade eder. Bir bankanın tüm işlemleri tek bir veritabanı üzerinde yapmasını beklemediğiniz yapılar ;) Bu tür işlemler genellikle farklı mikroservisler veya sistemler arasında veri tutarlılığını sağlamak için kullanılır. Bu işlemler, tüm bileşenlerin ya başarıyla tamamlanmasını ya da hiçbirinin gerçekleştirilmemesini sağlayarak veri bütünlüğünü korumak amacıyla tasarlanmıştır.
Aralarındaki farklara bir göz atın ama adım adım gideceğiz, çok takılmanıza gerek yok ;)
Önce ACID prensiplerini tanıyalım. Çünkü veri saklamaya dair kurallarımızı ACID’den alıyoruz. Transaction sadece veritabanlarında geçerli bir kavram değil elbette ancak algılanması daha kolay.
Veritabanı işlemleri, ACID prensiplerini takip ederek güvenilir bir şekilde gerçekleştirilir. Herhangi bir veritabanı sistemi, bu prensiplere uygun olarak tasarlandığında, veri bütünlüğü ve güvenilirliği sağlanmış olur. Örneğin, bankacılık sistemlerinde para transferi gibi kritik işlemler ACID prensipleri sayesinde güvence altına alınır; eğer transferin herhangi bir aşamasında hata olursa, tüm işlem geri alınarak veri tutarlılığı korunur
ACID Prensipleri
- Atomicity (Bütünlük): Bir işlem ya tamamen başarılı olmalı ya da hiç gerçekleşmemelidir. Eğer işlem içindeki herhangi bir adım başarısız olursa, tüm işlem geri alınır. Bu, veritabanının tutarlılığını korur.
- Consistency (Tutarlılık): İşlemler, veritabanını her zaman geçerli bir durumdan diğerine taşır. Yani, bir işlem tamamlandığında veritabanının tutarlı bir durumda kalması sağlanır. Eğer bir işlem geçersiz veri üretiyorsa, veritabanı otomatik olarak en son geçerli duruma döner.
- Isolation (Yalıtım): Bir işlem, başka işlemlerin etkisinden bağımsız olarak çalışmalıdır. Bu, aynı anda yürütülen işlemlerin birbirini etkilememesini sağlar; yani bir işlem tamamlanmadan diğer işlemler onun verilerini göremez.
- Durability (Dayanıklılık): Bir işlem başarıyla tamamlandıktan sonra, bu işlemin sonuçları kalıcı hale gelir. Sistem çökse bile, bu sonuçlar kaybolmaz ve geri alınamaz
Bir e-ticaret uygulamasında sipariş oluşturma süreci üzerinden örnek verelim:
- Sipariş servisi, yeni bir sipariş oluşturduğunda “ORDER_CREATED_EVENT” adında bir olay yayınlar.
- Ödeme servisi bu olayı alarak ödemeyi gerçekleştirir ve “BILLED_ORDER_EVENT” yayınlar.
- Stok servisi bu olayı alarak stok güncellemelerini yapar ve “ORDER_PREPARED_EVENT” yayınlar.
- Son olarak, teslimat servisi bu olayı alarak siparişi teslim eder ve “ORDER_DELIVERED_EVENT” gönderir.
Eğer bu süreçte herhangi bir aşamada hata oluşursa, sistemin tutarlı kalabilmesi için geri alma (rollback) işlemleri gerçekleştirilir. Bu ACID’in A’sı yani “atomicity” olup işlemin tüm parçalarının başarıyla tamamlanmasını hedefler.
Buradaki 4 hizmetin (sipariş, ödeme, stok, teslimat) ACID prensiplerini sağlaması beklenir.
ACID prensiplerine göre bir işleme (transaction) ait alt işlemlerin her biri başarıyla tamamlanmalı. Eğer bir tanesi bile başarısız olursa sistemin tutarlılığını sağlamak için her servis, ilgili geri alma taleplerini alır ve geri alma (rollback) işlemleri gerçekleştirilir ve tekrar önceki kararlı duruma dönülür.
İşte bu onaylama veya geri sarma işini yapma yani bütünlüğü sağlamak adına dağıtık sistemlerde işlem yönetimi için 2PC veya SAGA tasarım desenini/yöntemini kullanıyoruz.
Two-Phase Commit (2PC), “İki Aşamalı İşlem”
Bu yöntem, işlemlerin ACID prensiplerini destekleyerek veri tutarlılığını sağlamak amacıyla hazırlık ve tamamlama aşamalarını bir koordinatör yönetir.
Koordinatör, dağıtık işlem sürecini yöneten ve işlemin başarılı bir şekilde tamamlanıp tamamlanmayacağına karar veren bileşendir. Koordinatör, tüm katılımcı düğümlere (worker nodes) işlem hakkında talimatlar gönderir ve onların yanıtlarını toplar. Eğer tüm katılımcılar işlemi gerçekleştirmeye hazır olduklarını belirtirse, koordinatör işlemi tamamlar; aksi takdirde, tüm katılımcılara işlemi geri alması talimatını verir.
- Apache Zookeeper: Dağıtık sistemlerde koordinasyon ve yapılandırma yönetimi için yaygın olarak kullanılan bir araçtır. 2PC gibi protokollerin uygulanmasına yardımcı olabilir.
- Atomikos: Java tabanlı uygulamalar için 2PC desteği sunan bir işlem yöneticisidir.
- Spring Framework: Spring’in Transaction Management modülü, 2PC’yi destekleyen çeşitli veri kaynakları ile entegrasyon sağlar.
- X/Open XA: Dağıtık işlem yönetimi için standart bir API sağlayan bir protokoldür ve 2PC ile uyumludur.
Bu kütüphaneler, geliştiricilerin 2PC protokolünü uygularken karşılaşabilecekleri karmaşıklıkları azaltmaya yardımcı olur ve sistemin güvenilirliğini artırır.
Resilience, bir sistemin, beklenmedik durumlar veya arızalar karşısında işlevini sürdürebilme yeteneğidir. Yazılım mimarisinde, özellikle mikroservislerde, bu kavram, bir bileşenin arızalanması durumunda diğer bileşenlerin etkilenmeden çalışmaya devam etmesi demektir. Bu, sistemin genel dayanıklılığını artırarak kesintisiz hizmet sunma kapasitesini geliştirir.
Mikro Hizmetler Arasında İletişim Mimarileri
1. RESTful API
Mikro hizmetler, HTTP protokolü üzerinden RESTful API’ler aracılığıyla birbirleriyle iletişim kurabilir. Her hizmet, belirli uç noktalar (endpoints) sunarak diğer hizmetlerin ihtiyaç duyduğu verilere erişmesini sağlar.
Örnek Kullanım:
- Sipariş Servisi, yeni bir sipariş oluşturmak için
POST /orders
uç noktasını kullanır. - Ödeme Servisi, sipariş bilgilerini almak için
GET /payments/{orderId}
uç noktasını kullanır.
2. Mesaj Kuyruğu (Message Queue)
Mikro hizmetler arasında asenkron iletişim sağlamak için mesaj kuyrukları kullanılabilir. Bu yaklaşım, olay tabanlı mimarilerde yaygın olarak tercih edilir ve sistemin ölçeklenebilirliğini artırır.
Örnek Kullanım:
- Sipariş Servisi, “ORDER_CREATED_EVENT” mesajını bir mesaj kuyruğuna (örneğin RabbitMQ veya Apache Kafka) gönderir.
- Ödeme Servisi bu mesajı dinleyerek ödemeyi gerçekleştirir.
3. Olay Tabanlı Mimari (Event-Driven Architecture)
Mikro hizmetler, olay tabanlı bir mimari ile birbirleriyle etkileşimde bulunabilir. Her hizmet, belirli olayları yayınlar ve diğer hizmetler bu olayları dinleyerek yanıt verir.
Örnek Kullanım:
- Sipariş Servisi, sipariş oluşturulduğunda “ORDER_CREATED_EVENT” olayını yayınlar.
- Stok Servisi bu olayı dinleyerek stok güncelleme işlemini başlatır.
4. API Gateway
API Gateway, mikro hizmetlerin dış dünyaya sunulan tek bir giriş noktasıdır. Tüm istekler bu noktadan geçer ve uygun mikro hizmetlere yönlendirilir.
Örnek Kullanım:
- Müşteri, sipariş oluşturmak için API Gateway üzerinden istek gönderir.
- API Gateway, isteği uygun olan Sipariş Servisi’ne yönlendirir.
5. gRPC
gRPC, Google tarafından geliştirilen yüksek performanslı bir uzaktan prosedür çağrısı (RPC) çerçevesidir. Mikro hizmetler arasında hızlı ve verimli iletişim sağlar.
Örnek Kullanım:
- Mikro hizmetler arasında daha karmaşık veri yapıları iletmek gerektiğinde gRPC kullanılabilir.
- Örneğin, Sipariş Servisi ve Ödeme Servisi arasında ödeme bilgilerini iletmek için gRPC çağrıları yapılabilir.