Gids voor Resilience4j

1. Overzicht

In deze tutorial zullen we praten over de Resilience4j-bibliotheek.

De bibliotheek helpt bij het implementeren van veerkrachtige systemen door fouttolerantie voor externe communicatie te beheren.

De bibliotheek is geïnspireerd door Hystrix maar biedt een veel handiger API en een aantal andere functies zoals Rate Limiter (blokkeer te frequente verzoeken), Bulkhead (vermijd te veel gelijktijdige verzoeken) etc.

2. Maven-instellingen

Om te beginnen moeten we de doelmodules toevoegen aan onze pom.xml (hier voegen we bijvoorbeeld de stroomonderbreker toe):

 io.github.resilience4j resilience4j-circuitbreaker 0.12.1 

Hier gebruiken we de zekering module. Alle modules en hun laatste versies zijn te vinden op Maven Central.

In de volgende secties zullen we de meest gebruikte modules van de bibliotheek doornemen.

3. Stroomonderbreker

Merk op dat we voor deze module de resilience4j-circuitbreaker afhankelijkheid hierboven weergegeven.

Het Circuit Breaker-patroon helpt ons een cascade van storingen te voorkomen wanneer een service op afstand uitvalt.

Na een aantal mislukte pogingen kunnen we ervan uitgaan dat de service niet beschikbaar / overbelast is en alle volgende verzoeken gretig afwijzen eraan. Op deze manier kunnen we systeembronnen besparen voor oproepen die waarschijnlijk zullen mislukken.

Laten we eens kijken hoe we dat kunnen bereiken met Resilience4j.

Eerst moeten we de instellingen definiëren die moeten worden gebruikt. De eenvoudigste manier is om de standaardinstellingen te gebruiken:

CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults ();

Het is ook mogelijk om aangepaste parameters te gebruiken:

CircuitBreakerConfig config = CircuitBreakerConfig.custom () .failureRateThreshold (20) .ringBufferSizeInClosedState (5) .build ();

Hier hebben we de tariefdrempel ingesteld op 20% en een minimum aantal van 5 belpogingen.

Vervolgens maken we een Zekering object en bel de service op afstand erdoor:

interface RemoteService {int proces (int i); } CircuitBreakerRegistry registry = CircuitBreakerRegistry.of (config); CircuitBreaker circuitBreaker = registry.circuitBreaker ("mijn"); Functie ingericht = CircuitBreaker .decorateFunction (circuitBreaker, service :: proces);

Laten we tot slot kijken hoe dit werkt door middel van een JUnit-test.

We proberen de service 10 keer te bellen. We zouden moeten kunnen verifiëren dat de oproep minimaal 5 keer is geprobeerd en vervolgens is gestopt zodra 20% van de oproepen is mislukt:

when (service.process (any (Integer.class))). thenThrow (nieuwe RuntimeException ()); for (int i = 0; i <10; i ++) {probeer {versierd.apply (i); } catch (uitzondering negeren) {}} verifieer (service, times (5)). process (any (Integer.class));

3.1. Stroomonderbrekers Staten en instellingen

EEN Zekering kan zich in een van de drie staten bevinden:

  • GESLOTEN - alles is in orde, er is geen sprake van kortsluiting
  • OPEN - externe server is uitgeschakeld, alle verzoeken hieraan zijn kortgesloten
  • HALF OPEN - een geconfigureerde hoeveelheid tijd sinds het ingaan van de OPEN-status is verstreken en Zekering staat verzoeken toe om te controleren of de service op afstand weer online is

We kunnen de volgende instellingen configureren:

  • de uitvalpercentagedrempel waarboven de Zekering opent en begint met het kortsluiten van oproepen
  • de wachttijd die bepaalt hoe lang het Zekering moet open blijven voordat het overschakelt naar half open
  • de grootte van de ringbuffer wanneer de Zekering is half open of gesloten
  • een gewoonte CircuitBreakerEventListener welke handvatten Zekering evenementen
  • een gewoonte Predikaat die evalueert of een uitzondering als een mislukking moet gelden en dus het uitvalpercentage verhoogt

4. Snelheidsbegrenzer

Net als in de vorige sectie, vereist deze functie de veerkracht4j-ratelimiter afhankelijkheid.

Zoals de naam impliceert, Met deze functionaliteit kan de toegang tot bepaalde services worden beperkt. De API lijkt erg op Stroomonderbrekers - er zijn Register, Config en Begrenzer klassen.

Hier is een voorbeeld van hoe het eruit ziet:

RateLimiterConfig config = RateLimiterConfig.custom (). LimitForPeriod (2) .build (); RateLimiterRegistry registry = RateLimiterRegistry.of (config); RateLimiter rateLimiter = registry.rateLimiter ("mijn"); Functie ingericht = RateLimiter.decorateFunction (rateLimiter, service :: proces);

Nu doen alle oproepen het ingerichte serviceblok indien nodig om te voldoen aan de configuratie van de snelheidsbegrenzer.

We kunnen parameters configureren zoals:

  • de periode van de vernieuwing van de limiet
  • de machtigingslimiet voor de vernieuwingsperiode
  • de standaard wachttijd voor machtigingsduur

5. Schot

Hier hebben we eerst de resilience4j-bulkhead afhankelijkheid.

Het is mogelijk om het aantal gelijktijdige oproepen naar een bepaalde service te beperken.

Laten we een voorbeeld bekijken van het gebruik van de Bulkhead API om een ​​maximum aantal gelijktijdige aanroepen te configureren:

BulkheadConfig config = BulkheadConfig.custom (). MaxConcurrentCalls (1) .build (); BulkheadRegistry registry = BulkheadRegistry.of (config); Bulkhead bulkhead = registry.bulkhead ("mijn"); Functie versierd = Bulkhead.decorateFunction (schot, service :: proces);

Om deze configuratie te testen, noemen we de methode van een nepservice.

Dan zorgen we ervoor dat Schot staat geen andere oproepen toe:

CountDownLatch latch = nieuwe CountDownLatch (1); when (service.process (anyInt ())). thenAnswer (aanroep -> {latch.countDown (); Thread.currentThread (). join (); return null;}); ForkJoinTask task = ForkJoinPool.commonPool (). Submit (() -> {probeer {versierd.apply (1);} eindelijk {bulkhead.onComplete ();}}); latch.await (); assertThat (bulkhead.isCallPermitted ()). isFalse ();

We kunnen de volgende instellingen configureren:

  • het maximale aantal parallelle uitvoeringen toegestaan ​​door het schot
  • de maximale tijd dat een draad zal wachten bij een poging om een ​​verzadigd schot binnen te dringen

6. Probeer het opnieuw

Voor deze functie moeten we de resilience4j-retry bibliotheek naar het project.

Wij kunnen automatisch een mislukte oproep opnieuw proberen met behulp van de Retry API:

RetryConfig config = RetryConfig.custom (). MaxAttempts (2) .build (); RetryRegistry registry = RetryRegistry.of (config); Retry retry = registry.retry ("mijn"); Functie versierd = Retry.decorateFunction (retry, (Integer s) -> {service.process (s); return null;});

Laten we nu een situatie emuleren waarin een uitzondering wordt gegenereerd tijdens een externe serviceaanroep en ervoor zorgen dat de bibliotheek de mislukte oproep automatisch opnieuw probeert:

wanneer (service.process (anyInt ())). thenThrow (nieuwe RuntimeException ()); probeer {gedecoreerd.apply (1); fail ("Verwachtte dat er een uitzondering zou worden gegenereerd als alle nieuwe pogingen mislukten"); } catch (uitzondering e) {verificeer (service, tijden (2)). proces (elke (integer.klasse)); }

We kunnen ook het volgende configureren:

  • het maximum aantal pogingen
  • de wachttijd voor nieuwe pogingen
  • een aangepaste functie om het wachtinterval na een storing te wijzigen
  • een gewoonte Predikaat die evalueert of een uitzondering moet resulteren in het opnieuw proberen van de aanroep

7. Cache

De Cache-module vereist het resilience4j-cache afhankelijkheid.

De initialisatie ziet er iets anders uit dan de andere modules:

javax.cache.Cache cache = ...; // Gebruik hier de juiste cache Cache cacheContext = Cache.of (cache); Functie ingericht = Cache.decorateSupplier (cacheContext, () -> service.process (1));

Hier wordt de caching gedaan door de JSR-107 Cache-implementatie die wordt gebruikt en Resilience4j biedt een manier om het toe te passen.

Merk op dat er geen API is voor het decoreren van functies (zoals Cache.decorateFunction (Functie)), ondersteunt de API alleen Leverancier en Oproepbaar types.

8. TimeLimiter

Voor deze module moeten we de veerkracht4j-timelimiter afhankelijkheid.

Het is mogelijk Beperk de hoeveelheid tijd die wordt besteed aan het bellen naar een externe service met behulp van de TimeLimiter.

Laten we om te demonstreren een TimeLimiter met een geconfigureerde time-out van 1 milliseconde:

lange ttl = 1; TimeLimiterConfig config = TimeLimiterConfig.custom (). TimeoutDuration (Duration.ofMillis (ttl)). Build (); TimeLimiter timeLimiter = TimeLimiter.of (config);

Laten we vervolgens controleren of Resilience4j aanroept Future.get () met de verwachte time-out:

Future futureMock = mock (Future.class); Oproep beperkt beperktCall = TimeLimiter.decorateFutureSupplier (timeLimiter, () -> futureMock); beperktCall.call (); verifieer (futureMock) .get (ttl, TimeUnit.MILLISECONDS);

We kunnen het ook combineren met Zekering:

Oproepbaar chainedCallable = CircuitBreaker.decorateCallable (circuitBreaker, restrictedCall);

9. Uitbreidingsmodules

Resilience4j biedt ook een aantal add-on modules die de integratie met populaire frameworks en bibliotheken vergemakkelijken.

Enkele van de meer bekende integraties zijn:

  • Spring Boot - veerkracht4j-spring-boot module
  • Ratpack - veerkracht4j-ratpack module
  • Achteraf inbouwen - resilience4j-retrofit module
  • Vertx - veerkracht4j-vertx module
  • Dropwizard - resilience4j-metrics module
  • Prometheus - veerkracht4j-prometheus module

10. Conclusie

In dit artikel hebben we verschillende aspecten van de Resilience4j-bibliotheek doorgenomen en geleerd hoe we deze kunnen gebruiken om verschillende problemen met fouttolerantie in communicatie tussen servers aan te pakken.

Zoals altijd is de broncode voor de bovenstaande voorbeelden te vinden op GitHub.