Einführung
In der modernen Softwareentwicklung ist die Zuverlässigkeit von Anwendungen von entscheidender Bedeutung. Mikroservices-Architekturen und verteilte Systeme sind sehr populär geworden, was jedoch auch Herausforderungen mit sich bringt, insbesondere bei der Handhabung von Fehlern und der Resilienz von Anwendungen. Eine der Techniken zur Verbesserung der Resilienz ist das Circuit Breaker Muster. Resilience4j ist eine leichtgewichtige, auf Funktionen basierende Java-Bibliothek, die verschiedene Resilienz-Muster unterstützt, darunter auch Circuit Breaker. In diesem Artikel werden wir detailliert erläutern, wie man mit Resilience4j einen Circuit Breaker in Java implementiert und die verschiedenen Konfigurationsoptionen erklärt.
Was ist ein Circuit Breaker?
Ein Circuit Breaker ist ein Designmuster, das dazu dient, den Ausfall eines Services zu verhindern, indem Anfragen an einen fehlerhaften Service unterbrochen werden. Es arbeitet ähnlich wie ein elektrischer Sicherungsautomat und hat drei Zustände:
- Closed: Der Circuit Breaker leitet die Anfragen weiter.
- Open: Der Circuit Breaker blockiert alle Anfragen und lässt keine weiteren Versuche zu, bis eine festgelegte Wartezeit abgelaufen ist.
- Half-Open: Der Circuit Breaker lässt eine begrenzte Anzahl von Anfragen durch, um zu testen, ob der Service wieder funktionsfähig ist.
Einrichtung von Resilience4j
Bevor wir mit der Implementierung beginnen, müssen wir die notwendigen Abhängigkeiten zu unserem Projekt hinzufügen. Für ein Maven-Projekt fügen wir folgende Abhängigkeit in der pom.xml
hinzu:
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-circuitbreaker</artifactId>
<version>1.7.1</version>
</dependency>
Code-Sprache: HTML, XML (xml)
Für Gradle-Projekte fügen wir folgende Zeile in der build.gradle
Datei hinzu:
implementation 'io.github.resilience4j:resilience4j-circuitbreaker:1.7.1'
Code-Sprache: JavaScript (javascript)
Grundlegende Implementierung
Nun, da die Abhängigkeiten hinzugefügt sind, können wir mit der Implementierung eines einfachen Circuit Breakers beginnen. Zuerst müssen wir eine Instanz des Circuit Breakers erstellen und konfigurieren.
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import java.time.Duration;
public class CircuitBreakerExample {
public static void main(String[] args) {
// Konfiguration des Circuit Breakers
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50) // 50% Fehlerrate
.waitDurationInOpenState(Duration.ofSeconds(10)) // 10 Sekunden im Open-Zustand
.slidingWindowSize(20) // Größe des Sliding Window
.build();
// Registrierung des Circuit Breakers
CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(config);
// Erstellen des Circuit Breakers
CircuitBreaker circuitBreaker = registry.circuitBreaker("exampleCircuitBreaker");
// Anwendung des Circuit Breakers
Supplier<String> decoratedSupplier = CircuitBreaker
.decorateSupplier(circuitBreaker, CircuitBreakerExample::remoteServiceCall);
// Ausführen des dekorierten Suppliers
String result = Try.ofSupplier(decoratedSupplier)
.recover(throwable -> "Fallback").get();
System.out.println(result);
}
// Beispiel für einen Remote Service Call, der fehlschlägt
public static String remoteServiceCall() {
throw new RuntimeException("Service not available");
}
}
Code-Sprache: JavaScript (javascript)
In diesem Beispiel konfigurieren wir einen Circuit Breaker mit einer Fehlerrate von 50%, einer Wartezeit von 10 Sekunden im offenen Zustand und einem Sliding Window von 20 Anfragen. Wir dekorieren dann einen Supplier, der einen Remote Service Call simuliert. Wenn der Remote Service Call fehlschlägt, greift der Circuit Breaker ein und der Fallback-Mechanismus liefert einen Standardwert zurück.
Erweiterte Konfigurationsoptionen
Die grundlegende Implementierung kann durch verschiedene Konfigurationsoptionen erweitert werden, um spezifischeren Anforderungen gerecht zu werden. Hier sind einige erweiterte Konfigurationsoptionen:
Fehlerrate (failureRateThreshold)
Die Fehlerrate definiert den Prozentsatz der fehlgeschlagenen Anfragen, bei dem der Circuit Breaker in den Open-Zustand wechselt. In unserem Beispiel bedeutet eine Fehlerrate von 50%, dass der Circuit Breaker in den Open-Zustand wechselt, wenn mindestens die Hälfte der Anfragen innerhalb des Sliding Windows fehlschlagen.
.failureRateThreshold(50) // 50% Fehlerrate
Code-Sprache: JavaScript (javascript)
Wartezeit im offenen Zustand (waitDurationInOpenState)
Die Wartezeit definiert, wie lange der Circuit Breaker im Open-Zustand bleibt, bevor er in den Half-Open-Zustand wechselt. In unserem Beispiel beträgt diese Wartezeit 10 Sekunden.
.waitDurationInOpenState(Duration.ofSeconds(10)) // 10 Sekunden im Open-Zustand
Code-Sprache: JavaScript (javascript)
Sliding Window (slidingWindowSize)
Das Sliding Window definiert die Anzahl der Anfragen, die zur Berechnung der Fehlerrate herangezogen werden. In unserem Beispiel umfasst das Sliding Window 20 Anfragen. Es gibt zwei Modi für das Sliding Window:
- Count-based sliding window: Das Fenster basiert auf einer festen Anzahl von Anfragen.
- Time-based sliding window: Das Fenster basiert auf einem festen Zeitintervall.
In unserem Beispiel verwenden wir ein Count-based sliding window.
.slidingWindowSize(20) // Größe des Sliding Window
Code-Sprache: JavaScript (javascript)
Ausnahmen (recordExceptions und ignoreExceptions)
Wir können spezifische Ausnahmen definieren, die vom Circuit Breaker als Fehler gezählt oder ignoriert werden sollen.
.recordExceptions(RuntimeException.class, TimeoutException.class) // Ausnahmen, die gezählt werden
.ignoreExceptions(IgnoredException.class) // Ausnahmen, die ignoriert werden
Code-Sprache: JavaScript (javascript)
Ereignisüberwachung
Resilience4j bietet auch die Möglichkeit, Ereignisse zu überwachen und zu protokollieren, die vom Circuit Breaker ausgelöst werden. Dies ist nützlich, um Einblicke in das Verhalten des Circuit Breakers zu erhalten.
circuitBreaker.getEventPublisher()
.onSuccess(event -> System.out.println("Call succeeded"))
.onError(event -> System.out.println("Call failed"))
.onStateTransition(event -> System.out.println("State transitioned to " + event.getStateTransition()));
Code-Sprache: CSS (css)
Integration mit Spring Boot
Resilience4j lässt sich auch nahtlos in Spring Boot Anwendungen integrieren. Hier ist ein einfaches Beispiel:
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class MyService {
private final RestTemplate restTemplate;
public MyService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@CircuitBreaker(name = "myServiceCircuitBreaker", fallbackMethod = "fallback")
public String callExternalService() {
return restTemplate.getForObject("http://example.com/api", String.class);
}
public String fallback(Throwable throwable) {
return "Fallback response";
}
}
Code-Sprache: JavaScript (javascript)
In diesem Beispiel wird ein REST-Aufruf mit einem Circuit Breaker geschützt. Wenn der Aufruf fehlschlägt, wird die fallback
Methode aufgerufen.
Fazit
Die Resilience4j-Bibliothek bietet eine leistungsfähige und flexible Möglichkeit, das Circuit Breaker Muster in Java-Anwendungen zu implementieren. Durch die Verwendung von Resilience4j können Entwickler die Resilienz ihrer Anwendungen erheblich verbessern und die Auswirkungen von Fehlern und Ausfällen minimieren. Mit einer Vielzahl von Konfigurationsmöglichkeiten und der einfachen Integration in Spring Boot ist Resilience4j eine ausgezeichnete Wahl für die Implementierung von Resilienzmustern in modernen Java-Anwendungen.