Spring Cloud Bileşenleriyle Java Mikroservis Mimarisi Kurma
Spring Cloud ile mikroservis kurma yolu: servis keşfi, ağ geçidi, merkezi yapılandırma, servisler arası çağrı, dayanıklılık ve sık yapılan tasarım hataları.
Spring Cloud neyi çözer, neyi çözmez?
Mikroservis mimarisinde uygulama, bağımsız dağıtılan küçük servislere bölünür. Bu bölünme beraberinde dağıtık sistem sorunlarını getirir: servisler birbirini nasıl bulur, istekler hangi servise yönlenir, ortak ayarlar nerede tutulur, bir servis çöktüğünde diğerleri nasıl etkilenmez? Spring Cloud, Spring Boot uygulamalarına bu sorunlar için hazır desen ve kütüphaneler sunan bir proje ailesidir.
Resmî proje sayfası özelliklerini şöyle sıralar: dağıtık ve sürümlenmiş yapılandırma, servis kaydı ve keşfi, yönlendirme, servisler arası çağrı, yük dengeleme, devre kesiciler (circuit breaker), dağıtık mesajlaşma ve kısa ömürlü görevler. Önemli bir nokta: Spring Cloud mikroservis mimarisini sizin yerinize tasarlamaz. Servis sınırlarını nereden çizeceğiniz, veriyi nasıl böleceğiniz ve ekipleri nasıl organize edeceğiniz tasarım kararlarıdır. Kubernetes gibi bir platform kullanıyorsanız, bazı işleri (servis keşfi, yapılandırma) platformun kendi araçlarına bırakmak da mümkündür; bunun için Spring Cloud Kubernetes gibi entegrasyonlar vardır.
Önce servis sınırlarını çizin
Araç seçmeden önce asıl iş, uygulamanın hangi iş yeteneklerine ayrılacağını belirlemektir. Pratik ölçütler:
- Her servis bir iş yeteneğine sahip olmalı: sipariş, stok, ödeme, bildirim gibi. Teknik katmanlara (veri erişim servisi, arayüz servisi) göre bölmek sık yapılan hatadır.
- Servis kendi verisinin sahibi olmalı. Servisler aynı tabloyu doğrudan paylaşıyorsa bağımsız dağıtım kâğıt üstünde kalır.
- Sık birlikte değişen parçalar aynı serviste kalmalı. Her değişiklikte birkaç servisi birlikte yayınlamak zorunda kalıyorsanız sınır yanlış yerde olabilir.
- Başlangıçta fazla ayrıştırmaktan kaçının. Önce modüler bir monolit olarak başlayıp sınırlar netleştikçe servis çıkarmak çoğu zaman daha güvenlidir.
Bu aşamada servisler arası bağımlılık haritası da çıkarılır. Senkron (istek-cevap) ve asenkron (olay tabanlı) iletişimin nerede kullanılacağı bu haritaya göre kararlaştırılır.
Temel bileşenler: ne işe yarar, nasıl bağlanır?
Tipik bir Spring Cloud mikroservis kurulumunda şu parçalar bulunur:
- Servis kaydı ve keşfi: Servisler kendilerini bir kayıt defterine bildirir, diğer servisler isim üzerinden bulur. Eureka sunucusu için sunucu uygulamasında @EnableEurekaServer, istemci servislerde ise @EnableDiscoveryClient ya da ilgili başlatıcı bağımlılığı kullanılır. Alternatif olarak Consul, ZooKeeper veya Kubernetes entegrasyonları vardır.
- Ağ geçidi (Spring Cloud Gateway): Dışarıdan gelen istekleri karşılar, rota tanımlarına göre iç servislere yönlendirir. Her rota bir koşul (örneğin yol deseni), isteğe uygulanacak filtreler ve hedef servisten oluşur. Hedef, keşif sistemindeki servis adı olabilir ve yük dengeleme otomatik yapılır.
- Merkezi yapılandırma (Spring Cloud Config): Ayarlar bir Git deposunda ya da başka bir kaynakta tutulur, servisler açılırken buradan okur. Ortama göre farklı değer (geliştirme, test, üretim) bu sayede kodsuz değiştirilir.
- Servisler arası çağrı (OpenFeign): Arayüz tanımlayarak HTTP çağrısını bildirimsel yazarsınız; @FeignClient ile hedef servis adı verilir.
- Devre kesici (Spring Cloud Circuit Breaker): Çağrılan servis yanıt vermediğinde istekleri belirli bir süre kesip yedek davranışa (fallback) yönlendirir.
- Mesajlaşma (Spring Cloud Stream, Bus): Servisler arası olay iletimi için Kafka veya RabbitMQ gibi aracıları soyutlar; Bus ise yapılandırma değişikliği gibi durum değişikliklerini yayar.
Önemli uyarı: Bileşenlerin yapılandırma özellik adları ve bazı sınıf adları Spring Cloud sürümleri arasında değişir. Uygulama yazarken seçtiğiniz Spring Boot ve Spring Cloud sürüm çiftini resmî uyumluluk tablosundan doğrulayın.
Örnek akış: sipariş servisi stok servisini çağırıyor
Örneğin bir e-ticaret sisteminde sipariş servisi, bir sipariş oluştururken stok servisinden ürünün mevcut olup olmadığını soruyor.
Akış şöyle kurulur. Stok servisi açılırken keşif sunucusuna kayıt olur. Sipariş servisinde stok servisinin arayüzü tanımlanır:
@FeignClient(name = "inventory-service") public interface InventoryClient { @GetMapping("/inventory/{productCode}") InventoryInfo getInventory(@PathVariable String productCode); }
Sipariş servisi bu arayüzü sıradan bir bileşen gibi çağırır; Feign, keşif sisteminden stok servisinin adresini bulur ve yük dengeleyerek istek gönderir. Stok servisi yanıt vermediğinde ise bir devre kesici devreye girer. Kod tarafında Spring Cloud Circuit Breaker soyutlaması kullanıldığında çağrı bir fabrikadan oluşturulur ve ek parametreyle yedek davranış verilir:
circuitBreakerFactory.create("stok").run(() -> inventoryClient.getInventory(kod), hata -> InventoryInfo.bilinmiyor());
Yedek davranışın ne olacağı iş kararıdır: sipariş alınıp stok sonradan doğrulanabilir mi, yoksa sipariş reddedilmeli mi? Dayanıklılık, bu soruların net cevabıyla başlar.
Kontrol listesi: üretime almadan önce
- Her servisin sağlık kontrolü ve yönetim uç noktaları tanımlı mı?
- Servisler arası tüm çağrıların zaman aşımı ve yeniden deneme davranışı açıkça yapılandırıldı mı? Varsayılanlara güvenmek, zincirleme gecikme riskidir.
- Devre kesici ve yedek davranışlar iş sahibiyle birlikte kararlaştırıldı mı?
- Yapılandırmada gizli bilgiler (parola, anahtar) açık metin olarak mı tutuluyor? Bunun için şifreleme ya da ayrı bir gizli bilgi yönetimi kullanın.
- Dağıtık izleme için isteklere ortak bir izleme kimliği taşınıyor mu? Günlükler merkezi bir yerde toplanıyor mu?
- Veri tutarlılığı için dağıtık işlem yerine olay tabanlı yaklaşım ya da telafi adımları düşünüldü mü?
- Ağ geçidinde kimlik doğrulama ve yetkilendirme nerede yapılıyor? İç servisler dışarıya doğrudan açık mı?
- Servislerin sürümlenmesi ve geriye dönük uyumluluk kuralı var mı?
- Yerel geliştirme ortamında tüm bileşenleri ayağa kaldırmak makul bir çabayla mümkün mü?
Sık yapılan hatalar
- Dağıtık monolit kurmak. Servisler ayrı dağıtılsa da her değişiklikte birlikte yayınlanıyor ve senkron çağrı zincirleriyle birbirine bağlıysa bir monolitin tüm dezavantajlarını, ağ gecikmesiyle birlikte taşırsınız.
- Tek ortak veritabanını paylaşmak. Servislerin bağımsızlığını ortadan kaldırır.
- Her şeyi senkron çağırmak. Uzun çağrı zincirlerinde en yavaş servis tüm isteği yavaşlatır; uygun yerlerde olay tabanlı iletişim kullanın.
- Zaman aşımı ve devre kesiciyi atlamak. Tek bir yavaş servis, bekleyen iş parçacıklarını doldurup tüm sistemi durdurabilir.
- Ağ geçidini iş mantığıyla doldurmak. Geçit yönlendirme, kimlik ve genel filtreler için; iş kuralları servislerde olmalı.
- Sürüm uyumsuzluğunu görmezden gelmek. Spring Boot ve Spring Cloud sürümleri birbirine uygun seçilmezse çalışma anı hataları görülür.
- Gözlemlenebilirliği sona bırakmak. Dağıtık sistemde sorun aramak için izleme ve merkezi günlük başından kurulmalıdır.
- Gereksiz mikroservis. Küçük bir ekip ve basit bir uygulama için iyi tasarlanmış bir monolit çoğu zaman daha uygun bir başlangıçtır.
Bu beceriyi Mindset programlarında nerede çalışırsınız?
Java Mikroservisleri (Spring Cloud) eğitimi bu sayfadaki yol haritasını modüller halinde işler: Mikroservis Tasarımı ve Ayrıştırma, Servis Keşfi ve Ağ Geçidi, Merkezî Yapılandırma ve Ortam Yönetimi, Servisler Arası İletişim ve Dayanıklılık, Veri Yönetimi ve Olay Tabanlı Entegrasyon ile Gözlemlenebilirlik ve Dağıtım. Eğitim, monolit uygulamayı servislere ayırmayı planlayan ya da dağıtık sistemde çalışan Java geliştiricileri, teknik liderler ve mimarlar için uygundur.
Kurumsal ekiplerde, servis sınırları ve dayanıklılık kararlarının ortak bir dille konuşulması için yararlıdır; aynı mimari anlayışla çalışan bir ekip, servisler arası sözleşmeleri ve operasyon standartlarını daha tutarlı kurar.
İlgili Mindset programı
Java Mikroservisleri (Spring Cloud)
Sık sorulan sorular
Spring Cloud kullanmak için Kubernetes gerekli mi?
Hayır. Spring Cloud, Kubernetes olmadan da servis keşfi, yapılandırma ve yük dengeleme sağlar. Kubernetes kullanıyorsanız bu işlerin bir kısmını platformun kendi mekanizmalarına bırakmak mümkündür ve Kubernetes entegrasyon projesi bunu destekler. Karar, ekibin operasyon yetkinliğine ve altyapıya bağlıdır.
Eureka ve Consul arasında nasıl seçim yapılır?
Her ikisi de servis keşfi sunar. Eureka, Spring ekosistemiyle yaygın kullanılan, görece basit bir kayıt defteridir; Consul ise anahtar-değer deposu ve sağlık kontrolü gibi ek yetenekler getirir. Kurumun mevcut altyapısı, operasyon deneyimi ve ihtiyaç duyulan özellikler seçimi belirler.
Servisler arası çağrıda Feign mi, başka bir istemci mi?
OpenFeign, bildirimsel arayüz yazarak çağrıyı sadeleştirir ve Spring Boot ile uyumludur. Reaktif yığın kullanıyorsanız ya da daha ince kontrol istiyorsanız Spring'in diğer HTTP istemcileri tercih edilebilir. Hangisi seçilirse seçilsin zaman aşımı, yeniden deneme ve devre kesici davranışı açıkça tanımlanmalıdır.
Spring Cloud Gateway ile OpenFeign arasındaki fark nedir?
Gateway sistemin kapısıdır: dışarıdan gelen istekleri karşılar, rota tanımlarına göre iç servislere yönlendirir ve kimlik gibi genel filtreleri uygular. OpenFeign ise bir servisin başka bir servisi çağırırken kullandığı bildirimsel HTTP istemcisidir. Biri dış trafiği, diğeri iç servis çağrılarını yönetir; çoğu kurulumda ikisi birlikte bulunur.
Küçük bir ekip mikroservisle başlamalı mı?
Çoğu zaman gerekmez. Mikroservis, bağımsız dağıtım ve ekip ölçeklemesi için bir maliyet getirir: operasyon yükü, dağıtık hata ayıklama, sürüm yönetimi. İş yeteneği sınırları netleşene kadar modüler bir monolit ile başlamak ve ihtiyaç doğdukça servis çıkarmak makul bir yoldur.
İlgili rehberler
Kategori: Yazılım Geliştirme, DevOps ve Bulut Teknolojileri