Vejledning til modstandsdygtighed4j

1. Oversigt

I denne vejledning taler vi om Resilience4j-biblioteket.

Biblioteket hjælper med at implementere robuste systemer ved at styre fejltolerance for fjernkommunikation.

Biblioteket er inspireret af Hystrix, men tilbyder en meget mere praktisk API og en række andre funktioner som Rate Limiter (blokere for hyppige anmodninger), Bulkhead (undgå for mange samtidige anmodninger) osv.

2. Maven-opsætning

For at starte skal vi tilføje målmodulerne til vores pom.xml (fx her tilføjer vi strømafbryderen):

 io.github.resilience4j modstandsdygtighed4j-kredsløbsafbryder 0.12.1 

Her bruger vi afbryder modul. Alle moduler og deres nyeste versioner kan findes på Maven Central.

I de næste sektioner gennemgår vi de mest anvendte moduler i biblioteket.

3. Afbryder

Bemærk, at til dette modul har vi brug for modstandsdygtighed4j-kredsløbsbryder afhængighed vist ovenfor.

Circuit Breaker-mønsteret hjælper os med at forhindre en kaskade af fejl, når en fjerntjeneste er nede.

Efter et antal mislykkede forsøg kan vi overveje, at tjenesten er utilgængelig / overbelastet og afvise ivrigt alle efterfølgende anmodninger til det. På denne måde kan vi spare systemressourcer til opkald, der sandsynligvis mislykkes.

Lad os se, hvordan vi kan opnå det med Resilience4j.

Først skal vi definere de indstillinger, der skal bruges. Den enkleste måde er at bruge standardindstillinger:

CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults ();

Det er også muligt at bruge tilpassede parametre:

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

Her har vi sat hastighedstærsklen til 20% og et minimum antal på 5 opkaldsforsøg.

Derefter opretter vi en Afbryder gør indsigelse og ring til fjerntjenesten gennem den:

interface RemoteService {int-proces (int i); } CircuitBreakerRegistry registry = CircuitBreakerRegistry.of (config); CircuitBreaker circuitBreaker = registry.circuitBreaker ("min"); Funktion dekoreret = CircuitBreaker .decorateFunction (circuitBreaker, service :: proces);

Lad os endelig se, hvordan dette fungerer gennem en JUnit-test.

Vi forsøger at ringe til tjenesten 10 gange. Vi skulle være i stand til at kontrollere, at opkaldet blev forsøgt mindst 5 gange og derefter stoppet, så snart 20% af opkaldene mislykkedes:

når (service.process (enhver (Integer.class))). derefter Kast (ny RuntimeException ()); for (int i = 0; i <10; i ++) {prøv {dekoreret.apply (i); } fange (Undtagelse ignorere) {}} bekræfte (service, tidspunkter (5)). proces (enhver (Integer.class));

3.1. Afbrydere Tilstande og indstillinger

EN Afbryder kan være i en af ​​de tre tilstande:

  • LUKKET - alt er fint, ingen kortslutning involveret
  • ÅBEN - fjernserver er nede, alle anmodninger til den er kortsluttet
  • HALF_OPEN - der er gået et konfigureret tidsrum siden indtastning af OPEN-tilstand og Afbryder tillader anmodninger om at kontrollere, om fjerntjenesten er tilbage online

Vi kan konfigurere følgende indstillinger:

  • fejlgrænsen, over hvilken Afbryder åbner og starter kortslutningsopkald
  • ventetiden, der definerer, hvor længe Afbryder skal være åben, før den skifter til halv åben
  • størrelsen på ringbufferen, når Afbryder er halvåben eller lukket
  • en skik CircuitBreakerEventListener som håndterer Afbryder begivenheder
  • en skik Prædikat som vurderer, om en undtagelse skal tælle som en fiasko og dermed øge fejlprocenten

4. Satsbegrænser

I lighed med det foregående afsnit kræver disse funktioner modstandsdygtighed4j-ratelimiter afhængighed.

Som navnet antyder, denne funktion tillader begrænsning af adgangen til nogle tjenester. Dens API ligner meget Afbrydere - der er Registreringsdatabase, Konfig og Begrænser klasser.

Her er et eksempel på, hvordan det ser ud:

RateLimiterConfig config = RateLimiterConfig.custom (). LimitForPeriod (2) .build (); RateLimiterRegistry registry = RateLimiterRegistry.of (config); RateLimiter rateLimiter = registry.rateLimiter ("min"); Funktion dekoreret = RateLimiter.decorateFunction (rateLimiter, service :: proces);

Nu kalder alle på den dekorerede serviceblok om nødvendigt for at overholde hastighedsbegrænser-konfigurationen.

Vi kan konfigurere parametre som:

  • perioden for grænsen opdateres
  • tilladelsesgrænsen for opdateringsperioden
  • standard ventetiden på tilladelsens varighed

5. Skot

Her har vi først brug for resilience4j-skot afhængighed.

Er det muligt for at begrænse antallet af samtidige opkald til en bestemt tjeneste.

Lad os se et eksempel på brug af Bulkhead API til at konfigurere et maksimalt antal af én samtidige opkald:

BulkheadConfig config = BulkheadConfig.custom (). MaxConcurrentCalls (1) .build (); BulkheadRegistry registry = BulkheadRegistry.of (config); Skotskot = registry.bulkhead ("min"); Funktion dekoreret = Bulkhead.decorateFunction (skot, service :: proces);

For at teste denne konfiguration kalder vi en mock-tjenestes metode.

Derefter sikrer vi det Skot tillader ikke andre opkald:

CountDownLatch latch = ny CountDownLatch (1); når (service.process (anyInt ())). derefterAnswer (påkaldelse -> {latch.countDown (); Thread.currentThread (). join (); return null;}); ForkJoinTask opgave = ForkJoinPool.commonPool (). Indsend (() -> {prøv {dekoreret.apply (1);} endelig {bulkhead.onComplete ();}}); latch.await (); assertThat (bulkhead.isCallPermitted ()). isFalse ();

Vi kan konfigurere følgende indstillinger:

  • det maksimale antal parallelle henrettelser, der er tilladt af skottet
  • den maksimale tid, en tråd vil vente på, når du forsøger at indtaste et mættet skott

6. Prøv igen

For denne funktion skal vi tilføje resilience4j-prøv igen bibliotek til projektet.

Vi kan prøv igen et mislykket opkald igen ved hjælp af Retry API:

RetryConfig config = RetryConfig.custom (). MaxAttempt (2) .build (); RetryRegistry registry = RetryRegistry.of (config); Prøv igen prøv igen = registry.retry ("min"); Funktion dekoreret = Retry.decorateFunction (prøv igen, (Integer s) -> {service.process (s); return null;});

Lad os nu efterligne en situation, hvor en undtagelse kastes under et fjerntjenesteopkald og sikre, at biblioteket automatisk prøver igen det mislykkede opkald:

når (service.process (anyInt ())). derefterThrow (ny RuntimeException ()); prøv {dekoreret.apply (1); fail ("Forventet en undtagelse at blive kastet, hvis alle forsøg mislykkedes"); } fange (Undtagelse e) {verificere (service, tidspunkter (2)). proces (enhver (Integer.class)); }

Vi kan også konfigurere følgende:

  • det maksimale antal forsøg
  • ventetiden, før den prøver igen
  • en brugerdefineret funktion til at ændre ventetiden efter en fejl
  • en skik Prædikat som vurderer, om en undtagelse skulle resultere i at prøve opkaldet igen

7. Cache

Cache-modulet kræver resilience4j-cache afhængighed.

Initialiseringen ser lidt anderledes ud end de andre moduler:

javax.cache.Cache cache = ...; // Brug passende cache her Cache cacheContext = Cache.of (cache); Funktion dekoreret = Cache.decorateSupplier (cacheContext, () -> service.process (1));

Her udføres cachingen af ​​den anvendte JSR-107 Cache-implementering, og Resilience4j giver en måde at anvende den på.

Bemærk, at der ikke er nogen API til dekorationsfunktioner (som f.eks Cache.decorateFunction (funktion)), API understøtter kun Leverandør og Kan kaldes typer.

8. TimeLimiter

For dette modul skal vi tilføje resilience4j-timelimiter afhængighed.

Det er muligt at begrænse den tid, der bruges på at ringe til en fjerntjeneste ved hjælp af TimeLimiter.

Lad os oprette en for at demonstrere TimeLimiter med en konfigureret timeout på 1 millisekund:

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

Lad os derefter kontrollere, at Resilience4j ringer Future.get () med den forventede timeout:

Future futureMock = mock (Future.class); Kan kaldes restrictedCall = TimeLimiter.decorateFutureSupplier (timeLimiter, () -> futureMock); restrictedCall.call (); verificer (futureMock) .get (ttl, TimeUnit.MILLISECONDS);

Vi kan også kombinere det med Afbryder:

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

9. Tilføjelsesmoduler

Resilience4j tilbyder også et antal tilføjelsesmoduler, der letter integrationen med populære rammer og biblioteker.

Nogle af de mere kendte integrationer er:

  • Spring Boot - resilience4j-spring-boot modul
  • Ratpack - modstandsdygtighed4j-ratpack modul
  • Eftermontering - modstandsdygtighed4j-eftermontering modul
  • Vertx - modstandsdygtighed4j-vertx modul
  • Dropwizard - resilience4j-metrics modul
  • Prometheus - modstandsdygtighed4j-prometheus modul

10. Konklusion

I denne artikel gennemgik vi forskellige aspekter af Resilience4j-biblioteket og lærte, hvordan vi bruger det til at løse forskellige fejltoleranceproblemer i kommunikation mellem servere.

Som altid kan kildekoden til eksemplerne ovenfor findes på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found