ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 164
Numărul 163 Numărul 162 Numărul 161 Numărul 160 Numărul 159 Numărul 158 Numărul 157 Numărul 156 Numărul 155 Numărul 154 Numărul 153 Numărul 152 Numărul 151 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 164
Abonamente

Proiectarea sistemelor distribuite reziliente cu Polly și .NET 10

Daniela Crețu
Senior Software Engineer @ Cognizant



PROGRAMARE

Într-un sistem distribuit, aplicațiile vor eșua, nu pentru că cineva a greșit sau aplicația a fost scrisă prost. Explicația eșecului este alta. Deoarece rețelele sunt imprevizibile, serviciile rulează pe mașini diferite sau se supraîncarcă, bazele de date pot deveni lente, iar APIurile externe uneori nu mai răspund. Atunci când multe servicii comunică între ele, chiar și o întârziere mică sau o întrerupere temporară poate provoca probleme mult mai mari dacă sistemul nu este pregătit.

De aceea, reziliența trebuie gândită încă de la început. Cu instrumente precum Polly, putem adăuga comportamente inteligente care ajută aplicațiile să se recupereze într-un mod robust, în loc să se blocheze sau să se prăbușească. Construirea rezilienței din timp asigură că sistemele noastre rămân stabile și receptive, oferind o experiență mai bună utilizatorilor.

Ce este Polly și de ce este atât de folosită

Polly este librăria de reziliență pe care întregul ecosistem .NET se bazează pentru a gestiona tipurile de erori care apar în mod natural în sistemele distribuite. Ea oferă modele integrate precum retry, timeout, circuit breaker și bulkhead, ajutând aplicațiile să rămână receptive chiar și atunci când serviciile externe devin lente sau nesigure.

Începând cu .NET 8 și continuând cu .NET 10+, Microsoft a integrat Polly direct în infrastructura de reziliență a platformei, în special prin HttpClientFactory, metoda recomandată pentru a efectua apeluri HTTP în aplicațiile moderne .NET. Această integrare le permite dezvoltatorilor să aplice politici de reziliență cu doar câteva linii de configurație, în loc să scrie logică personalizată de retry sau cod complex de tratare a erorilor.

Pentru că Polly este atât de puternică, dar și ușor de folosit, a devenit alegerea standard pentru construirea de microservicii fiabile în .NET, transformând-o într-un instrument esențial pentru inginerii de orice nivel care vor să proiecteze sisteme ce se comportă previzibil chiar și în condiții de stres.

Modele fundamentale de reziliență

A. Politica Retry

În sistemele distribuite, erorile temporare sunt foarte frecvente. Un serviciu poate fi lent pentru câteva momente, un pachet de rețea poate fi pierdut sau un API poate răspunde pentru scurt timp cu o eroare 500. O politică de retry ajută aplicația să încerce automat operațiunea din nou, în loc să eșueze imediat. Este una dintre cele mai simple și eficiente tehnici de reziliență.

Polly oferă mai multe moduri de a reîncerca operațiunile eșuate, cum ar fi:

Atunci când se folosește retry, ar trebui reîncercate doar erorile temporare (tranzitorii) — cum ar fi timeouts sau răspunsurile de tip "too many requests" (408, 429, 502, 503, 504). De asemenea, ar trebui reîncercate doar operațiunile idempotente, adică acele operațiuni care pot fi repetate fără a produce efecte secundare nedorite. Pentru a evita agravarea problemei, de folosit întotdeauna exponential backoff și jitter, astfel încât reîncercările să nu se întâmple toate în același timp.

B. Politica Circuit Breaker

Modelul Circuit Breaker protejează sistemul de apeluri repetate către un serviciu care deja eșuează. În loc să tot încerci la nesfârșit și să se agraveze problema, circuit breakerul "se deschide" după un anumit număr de erori și blochează temporar apelurile către serviciul nesănătos. Acest lucru oferă timp serviciului defect să își revină și împiedică aplicația să irosească resurse pe apeluri care aproape sigur vor eșua.

O modalitate simplă de a înțelege aceste doua concepte este: Retry ajută atunci când un serviciu este temporar lent. Circuit breaker ajută atunci când un serviciu este constant defect.

Când circuitul este deschis, apelurile eșuează imediat. După o perioadă de răcire, circuitul trece în starea "halfopen", permițând câteva apeluri de test. Dacă acestea reușesc, circuitul se închide din nou și traficul revine la normal.

Acest model este esențial în sistemele distribuite deoarece previne "defecțiunile în cascadă", situații în care un singur serviciu defect provoacă eșecuri în lanț în multe alte servicii din cauza încărcării suplimentare.

C. Politica Timeout

Nu lăsa serviciile lente să te încetinească.

O politică de timeout protejează aplicația de situațiile în care se așteaptă prea mult după un răspuns. În sistemele distribuite, un serviciu lent poate fi la fel de dăunător ca unul care eșuează complet. Dacă aplicația așteaptă la nesfârșit, threadurile se blochează, cozile se umplu, iar întregul sistem devine brusc lent.

O politică de timeout rezolvă această problemă prin regula:

"Dacă acest apel nu răspunde în X secunde, oprește așteptarea și eșuează rapid."

Astfel, sistemul rămâne receptiv și previne consumul excesiv de resurse.

Când să folosim timeouturi:

D. Politica Bulkhead

Nu lăsa o singură defecțiune să scufunde întreaga "navă". Modelul Bulkhead provine din construcția navelor. Navele sunt împărțite în compartimente ("bulkheads"), astfel încât, dacă unul se inundă, întreaga navă nu se scufundă.

În sistemele distribuite, un bulkhead limitează câte apeluri simultane pot fi făcute către o anumită dependență. Dacă un serviciu devine lent sau supraîncărcat, nu se dorește ca toate threadurile tale să rămână blocate așteptând după el.

Un bulkhead spune practic:

"Doar X apeluri către acest serviciu în același timp. Restul eșuează rapid."

Acest lucru protejează restul sistemului de a fi tras în jos de un singur serviciu problematic.

Combinarea politicilor

Platforma .NET 10 include un pipeline de reziliență integrat, bazat pe Polly, care aplică automat politicile în ordinea corectă atunci când folosim HttpClientFactory și AddStandardResilienceHandler.

Într-un sistem distribuit real, o singură politică nu este suficientă.

O politică de retry poate ajuta în cazul erorilor temporare, dar nu te protejează de apeluri lente. Un timeout previne blocarea pe operațiuni care durează prea mult, dar nu împiedică supraîncărcarea unui serviciu deja instabil. Un circuit breaker oprește apelurile către un serviciu care eșuează constant, dar nu reîncearcă atunci când problema este tranzitorie. Iar un bulkhead limitează câte cereri simultane pot intra, prevenind blocarea resurselor.

De aceea, .NET 10 combină automat aceste politici într-un pipeline coerent și predictibil. Practic, platforma "stivuiește" politicile astfel încât fiecare să protejeze sistemul de un alt tip de problemă.

O combinație tipică arată astfel:

Ordinea corectă a strategiilor

.NET 10 aplică politicile în această ordine:

  1. Bulkhead (cel mai exterior strat),

  2. Timeout,

  3. Retry,

  4. Circuit Breaker (cel mai interior strat).

Această ordine asigură că:

Folosirea Polly cu HttpClientFactory în .NET 10+

În aplicațiile moderne .NET, metoda recomandată pentru a face apeluri HTTP este prin HttpClientFactory, care este integrat direct în sistemul de dependency injection (DI).

Acesta este un mare avantaj: se obține reziliență de nivel enterprise cu doar câteva linii de cod și se evita greșelile comune, cum ar fi crearea excesivă de instanțe HttpClient sau ordonarea greșită a politicilor.

Folosirea HttpClientFactory împreună cu handlerul de reziliență integrat îți oferă:

Aceasta este metoda standard pentru a construi microservicii reziliente în .NET 10+.

Un exemplu simplu de configurare a unui client HTTP rezilient folosind pipelineul integrat Polly:

builder.Services.AddHttpClient("WeatherApi")
.AddStandardResilienceHandler(options =>
  {
    // Retry cu exponential backoff + jitter
    options.Retry.MaxRetryAttempts = 3;
    options.Retry.Delay = TimeSpan
    .FromMilliseconds(200);

    options.Retry.UseJitter = true;

    // Timeout pentru fiecare încercare
    options.Timeout.Timeout = TimeSpan
    .FromSeconds(3);

    // Circuit breaker pentru erori persistente
    options.CircuitBreaker.FailureThreshold = 0.5;
    options.CircuitBreaker.SamplingDuration = 
      TimeSpan.FromSeconds(30);

    options.CircuitBreaker.MinimumThroughput = 10;
    options.CircuitBreaker.BreakDuration = TimeSpan
    .FromSeconds(15);

    // Bulkhead pentru limitarea concurenței
    options.Bulkhead.MaxConcurrentRequests = 10;
    options.Bulkhead.QueueLimit = 5;
 });

După înregistrare, injectezi clientul în serviciul tău:

public class WeatherService(HttpClient client)
{
  public async Task GetForecastAsync()
  {
   var response = await client.GetAsync("/forecast");
   response.EnsureSuccessStatusCode();
   return await response.Content.ReadAsStringAsync();
  }
}

Și îl înregistrezi astfel:

builder.Services.AddTransient<WeatherService>();
builder.Services.AddHttpClient<WeatherService>("WeatherApi");

Acum fiecare apel către Weather API folosește automat pipelineul de reziliență configurat.

Cum funcționează?

a. Retry - reîncearcă automat apelurile temporar eșuate.

Configurația:

Rezultat: reîncearcă inteligent, fără să supraîncarce serviciul.

b. Timeout - nu aștepta la nesfârșit.

c. Circuit Breaker - oprește apelurile către un serviciu care eșuează constant.

d. Bulkhead - limitează câte cereri simultane pot intra.

Cazuri reale de utilizare în producție

APIuri externe lente sau instabile.

Serviciile terțe (plăți, geolocație, SMS, email, hărți etc.) pot deveni lente în orele de vârf. Fără timeout și retry:

Cu reziliență configurată:

Rate limiting (429 Too Many Requests)

Multe APIuri moderne limitează numărul de cereri pe minut. Când primești 429:

3. Baze de date sau cacheuri care răspund greu:

Uneori baza de date sau Redis poate avea spikeuri de latență. În aceste situații:

4. Operațiuni idempotente care pot fi reîncercate în siguranță:

Greșeli de producție

1. Folosirea retryurilor fără timeout.

2. Retry pe operațiuni care nu sunt idempotente

Acest lucru poate duce la:

  1. dublarea comenzilor;

  2. tranzacții duplicate;

  3. inconsistență în date.

4. Lipsa unui circuit breaker

Fără circuit breaker:

În dezvoltarea de aplicații distribuite, reziliența nu este un "nicetohave", ci o necesitate. Politicile Retry, Circuit Breaker, Timeout și Bulkhead te ajută să construiești servicii stabile, capabile să gestioneze erori, latențe și dependențe imprevizibile. Înțelegerea și aplicarea lor te vor ajuta să scrii aplicații mai robuste, mai rapide și mai ușor de întreținut. Aceste patternuri nu doar rezolvă probleme, ci te pregătesc și pentru a gândi ca un programator orientat spre fiabilitate.

Conferință TSM

NUMĂRUL 164 - Academic 2 Business

Sponsori

  • BT Code Crafters
  • Betfair
  • MHP
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • BMW TechWorks Romania