
Fehlertoleranz-Bibliothek für Java, die Circuit Breaker, Rate Limiter, Retry, Bulkhead, Timeout und Cache-Dekoratoren für funktionale Programmierung bereitstellt.
= Fehlertoleranz-Bibliothek für funktionale Programmierung :author: Robert Winkler and Bohdan Storozhuk :icons: :toc: macro :numbered: 1 ifdef::env-github[] :tip-caption: 💡 :note-caption: ℹ️ :important-caption: ❗ :caution-caption: 🔥 :warning-caption: ⚠️ endif::[]
image:https://github.com/resilience4j/resilience4j/actions/workflows/gradle-build.yml/badge.svg["Build Status"] image:https://img.shields.io/nexus/r/io.github.resilience4j/resilience4j-circuitbreaker?server=https%3A%2F%2Foss.sonatype.org["Release"] image:https://img.shields.io/nexus/s/io.github.resilience4j/resilience4j-circuitbreaker?server=https%3A%2F%2Foss.sonatype.org["Snapshot"] image:http://img.shields.io/badge/license-ASF2-blue.svg["Apache License 2", link="http://www.apache.org/licenses/LICENSE-2.0.txt"]
image:https://sonarcloud.io/api/project_badges/measure?project=resilience4j_resilience4j&metric=coverage["Coverage", link="https://sonarcloud.io/dashboard?id=resilience4j_resilience4j"] image:https://sonarcloud.io/api/project_badges/measure?project=resilience4j_resilience4j&metric=sqale_rating["Maintainability", link="https://sonarcloud.io/dashboard?id=resilience4j_resilience4j"] image:https://sonarcloud.io/api/project_badges/measure?project=resilience4j_resilience4j&metric=reliability_rating["Reliability", link="https://sonarcloud.io/dashboard?id=resilience4j_resilience4j"] image:https://sonarcloud.io/api/project_badges/measure?project=resilience4j_resilience4j&metric=security_rating["Security", link="https://sonarcloud.io/dashboard?id=resilience4j_resilience4j"] image:https://sonarcloud.io/api/project_badges/measure?project=resilience4j_resilience4j&metric=vulnerabilities["Vulnerabilities", link="https://sonarcloud.io/dashboard?id=resilience4j_resilience4j"] image:https://sonarcloud.io/api/project_badges/measure?project=resilience4j_resilience4j&metric=bugs["Bugs", link="https://sonarcloud.io/dashboard?id=resilience4j_resilience4j"]
toc::[]
== Einführung
Resilience4j ist eine leichtgewichtige Fehlertoleranz-Bibliothek, die für funktionale Programmierung entwickelt wurde. Resilience4j stellt Funktionen höherer Ordnung (Dekorateure) bereit, um jede funktionale Schnittstelle, jeden Lambda-Ausdruck oder jede Methodenreferenz mit einem Circuit Breaker, Rate Limiter, Retry oder Bulkhead zu erweitern. Sie können mehr als einen Dekorateur auf jede funktionale Schnittstelle, jeden Lambda-Ausdruck oder jede Methodenreferenz stapeln. Der Vorteil besteht darin, dass Sie die Dekorateure auswählen können, die Sie benötigen – und sonst nichts.
Resilience4j 3 erfordert Java 21.
// Create a CircuitBreaker with default configuration CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendService");
// Create a Retry with default configuration // 3 retry attempts and a fixed time interval between retries of 500ms Retry retry = Retry.ofDefaults("backendService");
// Create a Bulkhead with default configuration Bulkhead bulkhead = Bulkhead.ofDefaults("backendService");
Supplier supplier = () -> backendService .doSomething(param1, param2);
// Decorate your call to backendService.doSomething() // with a Bulkhead, CircuitBreaker and Retry // **note: you will need the resilience4j-all dependency for this Supplier decoratedSupplier = Decorators.ofSupplier(supplier) .withCircuitBreaker(circuitBreaker) .withBulkhead(bulkhead) .withRetry(retry) .decorate();
// Execute the decorated supplier and recover from any exception String result = Try.ofSupplier(decoratedSupplier) .recover(throwable -> "Hello from Recovery").get();
// When you don't want to decorate your lambda expression, // but just execute it and protect the call by a CircuitBreaker. String result = circuitBreaker .executeSupplier(backendService::doSomething);
// You can also run the supplier asynchronously in a ThreadPoolBulkhead ThreadPoolBulkhead threadPoolBulkhead = ThreadPoolBulkhead .ofDefaults("backendService");
// The Scheduler is needed to schedule a timeout on a non-blocking CompletableFuture ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3); TimeLimiter timeLimiter = TimeLimiter.of(Duration.ofSeconds(1));
NOTE: Mit Resilience4j müssen Sie nicht alles auf einmal nutzen, Sie können https://mvnrepository.com/artifact/io.github.resilience4j[*auswählen, was Sie benötigen*].
== Dokumentation
Einrichtung und Verwendung werden in unserem https://resilience4j.readme.io/docs[Benutzerhandbuch] beschrieben.
https://github.com/resilience4j-docs-ja/resilience4j-docs-ja[有志による日本語訳(非公式) Japanische Übersetzung von Freiwilligen (inoffiziell)]
https://github.com/lmhmhl/Resilience4j-Guides-Chinese[这是Resilience4j的非官方中文文档 Chinesische Übersetzung von Freiwilligen (inoffiziell)]
== Übersicht
Resilience4j bietet mehrere Kernmodule:
Es gibt außerdem Zusatzmodule für Metriken, Feign, Kotlin, Spring, Ratpack, Vertx, RxJava2 und weitere.
NOTE: Die vollständige Liste der Module finden Sie in unserem https://resilience4j.readme.io/docs#section-modularization[Benutzerhandbuch].
TIP: Für das Paket der Kernmodule oder den +Decorators+-Builder siehe https://mvnrepository.com/artifact/io.github.resilience4j/resilience4j-all[resilience4j-all].
== Bewährte Vorgehensweisen
=== Instanzverwaltung: Wann Instanzen geteilt werden sollten und wann nicht
Eines der wichtigsten Konzepte, das Neueinsteiger verstehen müssen, ist, wann separate Instanzen erstellt und wann Instanzen über verschiedene Remote-Dienste oder Backends hinweg geteilt werden sollten.
==== Warum eindeutige Instanzen wichtig sind
Die Erstellung separater Instanzen für jeden Backend-Dienst ist entscheidend für:
==== Instanzbezogene vs. nicht-instanzbezogene Muster
Verschiedene Resilienz-Muster stellen unterschiedliche Anforderungen an die Trennung von Instanzen:
===== Instanzbezogene Muster (dürfen NICHT geteilt werden)
Diese Muster halten einen Zustand vor, der für einen bestimmten Dienst spezifisch ist, und MÜSSEN über separate Instanzen verfügen:
// CORRECT: Separate CircuitBreaker for each service CircuitBreaker paymentServiceCB = CircuitBreaker.ofDefaults("paymentService"); CircuitBreaker inventoryServiceCB = CircuitBreaker.ofDefaults("inventoryService"); CircuitBreaker notificationServiceCB = CircuitBreaker.ofDefaults("notificationService");
// CORRECT: Separate Bulkhead for each service Bulkhead paymentBulkhead = Bulkhead.ofDefaults("paymentService"); Bulkhead inventoryBulkhead = Bulkhead.ofDefaults("inventoryService");
===== Nicht-instanzbezogene Muster (können geteilt werden, besser aber nicht)
Diese Muster erzeugen für jede Ausführung einen neuen Kontext und halten keinen dienstspezifischen Zustand vor:
// TECHNICALLY OK: Retry doesn't maintain state between calls Retry sharedRetry = Retry.ofDefaults("shared");
// BETTER: Unique instances provide better metrics and monitoring Retry paymentRetry = Retry.ofDefaults("paymentService"); Retry inventoryRetry = Retry.ofDefaults("inventoryService");
==== Empfohlener Ansatz: Verwenden Sie immer eindeutige Instanzen
Selbst bei Mustern, die geteilt werden können, ist die Erstellung eindeutiger Instanzen der empfohlene Ansatz, weil:
===== Vollständiges Beispiel: Mehrere Dienste
// You have 3 backend services to protect public class ServiceOrchestrator {
// Payment Service protection
private final CircuitBreaker paymentCB = CircuitBreaker.ofDefaults("paymentService");
private final Retry paymentRetry = Retry.ofDefaults("paymentService");
private final Bulkhead paymentBulkhead = Bulkhead.ofDefaults("paymentService");
// Inventory Service protection
private final CircuitBreaker inventoryCB = CircuitBreaker.ofDefaults("inventoryService");
private final Retry inventoryRetry = Retry.ofDefaults("inventoryService");
private final Bulkhead inventoryBulkhead = Bulkhead.ofDefaults("inventoryService");
// Notification Service protection
private final CircuitBreaker notificationCB = CircuitBreaker.ofDefaults("notificationService");
private final Retry notificationRetry = Retry.ofDefaults("notificationService");
public Order processOrder(OrderRequest request) {
// Each service call is protected by its own set of resilience instances
// Call payment service
Supplier<PaymentResult> paymentCall = () -> paymentService.charge(request);
PaymentResult payment = Decorators.ofSupplier(paymentCall)
.withCircuitBreaker(paymentCB)
.withRetry(paymentRetry)
.withBulkhead(paymentBulkhead)
.decorate()
.get();
// Call inventory service
Supplier<InventoryResult> inventoryCall = () -> inventoryService.reserve(request);
InventoryResult inventory = Decorators.ofSupplier(inventoryCall)
.withCircuitBreaker(inventoryCB)
.withRetry(inventoryRetry)
.withBulkhead(inventoryBulkhead)
.decorate()
.get();
// Call notification service (demonstrates circuit isolation: the notification circuit breaker remains independent of the payment circuit breaker, even if payment opens its circuit in other calls)
Runnable notificationCall = () -> notificationService.send(request);
Decorators.ofRunnable(notificationCall)
.withCircuitBreaker(notificationCB)
.withRetry(notificationRetry)
.decorate()
.run();
return new Order(payment, inventory);
}
==== Verwendung von Registries zur Instanzverwaltung
Resilience4j stellt Registry-Klassen bereit, um mehrere Instanzen effizient zu verwalten:
// Create a registry with custom default configuration CircuitBreakerConfig defaultConfig = CircuitBreakerConfig.custom() .failureRateThreshold(50) .waitDurationInOpenState(Duration.ofSeconds(30)) .build();
CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(defaultConfig);
// Get or create instances by name CircuitBreaker paymentCB = registry.circuitBreaker("paymentService"); CircuitBreaker inventoryCB = registry.circuitBreaker("inventoryService");
NOTE: Registries sind besonders nützlich in Spring-Boot-Anwendungen, wo sie automatisch konfiguriert werden und Instanzen bedarfsorientiert auf der Grundlage von Konfigurationseigenschaften erstellt werden.
==== Zusammenfassung
[cols="<.<*", options="header"] |=== |Muster |Kann geteilt werden? |Sollte geteilt werden? |Warum?
|CircuitBreaker |Nein |Nein |Der Zustand ist dienstspezifisch. Durch Teilen führen die Fehler eines Dienstes dazu, dass alle anderen betroffen sind.
|Bulkhead |Nein |Nein |Die Grenzen für gleichzeitige Aufrufe müssen pro Dienst isoliert sein, um eine ordnungsgemäße Ressourcenverwaltung zu gewährleisten.
|RateLimiter |Nein |Nein |Ratenbegrenzungen sind typischerweise pro Dienst unterschiedlich, und Teilen würde den Zweck zunichtemachen.
|Retry |Ja |Nein |Obwohl das Teilen technisch unbedenklich ist, bieten eindeutige Instanzen bessere Metriken und Beobachtbarkeit.
|TimeLimiter |Ja |Nein |Obwohl das Teilen technisch unbedenklich ist, bieten eindeutige Instanzen bessere Metriken und Beobachtbarkeit.
|Cache |Kommt darauf an |Kommt darauf an |Nur teilen, wenn die zwischengespeicherten Daten über die Anwendungsfälle hinweg wirklich identisch sind.
|===
== Resilienz-Muster
[cols="<.<*", options="header"] |=== |Name |Wie funktioniert es? |Beschreibung |Links
|Retry |wiederholt fehlgeschlagene Ausführungen |Viele Fehler sind vorübergehend und können sich nach einer kurzen Verzögerung von selbst beheben. |<<circuitbreaker-retry-fallback,Übersicht>>, https://resilience4j.readme.io/docs/retry[Dokumentation], https://resilience4j.readme.io/docs/getting-started-3#annotations[Spring]
|Circuit Breaker |unterbricht mögliche Fehler vorübergehend |Wenn ein System ernsthaft zu kämpfen hat, ist schnelles Scheitern besser, als Clients warten zu lassen. |<<circuitbreaker-retry-fallback,Übersicht>>, https://resilience4j.readme.io/docs/circuitbreaker[Dokumentation], https://resilience4j.readme.io/docs/feign[Feign], https://resilience4j.readme.io/docs/getting-started-3#annotations[Spring]
|Rate Limiter |begrenzt Ausführungen pro Zeitraum |Begrenzen Sie die Rate eingehender Anfragen. |<<ratelimiter,Übersicht>>, https://resilience4j.readme.io/docs/ratelimiter[Dokumentation], https://resilience4j.readme.io/docs/feign[Feign], https://resilience4j.readme.io/docs/getting-started-3#annotations[Spring]
|Time Limiter |begrenzt die Dauer der Ausführung |Nach einem bestimmten Warteintervall ist ein erfolgreiches Ergebnis unwahrscheinlich. |https://resilience4j.readme.io/docs/timeout[Dokumentation], https://resilience4j.readme.io/docs/getting-started-3#annotations[Spring]
|Bulkhead |begrenzt gleichzeitige Ausführungen |Ressourcen werden in Pools isoliert, sodass die anderen weiterarbeiten, wenn eine ausfällt. |<<bulkhead,Übersicht>>, https://resilience4j.readme.io/docs/bulkhead[Dokumentation], https://resilience4j.readme.io/docs/getting-started-3#annotations[Spring]
|Cache |merkt sich ein erfolgreiches Ergebnis |Ein gewisser Anteil der Anfragen kann ähnlich sein. |https://resilience4j.readme.io/docs/cache[Dokumentation]
|Fallback |liefert ein alternatives Ergebnis bei Fehlern |Es wird weiterhin Fehler geben - planen Sie, was Sie tun, wenn es so weit ist. |<<circuitbreaker-retry-fallback,Try::recover>>, https://resilience4j.readme.io/docs/getting-started-3#section-annotations[Spring], https://resilience4j.readme.io/docs/feign[Feign]
|===
Die obige Tabelle basiert auf https://github.com/App-vNext/Polly#resilience-policies[Polly: Resilienz-Richtlinien].
NOTE: Weitere Informationen zu Resilienz-Mustern finden Sie im Abschnitt link:#Talks[Talks]. Erfahren Sie in unserem https://resilience4j.readme.io/docs/getting-started-2[Benutzerhandbuch] mehr über die Komponenten.
== Spring Boot
Die Einrichtung und Verwendung in Spring Boot 3 wird https://github.com/resilience4j/resilience4j-spring-boot3-demo[hier] demonstriert.
Unterstützung für virtuelle Threads (Java 21 Project Loom) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Ab Resilience4j 3 können Sie die internen Scheduler auf Java-virtuelle Threads umstellen.
Dasselbe kann auch über ein JVM-Argument aktiviert werden:``` -Dresilience4j.thread.type=virtual
Wenn die Eigenschaft (oder Systemeigenschaft) weggelassen wird, verwendet die Bibliothek reguläre *Plattform*-Threads.
== Verwendungsbeispiele
[[circuitbreaker-retry-fallback]]
=== CircuitBreaker, Retry und Fallback
Das folgende Beispiel zeigt, wie ein Lambda-Ausdruck (Supplier) mit einem CircuitBreaker dekoriert wird und wie der Aufruf bei Auftreten einer Ausnahme höchstens dreimal wiederholt wird. Sie können das Warteintervall zwischen den Wiederholungen konfigurieren und außerdem einen benutzerdefinierten Backoff-Algorithmus einrichten.
Das Beispiel verwendet die Try-Monade von Vavr, um sich von einer Ausnahme zu erholen und einen anderen Lambda-Ausdruck als Fallback aufzurufen, wenn selbst alle Wiederholungen fehlgeschlagen sind.
[source,java]
----
// Simulates a Backend Service
public interface BackendService {
String doSomething();
}
// Create a CircuitBreaker (use default configuration)
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName");
// Create a Retry with at most 3 retries and a fixed time interval between retries of 500ms
Retry retry = Retry.ofDefaults("backendName");
// Decorate your call to BackendService.doSomething() with a CircuitBreaker
Supplier<String> decoratedSupplier = CircuitBreaker
.decorateSupplier(circuitBreaker, backendService::doSomething);
// Decorate your call with automatic retry
decoratedSupplier = Retry
.decorateSupplier(retry, decoratedSupplier);
// Use of Vavr's Try to
// execute the decorated supplier and recover from any exception
String result = Try.ofSupplier(decoratedSupplier)
.recover(throwable -> "Hello from Recovery").get();
// When you don't want to decorate your lambda expression,
// but just execute it and protect the call by a CircuitBreaker.
String result = circuitBreaker.executeSupplier(backendService::doSomething);
----
==== CircuitBreaker und RxJava2
Das folgende Beispiel zeigt, wie ein Observable mithilfe des benutzerdefinierten RxJava-Operators dekoriert wird.
[source,java]
----
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName");
Observable.fromCallable(backendService::doSomething)
.compose(CircuitBreakerOperator.of(circuitBreaker))
----
HINWEIS: Resilience4j bietet auch RxJava-Operatoren für `+RateLimiter+`, `+Bulkhead+`, `+TimeLimiter+` und `+Retry+` an.
Erfahren Sie mehr in unserem *https://resilience4j.readme.io/docs/getting-started-2[Benutzerhandbuch]*.
==== CircuitBreaker und Spring Reactor
Das folgende Beispiel zeigt, wie ein Mono mithilfe des benutzerdefinierten Reactor-Operators dekoriert wird.
[source,java]
----
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName");
Mono.fromCallable(backendService::doSomething)
.transformDeferred(CircuitBreakerOperator.of(circuitBreaker))
----
HINWEIS: Resilience4j bietet auch Reactor-Operatoren für `+RateLimiter+`, `+Bulkhead+`, `+TimeLimiter+` und `+Retry+` an.
Erfahren Sie mehr in unserem *https://resilience4j.readme.io/docs/getting-started-1[Benutzerhandbuch]*.
[[ratelimiter]]
=== RateLimiter
Das folgende Beispiel zeigt, wie die Aufrufrate einer Methode auf nicht mehr als 1 Anfrage pro Sekunde beschränkt wird.
[source,java]
----
// Create a custom RateLimiter configuration
RateLimiterConfig config = RateLimiterConfig.custom()
.timeoutDuration(Duration.ofMillis(100))
.limitRefreshPeriod(Duration.ofSeconds(1))
.limitForPeriod(1)
.build();
// Create a RateLimiter
RateLimiter rateLimiter = RateLimiter.of("backendName", config);
// Decorate your call to BackendService.doSomething()
Supplier<String> restrictedSupplier = RateLimiter
.decorateSupplier(rateLimiter, backendService::doSomething);
// First call is successful
Try<String> firstTry = Try.ofSupplier(restrictedSupplier);
assertThat(firstTry.isSuccess()).isTrue();
// Second call fails, because the call was not permitted
Try<String> secondTry = Try.of(restrictedSupplier);
assertThat(secondTry.isFailure()).isTrue();
assertThat(secondTry.getCause()).isInstanceOf(RequestNotPermitted.class);
----
[[bulkhead]]
=== Bulkhead
Es gibt zwei Isolationsstrategien und Bulkhead-Implementierungen.
==== SemaphoreBulkhead
Das folgende Beispiel zeigt, wie ein Lambda-Ausdruck mit einem Bulkhead dekoriert wird.
Ein Bulkhead kann verwendet werden, um die Anzahl paralleler Ausführungen zu begrenzen.
Diese Bulkhead-Abstraktion sollte über verschiedene Threading- und IO-Modelle hinweg gut funktionieren.
Sie basiert auf einem Semaphor und bietet – anders als Hystrix – keine Option für einen „Shadow"-Thread-Pool.
[source,java]
----
// Create a custom Bulkhead configuration
BulkheadConfig config = BulkheadConfig.custom()
.maxConcurrentCalls(150)
.maxWaitDuration(100)
.build();
Bulkhead bulkhead = Bulkhead.of("backendName", config);
Supplier<String> supplier = Bulkhead
.decorateSupplier(bulkhead, backendService::doSomething);
----
[[threadpoolbulkhead]]
==== ThreadPoolBulkhead
Das folgende Beispiel zeigt, wie ein Lambda-Ausdruck mit einem ThreadPoolBulkhead verwendet wird, der eine begrenzte Warteschlange und einen festen Thread-Pool nutzt.
[source,java]
----
// Create a custom ThreadPoolBulkhead configuration
ThreadPoolBulkheadConfig config = ThreadPoolBulkheadConfig.custom()
.maxThreadPoolSize(10)
.coreThreadPoolSize(2)
.queueCapacity(20)
.build();
ThreadPoolBulkhead bulkhead = ThreadPoolBulkhead.of("backendName", config);
// Decorate or execute immediately a lambda expression with a ThreadPoolBulkhead.
Supplier<CompletionStage<String>> supplier = ThreadPoolBulkhead
.decorateSupplier(bulkhead, backendService::doSomething);
CompletionStage<String> execution = bulkhead
.executeSupplier(backendService::doSomething);
----
[[events]]
== Emittierte Ereignisse konsumieren
Die Komponenten `+CircuitBreaker+`, `+RateLimiter+`, `+Cache+`, `+Bulkhead+`, `+TimeLimiter+` und `+Retry+` emittieren einen Ereignisstrom.
Er kann zur Protokollierung, für Assertions und jeden anderen Zweck konsumiert werden.
=== Beispiele
Ein `+CircuitBreakerEvent+` kann ein Zustandsübergang, ein Zurücksetzen des CircuitBreakers, ein erfolgreicher Aufruf, ein aufgezeichneter Fehler oder ein ignorierter Fehler sein.
Alle Ereignisse enthalten zusätzliche Informationen wie den Zeitpunkt der Ereigniserstellung und die Verarbeitungsdauer des Aufrufs.
Wenn Sie Ereignisse konsumieren möchten, müssen Sie einen Ereigniskonsumenten registrieren.
[source,java]
----
circuitBreaker.getEventPublisher()
.onSuccess(event -> logger.info(...))
.onError(event -> logger.info(...))
.onIgnoredError(event -> logger.info(...))
.onReset(event -> logger.info(...))
.onStateTransition(event -> logger.info(...));
// Or if you want to register a consumer listening to all events, you can do:
circuitBreaker.getEventPublisher()
.onEvent(event -> logger.info(...));
----
Sie können RxJava- oder Spring-Reactor-Adapter verwenden, um den `+EventPublisher+` in einen Reactive Stream umzuwandeln.
Der Vorteil eines Reactive Streams besteht darin, dass Sie den `+observeOn+`-Operator von RxJava verwenden können, um einen anderen Scheduler festzulegen, den der CircuitBreaker zum Senden von Benachrichtigungen an seine Observer/Konsumenten verwendet.
[source,java]
----
RxJava2Adapter.toFlowable(circuitBreaker.getEventPublisher())
.filter(event -> event.getEventType() == Type.ERROR)
.cast(CircuitBreakerOnErrorEvent.class)
.subscribe(event -> logger.info(...))
----
HINWEIS: Sie können auch Ereignisse von anderen Komponenten konsumieren.
Erfahren Sie mehr in unserem *https://resilience4j.readme.io/[Benutzerhandbuch]*.
== Vorträge
[cols="4*"]
|===
|0:34
|https://www.youtube.com/watch?v=kR2sm1zelI4[Kampf der Circuit Breaker: Resilience4J vs. Istio]
|Nicolas Frankel
|GOTO Berlin
|0:33
|https://www.youtube.com/watch?v=AwcjOhD91Q0[Kampf der Circuit Breaker: Istio vs. Hystrix/Resilience4J]
|Nicolas Frankel
|JFuture
|0:42
|https://www.youtube.com/watch?v=KosSsZEqS-k&t=157[Resilienz-Muster in der Post-Hystrix-Welt]
|Tomasz Skowroński
|Cloud Native Warsaw
|0:52
|https://www.youtube.com/watch?v=NHVxrLb3jFI[Entwicklung robuster und belastbarer Apps mit Spring Boot und Resilience4j]
|David Caron
|SpringOne
|0:22
|https://www.youtube.com/watch?v=gvDvOWtPLVY&t=140[Hystrix ist tot, was nun?]
|Tomasz Skowroński
|DevoxxPL
|===
== Unternehmen, die Resilience4j verwenden
* *Deutsche Telekom* (In einer Anwendung mit über 400 Millionen Anfragen pro Tag)
* *AOL* (In einer Anwendung mit geringen Latenzanforderungen)
* *Netpulse* (In einem System mit über 40 Integrationen)
* *wescale.de* (In einer B2B-Integrationsplattform)
* *Topia* (In einer HR-Anwendung, die mit einer Microservices-Architektur entwickelt wurde)
* *Auto Trader Group plc* (Der größte britische digitale Automobilmarktplatz)
* *PlayStation Network* (Ein Plattform-Backend)
* *TUI InfoTec GmbH* (Backend-Anwendungen in den Buchungs- und Reservierungs-Workflow-Strömen für Unterkünfte)
== Lizenz
Copyright 2020 Robert Winkler, Bohdan Storozhuk, Mahmoud Romeh, Dan Maas und andere
Lizenziert unter der Apache License, Version 2.0 (der „Lizenz");
Sie dürfen diese Datei nur in Übereinstimmung mit der Lizenz verwenden.
Eine Kopie der Lizenz erhalten Sie unter
http://www.apache.org/licenses/LICENSE-2.0
Sofern nicht durch geltendes Recht vorgeschrieben oder schriftlich vereinbart, wird die unter der Lizenz verteilte Software auf einer „AS IS"-BASIS verteilt,
OHNE GEWÄHRLEISTUNG ODER BEDINGUNGEN JEGLICHER ART, weder ausdrücklich noch stillschweigend.
Die Lizenz enthält die spezifischen Berechtigungen und Einschränkungen, die im Rahmen der Lizenz gelten.