Resilience4j & Circuit Breaker
Dağıtık sistemlerde servisler arası çağrılar ağ üzerinden gerçekleşir ve ağ güvenilir değildir. Bir servis yavaşlayabilir, cevap vermeyebilir veya tamamen çökebilir. Bu durumda onu çağıran servisin de yavaşlaması — hatta çökmesi — tehlikesi vardır. Bu "cascading failure" (zincirleme hata) problemidir. Resilience4j, bu tür hatalara karşı uygulamanızı dayanıklı kılan hafif bir Java kütüphanesidir.
Circuit Breaker Pattern
Elektrik devrelerindeki sigorta (circuit breaker) mantığıyla çalışır. Üç durumu vardır:
Başarılı çağrılar
┌──────────────┐
│ ▼
┌───────┐ ┌──────────┐ ┌───────┐
│ CLOSED│───►│ OPEN │───►│ HALF- │
│ │ │ │ │ OPEN │
└───────┘ └──────────┘ └───┬───┘
▲ │
└────────────────────────────┘
Başarılı test çağrılarıCLOSED (Kapalı — Normal Durum): İstekler normal şekilde geçer. Hata oranı izlenir.
OPEN (Açık — Devre Kesildi): Hata oranı eşiği aştığında devre açılır. İstekler hedefe gönderilmez, doğrudan fallback yanıtı döner. Bu, çökmüş servisi gereksiz yere bombardımana tutmayı engeller.
HALF-OPEN (Yarı Açık — Test): Belirli bir süre sonra sınırlı sayıda test isteği geçirilir. Başarılıysa CLOSED'a döner, başarısızsa tekrar OPEN olur.
Resilience4j Kurulumu
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>@CircuitBreaker Annotation
@Service
public class OrderService {
private final RestTemplate restTemplate;
public OrderService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@CircuitBreaker(name = "userService", fallbackMethod = "getUserFallback")
public UserDto getUser(Long userId) {
return restTemplate.getForObject(
"http://user-service/api/users/{id}",
UserDto.class, userId);
}
// Fallback metodu — aynı parametre + Exception parametresi
private UserDto getUserFallback(Long userId, Exception ex) {
return new UserDto(userId, "Unknown User", "N/A");
}
}YAML Konfigürasyonu
resilience4j:
circuitbreaker:
instances:
userService:
register-health-indicator: true
sliding-window-type: COUNT_BASED
sliding-window-size: 10 # son 10 çağrıyı izle
failure-rate-threshold: 50 # %50 hata → OPEN
wait-duration-in-open-state: 10s # 10 sn bekle → HALF-OPEN
permitted-number-of-calls-in-half-open-state: 3
minimum-number-of-calls: 5 # en az 5 çağrıdan sonra değerlendir
automatic-transition-from-open-to-half-open-enabled: true
orderService:
sliding-window-type: TIME_BASED
sliding-window-size: 60 # son 60 saniyeyi izle
failure-rate-threshold: 60 # %60 hata → OPEN
wait-duration-in-open-state: 30s
slow-call-duration-threshold: 2s # 2s'den yavaş = slow call
slow-call-rate-threshold: 80 # %80 slow call → OPEN@Retry — Yeniden Deneme
Geçici hatalarda otomatik yeniden deneme:
@Retry(name = "userService", fallbackMethod = "getUserFallback")
@CircuitBreaker(name = "userService", fallbackMethod = "getUserFallback")
public UserDto getUser(Long userId) {
return restTemplate.getForObject(
"http://user-service/api/users/{id}",
UserDto.class, userId);
}resilience4j:
retry:
instances:
userService:
max-attempts: 3
wait-duration: 1s
exponential-backoff-multiplier: 2 # 1s, 2s, 4s
retry-exceptions:
- java.io.IOException
- java.util.concurrent.TimeoutException
ignore-exceptions:
- com.example.BusinessException@RateLimiter — İstek Sınırlama
Belirli bir servise yapılan çağrı sayısını sınırlar:
@RateLimiter(name = "externalApi")
public ExternalData callExternalApi(String query) {
return externalClient.search(query);
}resilience4j:
ratelimiter:
instances:
externalApi:
limit-for-period: 10 # periyot başına 10 istek
limit-refresh-period: 1s # her 1 saniyede yenile
timeout-duration: 500ms # limit aşılınca max bekleme@Bulkhead — İzolasyon
Bir servisin tüm thread'leri tüketmesini engellemek için eş zamanlı çağrı sayısını sınırlar:
@Bulkhead(name = "userService", type = Bulkhead.Type.SEMAPHORE)
public UserDto getUser(Long userId) {
return restTemplate.getForObject(
"http://user-service/api/users/{id}",
UserDto.class, userId);
}resilience4j:
bulkhead:
instances:
userService:
max-concurrent-calls: 25 # max 25 eş zamanlı çağrı
max-wait-duration: 500ms # doluysa max bekleme@TimeLimiter — Zaman Sınırı
Çağrının belirli sürede tamamlanmasını zorlar:
@TimeLimiter(name = "userService")
public CompletableFuture<UserDto> getUser(Long userId) {
return CompletableFuture.supplyAsync(() ->
restTemplate.getForObject(
"http://user-service/api/users/{id}",
UserDto.class, userId));
}resilience4j:
timelimiter:
instances:
userService:
timeout-duration: 3s
cancel-running-future: trueAnnotation Sırası
Birden fazla annotation kullanıldığında çalışma sırası önemlidir (dıştan içe):
Retry → CircuitBreaker → RateLimiter → TimeLimiter → Bulkhead → MethodResilience4j, mikroservis mimarisinde hata yönetiminin temel yapı taşıdır. Servisler arası iletişimde oluşabilecek hataları zarif bir şekilde ele alarak uygulamanızın dayanıklılığını artırır.
AI Asistan
Sorularını yanıtlamaya hazır