Gereksinimler ve Ön Hazırlık
Uygulamaya başlamadan önce sisteminizde Java 21 veya daha güncel bir sürümün yüklü olduğundan emin olun. Projenizi yönetmek için Maven veya Gradle kullanabilirsiniz. Resilient4j, Spring Boot ile mükemmel bir uyum içinde çalışır; bu nedenle bağımlılıkları pom.xml dosyanıza ekleyerek süreci başlatacağız.
Aşağıdaki bağımlılıklar, projenizde gerekli olan temel modülleri sağlar:
io.github.resilience4j
resilience4j-spring-boot3
2.2.0
org.springframework.boot
spring-boot-starter-aop
AOP (Aspect Oriented Programming - Yönelimli Programlama), metodlarınızın etrafına ek mantıklar (örneğin hata yönetimi) sarmalamanıza olanak tanır. Bu kütüphaneler, Resilient4j'nin anotasyon tabanlı çalışması için zorunludur.
Circuit Breaker (Devre Kesici) ile Servis Kesintilerini Yönetme
Circuit Breaker, bir servise yapılan çağrıların başarısızlık oranını izler. Eğer başarısızlık belirli bir eşiği aşarsa, devre "açılır" ve bir süre boyunca servise istek gönderilmesini engelleyerek sistemin nefes almasını sağlar. Bu, çökmüş bir servise sürekli istek göndererek kaynak tüketimini önlemenin en iyi yoludur.
Yapılandırma için application.yml dosyanızda şu ayarları kullanabilirsiniz:
resilience4j.circuitbreaker:
instances:
servisAdi:
registerHealthIndicator: true
slidingWindowSize: 10
minimumNumberOfCalls: 5
permittedNumberOfCallsInHalfOpenState: 3
failureRateThreshold: 50
waitDurationInOpenState: 10s
Bu ayarlar, son 10 çağrının %50'si başarısız olduğunda devreyi açar ve 10 saniye boyunca bekler. @CircuitBreaker anotasyonu ile metodunuzu şu şekilde korumaya alırsınız:
@Service
public class DisServisClient {
@CircuitBreaker(name = "servisAdi", fallbackMethod = "yedekMetod")
public String veriGetir() {
// Dış servis çağrısı
return restTemplate.getForObject("http://api.ornek.com/veri", String.class);
}
public String yedekMetod(Exception e) {
return "Servis şu an meşgul, lütfen daha sonra deneyin.";
}
}
Burada fallbackMethod, ana servis hata verdiğinde kullanıcıya gösterilecek veya dönecek olan alternatif yanıtı temsil eder.
Retry (Yeniden Deneme) Mekanizması ile Geçici Hataları Aşma
Ağdaki anlık kopmalar veya servislerin çok kısa süreli meşguliyetleri, kalıcı hata gibi görünebilir. Retry mekanizması, başarısız olan bir işlemi belirli aralıklarla tekrar deneyerek başarı şansını artırır.
Retry yapılandırması için aşağıdaki örneği inceleyin:
resilience4j.retry:
instances:
backendA:
maxAttempts: 3
waitDuration: 500ms
retryExceptions:
- org.springframework.web.client.HttpServerErrorException
Bu kod, yalnızca belirtilen istisnalar (sunucu kaynaklı hatalar) oluştuğunda 500 milisaniye aralıklarla 3 kez deneme yapar. Bu, özellikle geçici ağ sorunlarını çözmek için idealdir.
Rate Limiter (Hız Sınırlayıcı) ile Kaynak Koruma
Bir servisin belirli bir zaman diliminde alabileceği istek sayısını sınırlamak, servisin aşırı yüklenmesini engeller. Rate Limiter, API'nizi kötü niyetli veya aşırı yoğun trafikten korumak için kullanılır.
@RateLimiter(name = "apiLimiter")
public void kritikIslemYap() {
// İş mantığı
}
Yapılandırma dosyasında ise şu şekilde tanımlanır:
resilience4j.ratelimiter:
instances:
apiLimiter:
limitForPeriod: 50
limitRefreshPeriod: 1s
timeoutDuration: 0
Bu ayar, saniyede en fazla 50 isteğe izin verir. Limiti aşan istekler doğrudan reddedilir veya timeoutDuration süresince bekletilir.
Hata Tolerans Yöntemlerinin Karşılaştırması
| Yöntem | Amacı | Ne Zaman Kullanılır? |
|---|---|---|
| Circuit Breaker | Basamak etkisini önleme | Dış servis çöktüğünde |
| Retry | Geçici hataları düzeltme | Ağ kopmaları/kısa süreli meşguliyet |
| Rate Limiter | Kaynakları koruma | Aşırı trafik/DoS saldırısı |
| Bulkhead | Kaynak izolasyonu | Birden fazla servisi ayırmak için |
Kritik Uyarılar ve Güvenlik Önlemleri
Dikkat: Üretim ortamında (production) Circuit Breaker eşik değerlerini çok düşük tutmak, servisin gereksiz yere "açık" duruma geçmesine neden olabilir. Ayrıca, fallback metodlarınızda asla hassas verileri veya hata yığınlarını (stack trace) kullanıcıya doğrudan göstermeyin; bu bir güvenlik açığıdır.
Kod güvenliği konusunda, özellikle dış servislerden gelen verileri her zaman valide edin. fallbackMethod içerisinde yapılan işlemlerin güvenli olduğundan ve SQL injection gibi riskler taşımadığından emin olun. Girdi doğrulama (input validation) her zaman önceliğiniz olmalıdır.
Sıkça Sorulan Sorular
Circuit Breaker neden sürekli açık kalıyor?
Bu durum genellikle slidingWindowSize değerinin çok küçük olması veya dış servisin tamamen erişilemez olmasıdır. Servisinizin sağlık durumunu kontrol edin.
Retry mekanizması her hatada çalışmalı mı?
Hayır. Özellikle 4xx (Client Error) hatalarında yeniden deneme yapmak anlamsızdır. Sadece 5xx (Server Error) veya ağ zaman aşımı hatalarında Retry kullanmalısınız.
Resilience4j ile Bulkhead nedir?
Bulkhead, bir servise ayrılan thread havuzunu veya semafor sayısını sınırlayarak, tek bir servisin tüm sistem kaynaklarını tüketmesini engeller.
Fallback metodunda ne tür işlemler yapmalıyım?
Fallback metodları mümkün olduğunca hafif olmalıdır. Önbellekten veri okumak veya varsayılan bir değer dönmek en iyi pratiklerdir.
Rate Limiter kullanıcı bazlı mı çalışır?
Varsayılan olarak geneldir. Ancak özel bir RateLimiterRegistry kullanarak kullanıcı ID'sine göre dinamik limitler tanımlayabilirsiniz.
Resilience4j ile İleri Seviye İzleme ve Metrik Analizi
Hata tolerans mekanizmalarını kurmak, sistemin ayakta kalmasını sağlasa da, bu mekanizmaların ne zaman devreye girdiğini bilmek operasyonel mükemmellik için şarttır. Resilience4j, Micrometer kütüphanesi ile entegre çalışarak tüm olayları metrik olarak dışa aktarabilir.
Micrometer Entegrasyonu ile Metrikleri Görselleştirme
Uygulamanızdaki Circuit Breaker durumlarını veya Rate Limiter tarafından reddedilen istekleri Prometheus veya Grafana gibi araçlarla izlemek için öncelikle gerekli bağımlılıkları eklemeli ve ardından bir RegistryEventConsumer konfigürasyonu yapmalısınız.
@Configuration
public class Resilience4jConfig {
@Bean
public RegistryEventConsumer myRegistryEventConsumer() {
return new RegistryEventConsumer() {
@Override
public void onEntryAddedEvent(EntryAddedEvent entryAddedEvent) {
entryAddedEvent.getAddedEntry().getEventPublisher()
.onStateTransition(event -> log.info("Circuit Breaker Durum Değişimi: {}", event.getStateTransition()));
}
@Override
public void onEntryRemovedEvent(EntryRemovedEvent entryRemoveEvent) {}
@Override
public void onEntryReplacedEvent(EntryReplacedEvent entryReplacedEvent) {}
};
}
}
Resilience4j Test Stratejileri: Hata Senaryolarını Simüle Etme
Hata tolerans kodlarınızın gerçekten çalıştığından emin olmak için "Chaos Engineering" prensiplerini uygulamalısınız. Unit testlerinizde Awaitility kullanarak zaman aşımı ve devre kesici durumlarını simüle edebilirsiniz.
Awaitility ile Asenkron Hata Testi
Test ortamında gerçek bir servis kesintisi yaratmak yerine, mock nesneler üzerinden hata fırlatarak devre kesicinin "Open" durumuna geçip geçmediğini doğrulamak en güvenli yöntemdir.
@Test
public void circuitBreakerShouldOpenAfterThresholdExceeded() {
// 5 hata sonrası devre kesicinin açılmasını bekliyoruz
for (int i = 0; i < 5; i++) {
try { service.callExternalApi(); } catch (Exception e) {}
}
Awaitility.await().atMost(Duration.ofSeconds(2)).until(() ->
circuitBreakerRegistry.circuitBreaker("myService").getState() == CircuitBreaker.State.OPEN
);
}
Performans Üzerindeki Etkiler ve İnce Ayarlar
Resilience4j, dekoratör desenini (Decorator Pattern) kullandığı için her çağrıya ek bir katman ekler. Çok yüksek trafikli sistemlerde (saniyede binlerce istek) bu durum ihmal edilebilir bir gecikmeye (overhead) neden olabilir. Performansı optimize etmek için şu ipuçlarını izleyin:
- Thread Pool Kullanımı: Bulkhead yapısında
SemaphoreyerineThreadPoolkullanmak, ana iş parçacıklarını (main thread) korur ancak bağlam değiştirme (context switching) maliyetini artırır. - Event Publisher Performansı: Çok fazla log üreten
EventPublisherkonfigürasyonları, yoğun yük altında CPU kullanımını artırabilir. Üretim ortamında sadece kritik olayları (State Transition gibi) loglayın. - Kayıtlı Nesneler:
CircuitBreakerRegistryveRateLimiterRegistrynesnelerini Singleton olarak yönetin; her istekte yeni bir registry oluşturmak bellek sızıntısına yol açar.
İpucu: Eğer sisteminizde çok sayıda farklı servis çağrısı varsa, her biri için ayrı bir Circuit Breaker instance'ı oluşturmak yerine, ortak bir konfigürasyon şablonu (Config Template) kullanarak bellek kullanımını minimize edin.
Sonuç
Java ekosisteminde Resilient4j kullanmak, sisteminizin dayanıklılığını artırmak için atacağınız en önemli adımlardan biridir. Circuit Breaker ile sisteminizi korumayı, Retry ile geçici hataları aşmayı ve Rate Limiter ile kaynaklarınızı yönetmeyi öğrendiniz. Bir sonraki adım olarak, bu kütüphaneyi Micrometer ile izleyerek (monitoring) hata oranlarınızı görselleştiren paneller oluşturmanızı öneririm. Dayanıklı sistemler, sadece kod yazmakla değil, hataları öngörüp onlara hazırlıklı olmakla inşa edilir.


Yorumlar (0)
Yorum Yaz