![[17.0.0.3 and later]](../ng_v17003plus.gif)
Amélioration de la résilience des microservices dans Liberty
Vous pouvez utiliser la fonction MicroProfile Fault Tolerance pour améliorer la résilience des appels de service. Cette fonction est une implémentation d'Eclipse Microprofile Fault Tolerance Specification 1.0. La fonction utilise la bibliothèque open source Failsafe pour fournir un modèle de programmation permettant de prendre en charge les microservices résilients via des modèles qui incluent des nouvelles tentatives, des disjoncteurs, des cloisons, des délais d'attente et des rétromigrations.
Avant de commencer
Procédure
- Ajoutez la fonction mpFaultTolerance-1.0 à un élément featureManager dans votre fichier server.xml.
<featureManager> <feature>mpFaultTolerance-1.0</feature> </featureManager>
- Utilisez un fragment de code pour améliorer la résilience des microservices. Un disjoncteur de tolérance aux pannes fournit un mode d'interruption rapide aux systèmes. Il désactive temporairement l'exécution d'un service afin d'éviter que le service ne surcharge un système.Une stratégie de nouvelle tentative de tolérance aux pannes offre un moyen de configurer quand les services doivent être re-essayés. Si le service principal échoue, une rétromigration de tolérance aux pannes peut indiquer quel service doit être utilisé.
- Fragment de code Fallback et Retry 1 : FTServiceBean avec FallbackHandler et la politique de tentatives configurés
- Fragment de code Fallback et Retry 2 : FallbackHandler, le code qui est actionné en cas de défaillance du service principal
- Fragment de code Fallback et Retry 3 : Utilisation de FTServiceBean
Une cloison de tolérance aux pannes limite le nombre d'appels simultanés d'un service. La cloison limite le nombre de ressources système que les appels de service peuvent utiliser. Le fragment de code nécessite que la fonction Liberty concurrent-1.0 soit spécifiée dans le fichier server.xml en plus de la fonction mpFaultTolerance-1.0.Fragment de code CircuitBreaker 1 : Créez un CircuitBreakerBean avec CircuitBreaker et Timeout configurés.
@RequestScoped public class CircuitBreakerBean { private int executionCounterA = 0; // The combined effect of the specified requestVolumeThreshold and failureRatio is that 3 // failures will trigger the circuit to open. // After a 1 second delay the Circuit will allow fresh attempts to invoke the service. @CircuitBreaker(delay = 1, delayUnit = ChronoUnit.SECONDS, requestVolumeThreshold = 3, failureRatio = 1.0) // A service is considered to have timed out after 3 seconds @Timeout(value = 3, unit = ChronoUnit.SECONDS) public String serviceA() { executionCounterA++; if (executionCounterA <= 3) { //Sleep for 10 secs to force a timeout try { Thread.sleep(10000); } catch (InterruptedException e) { System.out.println("serviceA interrupted"); } }
Fragment de code CircuitBreaker 2 : Utilisation de CircuitBreakerBean.
@Inject CircuitBreakerBean bean; // FaultTolerance bean with circuit breaker, should fail 3 times for (int i = 0; i < 3; i++) { try { bean.serviceA(); throw new AssertionError("TimeoutException not caught"); } catch (TimeoutException e) { //expected } } // The CircuitBreaker should be open, so calling serviceA should generate a // CircuitBreakerOpenException. try { bean.serviceA(); throw new AssertionError("CircuitBreakerOpenException not caught"); } catch (CircuitBreakerOpenException e) { //expected } //allow time for the circuit to re-close Thread.sleep(3000); // The CircuitBreaker should be closed and serviceA should now succeed. String res = bean.serviceA(); if (!"serviceA: 4".equals(res)) { throw new AssertionError("Bad Result: " + res); }
Fragment de code Fallback et Retry 1 : FTServiceBean avec FallbackHandler et la politique Retry configurés.
@RequestScoped public class FTServiceBean { // Annotate serviceA with a named FallbackHandler and a Retry policy specifying the // number of retries. @Retry(maxRetries = 2) @Fallback(StringFallbackHandler.class) public String serviceA() { throw new RuntimeException("Connection failed"); return null; } }
Fragment de code Fallback et Retry 2 : Un FallbackHandler, le code qui est actionné en cas de défaillance du service principal.
@Dependent public class StringFallbackHandler implements FallbackHandler<String> { @Override public String handle(ExecutionContext context) { return "fallback for " + context.getMethod().getName(); } }
Fragment de code Fallback et Retry 3 : Utilisation de FTServiceBean.
private @Inject FTServiceBean ftServiceBean; try { // Call serviceA, which will be retried twice in the event of failure, after which // the FallbackHandler will be driven. 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"); }
Fragment de code Bulkhead 1 : Créez un BulkheadBean avec Bulkhead configuré.
@RequestScoped @Asynchronous public class BulkheadBean { private final AtomicInteger connectATokens = new AtomicInteger(0); // Configure a Bulkhead that supports at most 2 concurrent threads. @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); } } }
Fragment de code Bulkhead 2 : Utilisez le BulkheadBean.
@Inject BulkheadBean bean; // connectA has a poolSize of 2 // The first two calls to connectA should be run straight away, in parallel, each around // 5 seconds Future<Boolean> future1 = bean.connectA("One"); Thread.sleep(100); Future<Boolean> future2 = bean.connectA("Two"); Thread.sleep(100); // The next two calls to connectA should wait until the first 2 have finished Future<Boolean> future3 = bean.connectA("Three"); Thread.sleep(100); Future<Boolean> future4 = bean.connectA("Four"); Thread.sleep(100); //total time should be just over 10s 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"); }

Nom du fichier : twlp_microprofile_fault_tolerance.html