![[17.0.0.3 und höher]](../ng_v17003plus.gif)
Ausfallsicherheit von Mikroservices in Liberty verbessern
Sie können das Feature "MicroProfile Fault Tolerance" verwenden, um Serviceaufrufe mit erhöhter Ausfallsicherheit zu versehen. Dieses Feature ist eine Implementierung der Spezifikation "Eclipse Microprofile Fault Tolerance 1.0". Das Feature verwendet die Open-Source-Bibliothek "Failsafe", um ein Programmiermodell bereitzustellen, das die Ausfallsicherheit von Mikroservices mithilfe von Mustern unterstützt, die Wiederholungen, Sicherungen, Bulkheads, Zeitlimits und Fallbacks enthalten.
Vorbereitende Schritte
Vorgehensweise
- Fügen Sie das Feature mpFaultTolerance-1.0 einem Element featureManager
in Ihrer Datei server.xml hinzu.
<featureManager> <feature>mpFaultTolerance-1.0</feature> </featureManager>
- Verwenden Sie ein Code-Snippet, um die Ausfallsicherheit von Mikroservices zu verbessern. Eine Fehlertoleranzsicherung bietet Systemen eine Fail-Fast-Methode (frühzeitige Erkennung von Fehlern). Es setzt die Ausführung eines Service vorübergehend aus, um zu verhindern, dass der Service ein System überlastet.Mit einer Wiederholungsrichtlinie (RetryPolicy) für Fehlertoleranz können Sie konfigurieren, wann Services wiederholt werden. Mit einem Fehlertoleranz-Fallback kann angegeben werden, welcher Service verwendet werden soll, wenn der Hauptservice ausfällt.Ein Bulkhead für Fehlertoleranz beschränkt die Anzahl gleichzeitiger Aufrufe eines Service. Der Bulkhead beschränkt die Systemressourcen, die von Serviceaufrufen verwendet werden können. Das Code-Snippet setzt voraus, dass zusätzlich zum Feature mpFaultTolerance-1.0 das Liberty-Feature concurrent-1.0 in der Datei server.xml angegeben ist.
Code-Snippet 1 für CircuitBreaker: CircuitBreakerBean mit konfiguriertem CircuitBreaker und Timeout erstellen.
@RequestScopedpublic class CircuitBreakerBean { private int executionCounterA = 0; // Die kombinierte Auswirkung der angegebenen Werte für requestVolumeThreshold und failureRatio ist, dass 3 // Fehler das Öffnen des Circuit auslösen. // Nach einer Verzögerung von einer Sekunde lässt der Circuit neue Versuche zum Aufrufen des Service zu. @CircuitBreaker(delay = 1, delayUnit = ChronoUnit.SECONDS, requestVolumeThreshold = 3, failureRatio = 1.0) // Ein Service hat das zulässige Zeitlimit nach 3 Sekunden überschritten. @Timeout(value = 3, unit = ChronoUnit.SECONDS) public String serviceA() { executionCounterA++; if (executionCounterA <= 3) { // 10 Sekunden Inaktivität erzwingen eine Zeitlimitüberschreitung try { Thread.sleep(10000); } catch (InterruptedException e) { System.out.println("serviceA interrupted"); } }
Code-Snippet 2 für CircuitBreaker: CircuitBreakerBean verwenden.
@Inject CircuitBreakerBean bean; // FaultToleranceBean mit CircuitBreaker sollte 3 mal fehlschlagen for (int i = 0; i < 3; i++) { try { bean.serviceA(); throw new AssertionError("TimeoutException not caught"); } catch (TimeoutException e) { // erwartet } } // CircuitBreaker sollte offen sein, Aufruf an serviceA sollte eine Ausnahme des Typs // CircuitBreakerOpenException auslösen. try { bean.serviceA(); throw new AssertionError("CircuitBreakerOpenException not caught"); } catch (CircuitBreakerOpenException e) { // erwartet } // Zeit zum erneuten Schließen des Circuit zulassen Thread.sleep(3000); // CircuitBreaker sollte geschlossen sein und serviceA jetzt erfolgreich String res = bean.serviceA(); if (!"serviceA: 4".equals(res)) { throw new AssertionError("Bad Result: " + res); }
Code-Snippet 1 für Fallback und Retry: FTServiceBean mit konfigurierter FallbackHandler- und Retry-Richtlinie.
@RequestScopedpublic class FTServiceBean { // Annotatieren Sie serviceA mit einem benannten FallbackHandler und einer Retry-Richtlinie mit Angabe // der Anzahl für Wiederholungen. @Retry(maxRetries = 2) @Fallback(StringFallbackHandler.class) public String serviceA() { throw new RuntimeException("Connection failed"); return null; } }
Code-Snippet 2 für Fallback und Retry: Ein FallbackHandler, der Code, der verarbeitet wird, wenn der Hauptservice fehlschlägt
@Dependentpublic class StringFallbackHandler implements FallbackHandler<String> { @Override public String handle(ExecutionContext context) { return "fallback for " + context.getMethod().getName(); } }
Code-Snippet 2 für Fallback and Retry: FTServiceBean verwenden
private @Inject FTServiceBean ftServiceBean; try { // Aufruf von serviceA, der im Fall eines Fehlers zweimal wiederholt wird, danach // wird der FallbackHandler verarbeitet. String result = ftServiceBean.serviceA(); if(!result.contains("serviceA")) throw new AssertionError("The message should be \"fallback for serviceA\""); } catch(RuntimeException ex) { throw new AssertionError("serviceA should not throw a RuntimeException"); }
Code-Snippet 1 für Bulkhead: BulkheadBean mit konfiguriertem Bulkhead erstellen
@RequestScoped@Asynchronous public class BulkheadBean { private final AtomicInteger connectATokens = new AtomicInteger(0); // Konfigurieren Sie einen Bulkhead, der maximal 2 gleichzeitig ausgeführte Threads unterstützt. @Bulkhead(maxThreads = 2) public Future<Boolean> connectA(String data) throws InterruptedException { System.out.println("connectA starting " + data); int token = connectATokens.incrementAndGet(); try { if (token > 2) { throw new RuntimeException("Too many threads in connectA[" + data + "]: " + token); } Thread.sleep(5000); return CompletableFuture.completedFuture(Boolean.TRUE); } finally { connectATokens.decrementAndGet(); System.out.println("connectA complete " + data); } } }
Code-Snippet 2 für Bulkhead: BulkheadBean verwenden
@Inject BulkheadBean bean; // poolSize von connectA ist 2 // Der ersten beiden Aufrufe an connectA sollten sofort ausgeführt werden, parallel ungefähr alle // 5 Sekunden Future<Boolean> future1 = bean.connectA("One"); Thread.sleep(100); Future<Boolean> future2 = bean.connectA("Two"); Thread.sleep(100); // Die nächsten zwei Aufrufe an connectA sollten warten, bis die ersten zwei Aufrufe beendet wurden. Future<Boolean> future3 = bean.connectA("Three"); Thread.sleep(100); Future<Boolean> future4 = bean.connectA("Four"); Thread.sleep(100); // Gesamtzeit sollte etwas über 10 Sekunden sein. Thread.sleep(11000); if (!future1.get(1000, TimeUnit.MILLISECONDS)) { throw new AssertionError("Future1 did not complete properly"); } if (!future2.get(1000, TimeUnit.MILLISECONDS)) { throw new AssertionError("Future2 did not complete properly"); } if (!future3.get(1000, TimeUnit.MILLISECONDS)) { throw new AssertionError("Future3 did not complete properly"); } if (!future4.get(1000, TimeUnit.MILLISECONDS)) { throw new AssertionError("Future4 did not complete properly"); }

Dateiname: twlp_microprofile_fault_tolerance.html