Vodič za otpornost4j
1. Pregled
U ovom uputstvu razgovarat ćemo o biblioteci Resilience4j.
Biblioteka pomaže u implementaciji elastičnih sustava upravljajući tolerancijom kvara za daljinske komunikacije.
Biblioteka je nadahnuta Hystrixom, ali nudi puno prikladniji API i brojne druge značajke poput Ograničenja brzine (blokiranje prečestih zahtjeva), Pregrada (izbjegavanje previše istodobnih zahtjeva) itd.
2. Postavljanje Mavena
Za početak trebamo dodati ciljne module u naš pom.xml (npr. ovdje dodamo prekidač):
io.github.resilience4j elastičnost4j-osigurač 0.12.1
Ovdje koristimo osigurač modul. Svi moduli i njihove najnovije verzije mogu se naći na Maven Central.
U sljedećim odjeljcima proći ćemo kroz najčešće korištene module knjižnice.
3. Automatski osigurač
Imajte na umu da nam je za ovaj modul potreban elastičnost4j-prekidač ovisnost prikazana gore.
Uzorak prekidača pomaže nam u sprječavanju kaskade kvarova kada je udaljena usluga isključena.
Nakon niza neuspjelih pokušaja, možemo smatrati da usluga nije dostupna / preopterećena i nestrpljivo odbiti sve sljedeće zahtjeve tome. Na taj način možemo uštedjeti resurse sustava za pozive koji će vjerojatno propasti.
Pogledajmo kako to možemo postići s Resilience4j.
Prvo, moramo definirati postavke koje ćemo koristiti. Najjednostavniji način je korištenje zadanih postavki:
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults ();
Također je moguće koristiti prilagođene parametre:
CircuitBreakerConfig config = CircuitBreakerConfig.custom () .failureRateThreshold (20) .ringBufferSizeInClosedState (5) .build ();
Ovdje smo postavili prag brzine na 20% i minimalni broj 5 pokušaja poziva.
Zatim kreiramo Osigurač objekt i putem njega nazovite udaljenu uslugu:
sučelje RemoteService {proces int (int i); } CircuitBreakerRegistry registry = CircuitBreakerRegistry.of (config); CircuitBreaker circuitBreaker = registry.circuitBreaker ("moj"); Uređena funkcija = CircuitBreaker .decorateFunction (CircuitBreaker, service :: process);
Napokon, pogledajmo kako ovo funkcionira putem JUnit testa.
Pokušat ćemo nazvati službu 10 puta. Morali bismo biti u mogućnosti provjeriti je li poziv pokušan najmanje 5 puta, a zatim zaustavljen čim 20% poziva nije uspjelo:
kada (service.process (bilo koji (Integer.class))). thenThrow (new RuntimeException ()); for (int i = 0; i <10; i ++) {try {ukrašen.apply (i); } catch (Exception ignore) {}} verify (service, times (5)). process (bilo koji (Integer.class));
3.1. Prekidači Države i postavke
A Osigurač može biti u jednom od tri stanja:
- ZATVORENO - sve je u redu, nema kratkog spoja
- OTVORENA - udaljeni poslužitelj nije u funkciji, svi zahtjevi za njega su kratko spojeni
- POLA_OTVORENO - proteklo je konfigurirano vrijeme od ulaska u stanje OTVORENO i Osigurač omogućuje zahtjeve da provjere je li udaljena usluga ponovno na mreži
Možemo konfigurirati sljedeće postavke:
- prag stope otkaza iznad kojeg je Osigurač otvara i započinje kratki spoj
- trajanje čekanja koje definira koliko dugo Osigurač treba ostati otvoren prije nego što se prebaci na poluotvoreno
- veličina prstena u međuspremniku kada Osigurač je napola otvoren ili zatvoren
- običaj CircuitBreakerEventListener koji rukuje Osigurač događaja
- običaj Predikat koja procjenjuje treba li iznimka računati kao kvar i tako povećati stopu kvara
4. Ograničite ocjenu
Slično prethodnom odjeljku, i za ove značajke potreban je elastičnost4j-ratelimiter ovisnost.
Kao što naziv govori, ova funkcionalnost omogućuje ograničavanje pristupa nekim uslugama. Njegov API je vrlo sličan Prekidači - tamo su Registar, Config i Ograničitelj razreda.
Evo primjera kako to izgleda:
RateLimiterConfig config = RateLimiterConfig.custom (). LimitForPeriod (2) .build (); RateLimiterRegistry registry = RateLimiterRegistry.of (config); RateLimiter rateLimiter = registry.rateLimiter ("moj"); Uređena funkcija = RateLimiter.decorateFunction (rateLimiter, service :: process);
Sada se svi pozivi na ukrašenom servisnom bloku po potrebi prilagođavaju konfiguraciji ograničenja brzine.
Možemo konfigurirati parametre poput:
- razdoblje ograničenja osvježavanja
- ograničenje dopuštenja za razdoblje osvježavanja
- zadano čekanje na trajanje dopuštenja
5. Pregrada
Evo, prvo će nam trebati elastičnost4j-pregrada ovisnost.
To je moguće kako biste ograničili broj istovremenih poziva na određenu uslugu.
Pogledajmo primjer korištenja Bulkhead API-ja za konfiguriranje maksimalnog broja istodobnih poziva:
BulkheadConfig config = BulkheadConfig.custom (). MaxConcurrentCalls (1) .build (); Registar BulkheadRegistry = BulkheadRegistry.of (config); Pregrada pregrada = registry.bulkhead ("moja"); Uređena funkcija = Bulkhead.decorateFunction (pregrada, usluga :: proces);
Da bismo testirali ovu konfiguraciju, pozvat ćemo metodu lažne usluge.
Tada to osiguravamo Pregrada ne dopušta nikakve druge pozive:
Zasun CountDownLatch = novi CountDownLatch (1); when (service.process (anyInt ())). thenAnswer (invocation -> {latch.countDown (); Thread.currentThread (). join (); return null;}); ForkJoinTask zadatak = ForkJoinPool.commonPool (). Submit (() -> {try {ukrašen.primijeniti (1);} konačno {bulkhead.onComplete ();}}); zasun.čekaj (); assertThat (bulkhead.isCallPermitted ()). isFalse ();
Možemo konfigurirati sljedeće postavke:
- maksimalna količina paralelnih izvršavanja dopuštenih pregradom
- maksimalno vrijeme koje će nit čekati kada pokušava ući u zasićenu pregradu
6. Pokušajte ponovo
Za ovu značajku morat ćemo dodati elastičnost4j-ponovni pokušaj knjižnica na projekt.
Možemo automatski pokušaj neuspjelog poziva pomoću API-ja za ponovni pokušaj:
RetryConfig config = RetryConfig.custom (). MaxAttempts (2) .build (); RetryRegistry registry = RetryRegistry.of (config); Pokušaj ponovno pokušaj = registry.retry ("moj"); Uređena funkcija = Retry.decorateFunction (pokušaj, (Integer s) -> {service.process (s); return null;});
Ajmo sada oponašati situaciju kada se tijekom poziva udaljene usluge baci izuzetak i osigurajmo da knjižnica automatski pokuša neuspjeli poziv:
kada (service.process (anyInt ())). thenThrow (new RuntimeException ()); pokušajte {ukrašen.apply (1); fail ("Očekivano izbacivanje iznimke ako svi pokušaji ne uspiju"); } catch (Iznimka e) {verify (service, times (2)). process (bilo koji (Integer.class)); }
Također možemo konfigurirati sljedeće:
- maksimalan broj pokušaja
- trajanje čekanja prije ponovnih pokušaja
- prilagođena funkcija za izmjenu intervala čekanja nakon kvara
- običaj Predikat koja procjenjuje treba li iznimka rezultirati ponovnim pozivom
7. Predmemorija
Modul Cache zahtijeva elastičnost4j-predmemorija ovisnost.
Inicijalizacija izgleda malo drugačije od ostalih modula:
javax.cache.Cache cache = ...; // Ovdje upotrijebite odgovarajuću predmemoriju Cache cacheContext = Cache.of (cache); Uređena funkcija = Cache.decorateSupplier (cacheContext, () -> service.process (1));
Ovdje se predmemoriranje vrši korištenom implementacijom predmemorije JSR-107, a Resilience4j pruža način da se primijeni.
Imajte na umu da ne postoji API za ukrašavanje funkcija (poput Cache.decorateFunction (funkcija)), API podržava samo Dobavljač i Pozivno vrste.
8. TimeLimiter
Za ovaj modul moramo dodati elastičnost4j-timelimiter ovisnost.
Moguće je ograničite količinu vremena provedenog na pozivanju udaljene usluge koristeći TimeLimiter.
Da bismo demonstrirali, postavimo a TimeLimiter s konfiguriranim vremenskim ograničenjem od 1 milisekunde:
dugo ttl = 1; TimeLimiterConfig config = TimeLimiterConfig.custom (). TimeoutDuration (Duration.ofMillis (ttl)). Build (); TimeLimiter timeLimiter = TimeLimiter.of (config);
Dalje, provjerimo da Resilience4j poziva Future.get () s očekivanim vremenskim ograničenjem:
Budućnost futureMock = lažno (Future.class); Pozivni ograničeni poziv = TimeLimiter.decorateFutureSupplier (timeLimiter, () -> futureMock); restrictedCall.call (); verify (futureMock) .get (ttl, TimeUnit.MILLISECONDS);
Također ga možemo kombinirati s Osigurač:
Poziv chainedCallable = CircuitBreaker.decorateCallable (circuitBreaker, restrictedCall);
9. Dodatni moduli
Resilience4j također nudi brojne dodatne module koji olakšavaju njegovu integraciju s popularnim okvirima i knjižnicama.
Neke od poznatijih integracija su:
- Proljetna čizma - elastičnost4j-spring-boot modul
- Ratpack - elastičnost4j-ratpack modul
- Retrofit - elastičnost4j-naknadna ugradnja modul
- Vertx - elastičnost4j-vertx modul
- Čarobnjak - elastičnost4j-metrika modul
- Prometej - elastičnost4j-prometheus modul
10. Zaključak
U ovom smo članku prošli kroz različite aspekte biblioteke Resilience4j i naučili kako je koristiti za rješavanje različitih problema s tolerancijom grešaka u komunikaciji između poslužitelja.
Kao i uvijek, izvorni kod za gornje uzorke možete pronaći na GitHubu.