ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 150
Numărul 149 Numărul 148 Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 150
Abonament PDF

Spring Retry: Cum să gestionezi eșecurile temporare ca un profesionist!

Bianca Moga
Software Developer



PROGRAMARE

De fiecare dată când se execută un request, există șansa ca acesta să eșueze - fie din cauza unei conexiuni instabile, fie din cauza unui server temporar indisponibil. În astfel de situații, o strategie comună este să reîncercăm operațiunea, oferind aplicației noastre șansa de a depăși erorile temporare. Dar cum gestionăm acest proces fără să scriem cod redundant sau să pierdem din claritatea aplicației? Aici intervine Spring Retry, o bibliotecă puternică și flexibilă, integrată perfect în ecosistemul Spring Boot, care automatizează și simplifică reîncercarea operațiunilor eșuate.

Cum folosim Spring Retry

Mai întâi avem nevoie de următoarele dependențe în fișierul nostru pom.xml:

<dependency>
    <groupId>
     org.springframework.retry
   </groupId>
   <artifactId>
     spring-retry
   </artifactId>
   <version>2.0.3</version>
</dependency>
<dependency>
   <groupId>
     org.springframework
   </groupId>
   <artifactId>
     spring-aspects
   </artifactId>
    <version>6.1.5</version>
</dependency>

De asemenea, vom adăuga @EnableRetry în clasa de configurări ce conține deja @Configuration, pentru a crea un proxy din clasele care au metode de tip retryable.

@EnableRetry
public class YourApplication {
  public static void main(
   String[] args) {

  SpringApplication
 .run(YourApplication.class, args);
   }
}

De obicei, folosim \@Retryable atunci când facem un apel la un serviciu care poate fi indisponibil la un anumit moment și dorim să încercăm din nou să ne conectăm la acesta după o perioadă.

Exemplu:

@Retryable(
  retryFor = {
   ConnectException.class,   
   ResourceAccessException.class
  },
  maxAttempts = 5, 
  backoff = @Backoff(value = 1000, 
    multiplier = 2))

  public void callAPI(String arg) 
   throws ConnectException, 
   ResourceAccessException {
   //cod pentru retry...
 }

Putem configura următorii parametri:

Odată ce s-a executat numărul maxim de încercări, putem să ne revenim din eroarea întâlnită prin crearea unei metode de recuperare, marcată cu @Recover care va fi apelată la final. Rețineți că, dacă o încercare este reușită, nu se va mai apela următoarea încercare și nu se va apela metoda de recuperare.

Exemplu:

@Recover
public void recover(){
//cod pentru recuperare
}

Când folosim Spring Retry

  1. Eșecuri legate de rețea

    • Apeluri de servicii: când apelăm API-uri și microservicii, pot apărea probleme temporare de rețea

    • Conexiune la bazele de date: se poate întâmpla la sisteme distribuite unde baza de date poate fi ocazional inaccesibilă
  2. Folosirea brokerul de mesaje

    • Eșecuri ale brokerului de mesaje: dacă aplicația utilizează brokeri de mesaje precum RabbitMQ, Kafka sau alții, retry-ul poate fi necesar dacă brokerul este temporar indisponibil
  3. Blocări ale resurselor

    • Deadlock-uri: când mai multe procese doresc să acceseze aceeași resursă și se creează un deadlock (blocaj), e indicată o perioadă de așteptare înainte de a apela din nou serviciul

    • *File System Locking: la operațiuni de input/output* unde o resursă poate fi temporar blocată de un alt proces
  4. Acces la resurse externe

    • Servere de configurare: aplicația obține configurări de la un server de configurare extern (Spring Cloud Config Server), iar operațiunea retry poate ajuta dacă serverul este temporar indisponibil

    • Servicii de stocare la distanță: când interacționăm cu servicii de stocare în cloud precum AWS S3, operațiunea retry poate fi necesară în cazul unor eșecuri temporare
  5. Indisponibilitatea serviciilor apelate

    • Arhitectură de microservicii: într-o arhitectură de microservicii, unde serviciile pot fi temporar suprasolicitate sau în mentenanță

    • Implementarea unui Circuit Breaker: când ai implementat un șablon de tip circuit breaker (folosind Resilience4j sau Hystrix)
  6. Procesare asincronă

    • Background jobs: aplicația procesează joburi de fundal, folosind Spring Batch sau un instrument similar

    • Ascultători de evenimente: pentru arhitecturi bazate pe evenimente, operațiunea retry poate fi necesară atunci când se gestionează evenimente care eșuează din cauza problemelor tranzitorii
  7. Eșecuri ale dependințelor externe

    • Integrarea cu un serviciu de e-mail: aplicația trimite e-mailuri printr-un server SMTP extern

    • Third-Party Gateways: de exemplu, la integrarea gateway-uri de plată, operașiunea retry poate fi util dacă gateway-ul de plată experimentează întreruperi temporare sau latență
  8. Eșecuri în descoperirea serviciilor

    • *Probleme cu Eureka sau Consul: în cazul microserviciilor care folosesc mecanisme de descoperire a serviciilor precum Eureka sau Consul, operașiunea retry* poate ajuta atunci când un serviciu este temporar inaccesibil
  9. Indisponibilitatea temporară a cache-ului

    • Operațiuni Redis/Memcached: aplicația ta folosește Redis, Memcached sau o altă bază de date de tip in-memory, operațiunea retry poate fi utilă atunci când avem latență ridicată

RetryTemplate

RetryTemplate oferă o abordare programatică pentru implementarea operațiunii retry. Este mai flexibil și poate fi utilizat oriunde în cod, inclusiv în clase care nu sunt gestionate de Spring. Necesită să creezi manual instanța de RetryTemplate și să configurezi comportamentul său (maxAttempt, backoff, excepții etc.).

Exemplu:

RetryTemplate retryTemplate = RetryTemplate.builder()
 .maxAttempts(5)    // Maxim 5 încercări
 .fixedBackoff(1000)  // Pauză de 1 secundă între   
                    // încercări
 .retryOn(RuntimeException.class)  
 .build();

String result = retryTemplate.execute(context -> {
  System.out.println("Execut operația... Încercare: " 
   + context.getRetryCount());

  if (context.getRetryCount() < 4) {
    throw new RuntimeException("Eroare temporară");
  }
  return "Operație finalizată cu succes!";
});
System.out.println(result);

În exemplul de mai sus avem un număr maxim de încercări(maxAttempts(5)), ceea ce înseamnă că operația va fi încercată de maximum 5 ori: 1 încercare inițială + 4 operațiuni retry. Dacă operația continuă să eșueze după 5 încercări, se aruncă o excepție. Prin configurarea (fixedBackoff(1000)), se introduce o pauză de 1 secundă între încercări. Operațiunea retry pe RuntimeException (retryOn(RuntimeException.class)) va face următoarele: operațiunea retry se va declanșa doar dacă este aruncată o excepție de tip RuntimeException, iar orice altă excepție (sau finalizarea cu succes) va încheia procesul.

Dacă și a cincea încercare aruncă un RuntimeException, RetryTemplate nu mai face alte operațiuni retry și va arunca excepția către apelant. Rezultatul în acest caz ar fi o eroare și stack trace-ul excepției.

Retry vs RetryTemplate

Caracteristică @Retry RetryTemplate
Tip de implementare Declarativă Programatică
Activare Necesită \@EnableRetry și un bean Poate fi utilizat oriunde în cod
Spring
Ușurință în utilizare Simplu de folosit pentru metode de Mai configurabil, dar necesită mai mult
retry cod
Flexibilitate Limitat la metodele gestionate de Spring Poate fi folosit în clase non-Spring
Fallback ( @Recover) Suport direct cu o metodă specifică. Necesită gestionare manuală a
fallback-ului
Scenarii tipice Apeluri service/metode gestionate de Operații complexe sau apeluri
Spring non-Spring

Pe scurt, \@Retry este mai simplu, dar mai puțin flexibil, iar RetryTemplate este mai versatil, dar necesită cod suplimentar.

Concluzie

Retry este în general util când operațiunea este probabil să reușească dacă este încercată din nou după o scurtă întârziere. Totuși, retry-ul excesiv poate duce la epuizarea resurselor și la probleme de performanță. Configurarea corectă a limitelor de retry, a întârzierilor și a gestionării excepțiilor este crucială pentru a asigura că mecanismul de retry nu duce la impact negativ asupra sistemului.

Referințe:

  1. https://docs.spring.io/spring-batch/docs/1.0.x/spring-batch-docs/reference/html/ch06.html

  2. https://medium.com/\@virendra-oswal/retrying-and-recovery-via-spring-boot-using-spring-retry-91b590ef60f9

  3. https://www.baeldung.com/spring-retry

  4. https://medium.com/\@ankithahjpgowda/spring-retry-overview-6674b06a3235

NUMĂRUL 149 - Development with AI

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects