ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
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 131
Abonament PDF

Reziliența Aplicației - Nevoia unui plan B

Rareș Coantă
Senior Full-Stack .NET Developer @ CodeCrafters by BT



PROGRAMARE


Când vorbim despre conceptul de "craftsmanship", ne putem referi la o anumită calitate a codului, una ridicată, de care așa-zișii meșteri care au scris acel cod să poată fi mândri și care să reflecte atât abilitățile avansate de care aceștia dau dovadă, cât și dorința lor de a dezvolta o aplicație cât mai bună posibil. De cele mai multe ori, pentru a se putea ajunge la un astfel de nivel al calității este nevoie de multă experiență și de un proces de dezvoltare amănunțit și detaliat.

Cu toate acestea, ca niște buni meșteri, știm că uneori ceva neprevăzut poate apărea, lucru care poate duce la unele probleme și, ulterior, la o scădere graduală sau chiar drastică a calității unui cod bun, făcându-l să scadă sub standardele pe care vrem să le avem. Acest lucru este, poate, mai volatil când vine vorba de aplicațiile mobile sau web. Ele folosesc, de obicei, unul sau mai multe API-uri care se integrează cu alte servicii sau componente auxiliare, atât externe, cât și interne, aspect care poate duce la apariția celor mai des întâlnite probleme așa-zis "neprevăzute", și anume, diversele erori de comunicare cu acele servicii.

Spre deosebire de defectele relativ generice care pot apărea în cod, acestea sunt mai dificil de abordat, întrucât în mare parte din cazuri nu le putem repara direct, fie pentru că nu se reproduc în condiții normale, fie pentru că problema nu este una sub controlul direct al echipei, caz întâlnit cel mai des la integrările cu servicii externe.

Dincolo de aceste aspecte, faptul că sunt mai dificil de tratat nu face acest lucru imposibil, ci doar duce la nevoia unei noi abordări, una mai concentrată pe negocierea acestor probleme, astfel încât ele să dispară sau să nu fie simțite de către utilizatori.

Acesta este conceptul pe care ne place să îl numim "Reziliența Aplicației", unul bazat atât pe posibila tratare a erorilor înainte să apară, cât și pe grațioasa gestionare a unor erori care, în pofida tuturor asigurărilor, își fac apariția.

Gestionarea erorilor

Fiind cea mai comună dintre cele două, încercarea de gestionare a erorilor este așa-numitul minim de reziliență pe care o zonă de cod, dependentă de servicii externe, ar trebui să o aibă, aceasta servind rolul unui plan secundar care ar trebui să se întâmple în situația în care dependența externă nu se comportă cum este de așteptat.

Retry - Când vine vorba despre includerea acesteia în arhitectura aplicației, un pas de început este, de cele mai multe ori, implementarea unui mecanism de Retry, mecanism care facilitează reîncercarea unui request eșuat de mai multe ori. Această abordare este recomandată ca un prim mecanism de gestionare a erorilor de comunicare cu servicii externe, deoarece, în multe cazuri, problemele care cauzează eșecul apelului către dependență sunt relativ mici, fiind spontane și care dispar sau par să se rezolve de la sine la încercări ulterioare. Deși această metodă este una ușor de implementat și de folosit, ea nu ar trebui să fie foarte des aplicată (întrucât poate solicita mult sistemul). Mai ales că e sugerată utilizarea ei doar pentru zonele care "ating" dependențe externe sau care apelează alte servicii. Totodată, pentru a mări șansa succesului la încercările subsecvente, este recomandată și adăugarea unei perioade de așteptare, un așa-zis timp mort care dictează viteza cu care sunt repetate acțiunile, acordându-i, astfel, sistemului mai mult timp să-și revină.

Circuit Breaker - Desigur că, de la un punct, numărul repetat de încercări începe să ofere un randament descrescător, făcând repetatele încercări redundante. Pentru acest motiv, conceptul de Circuit Breaker este unul foarte important, făcând referire la un mod de a închide bucla de încercări atât pentru o perioadă predefinită, cât și definitiv. Pe lângă avantajul ușor de observat de a pune capăt așa-numitului circuit, principalul lui beneficiu este de a oferi posibilitatea de suspendare a execuției cât timp este rulat un alt segment de cod de rezervă. Spre exemplu, după câteva execuții eșuate, se poate suspenda circuitul pentru a se trimite un apel de repornire a serviciului, reluând încercarea de execuție o dată ce serviciul a răspuns unui ping cu succes.

Failover - Totuși, nimic nu surprinde existența unui plan B mai bine decât un sistem (sau mai multe) de rezervă, folosit atunci când cel principal eșuează din diverse motive. În cazul existenței unui asemenea sistem, încercările ulterioare pot să fie redirecționate spre el (mai ales într-o suspendare rapidă a circuitului), astfel oferind o șansă mai mare de succes. Pentru a o crește și mai tare, în cazul existenței mai multor astfel de sisteme de rezervă, este recomandată încercarea de folosire a mai multora în mod paralel, crescând exponențial numărul de încercări odată cu numărul de eșecuri. De precizat că, dacă există, astfel de servicii nu sunt mereu la același nivel de performanță cu cel al serviciului principal. În acest fel, apar probleme de altă natură, dar nu eșec total al sistemului.

Graceful Degradation - Uneori, toate încercările de gestionare a unei erori nu sunt suficiente. În aceste situații, este de preferat ca sistemul să eșueze într-un mod mai "grațios", astfel încât utilizatorii să resimtă acest lucru cât mai puțin. Această practică nu este atât de des întâlnită, iar atunci când este folosită, este, de obicei, limitată la utilizarea încercărilor de obținere a unor date de la un API sau de la un serviciu extern. În astfel de situații, este recomandată mascarea erorii prin returnarea unor date care țin locul celor reale unde este posibil, sau prin folosirea unor date anterioare care au fost salvate într-o memorie de tip cache a aplicației, acolo unde riscul unei astfel de schimbări este minim și probabil nu ar avea efecte asupra experienței utilizatorului.

Prevenirea erorilor

Spre deosebire de abordarea anterioară, aceasta este mult mai rar întâlnită, întrucât necesită un nivel de planificare mult mai mare, care trebuie să includă posibilitatea prevederii tuturor sau măcar a majorității erorilor care pot apărea, indiferent de cât de puțin probabilă este apariția lor. De înțeles că acest lucru este relativ greu de atins, motiv pentru care vom propune câteva metode generale de prevenire a erorilor sau a altor lucruri care nu pot să meargă conform așteptărilor.

Timeout - De multe ori, acesta unealtă, timeout, este utilizată de majoritatea celor care o folosesc fără măcar să fie văzută ca un mecanism de prevenție a erorilor, fiind inclusă pur și simplu în integrările cu alte servicii. Acest lucru, dacă o face să fie cea mai întâlnită, îi limitează sever potențialul, nerealizându-se utilizarea ei completă. În situația folosirii sale complete, mecanismul de timeout poate să ofere un mod de opri intenționat unele apeluri sau execuții pentru a trece direct la una din modalitățile de gestionare a erorii, condiție necesară pentru o experiență rapidă a utilizatorului. După o anumită durată, succesul e tot mai puțin probabil, motiv pentru care timeout-ul ne lasă să ne degradăm grațios și controlat mai rapid.

Bulkhead Isolation - Întâlnit de multe ori și în alte industrii, când vine vorba despre arhitectura unui API, conceptul de izolare a diferitelor componente nu este mereu pe primul plan, fiind uneori omis în întregime. În cazul nostru, acest concept nu se referă doar la separarea la un nivel arhitectural, ci și la o separare a serviciilor printr-o limitare de resurse de care ele pot dispune în cazul unei erori. Astfel, dacă o parte din sistemul nostru întâlnește o eroare, acea eroare va rămâne în "carantină" în zona sa, consumând un maxim alocat de resurse care nu ar trebui să se repercuteze asupra funcționării fluide a celorlalte componente.

Cache - Uneori, poate cel mai bun mod de a preveni apariția unei noi erori la folosirea unui serviciu extern este chiar evitarea folosirii serviciului. Pentru a reuși acest lucru, o modalitate de caching poate păstra date puțin probabil să se schimbe la fiecare apel către un serviciu extern, care nu ar mai trebui luate de fiecare dată, scăzând astfel riscul de eșec. Chiar dacă este extrem de util, acest tip de mecanism ar trebui să fie folosit doar atunci când există certitudinea că utilizarea unor date "vechi" nu reprezintă un impact major pentru utilizator. Totuși, în situațiile în care poate să fie folosit, cachingul scade riscul de eșec până la 0, făcându-l cel mai de încredere mod de prevenire a erorilor neprevăzute, motiv pentru care includerea lui unde este fezabil este mai mult decât recomandată.

Pentru a rezuma, reziliența unei aplicații este un concept extrem de important pentru o funcționare de calitate a unui sistem, chiar dacă de multe ori nu este luat în calcul în activ sau este tratat ca o adăugare secundară, în timp ce alte cerințe sunt implementate. Chiar și așa, orice aplicație sau orice API care depinde de alte servicii externe ar trebui să includă cel puțin minimul de reziliență prin gestionarea erorilor menționate mai sus. Chiar dacă prevenirea este mai recomandată decât gestionarea, cele două sunt gândite pentru a conlucra, putând atinge nivelul ideal de redresare și reziliență doar ca un întreg.

Metodele menționate mai sus asigură un minim de reziliență de la care se poate pleca pentru a încerca modalități mai avansate și mai bine croite pentru fiecare sistem, întrucât fiecare este diferit și are nevoile proprii.

În încheiere, orice meșter bun trebuie să știe să-și aleagă bine uneltele. De aceea, încurajează utilizarea diferitelor librării pentru reziliență deja existente, care fac adăugarea acestui concept mult mai ușoară, oferind modalități de implementare care nu pleacă de la 0 și care pot să crească eficiența și corectitudinea implementării, dar și să scadă timpul pentru finalizarea implementării.

NUMĂRUL 138 - Maps & AI

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • Connatix
  • BoatyardX
  • .msg systems
  • Yardi
  • Colors in projects

INTERVIURI VIDEO