![Search icon](https://hackernoon.imgix.net/search-new.png?w=19&h=19)
Otpornost softvera odnosi se na sposobnost aplikacije da nastavi funkcionirati glatko i pouzdano, čak i u slučaju neočekivanih problema ili kvarova. U Fintech projektima otpornost je od posebno velike važnosti zbog nekoliko razloga. Prvo, tvrtke su obvezne ispuniti regulatorne zahtjeve, a financijski regulatori naglašavaju operativnu otpornost kako bi održali stabilnost unutar sustava. Štoviše, širenje digitalnih alata i oslanjanje na pružatelje usluga trećih strana izlaže Fintech tvrtke povećanim sigurnosnim prijetnjama. Otpornost također pomaže u ublažavanju rizika od prekida rada uzrokovanih raznim čimbenicima kao što su cyber prijetnje, pandemije ili geopolitički događaji, čuvajući osnovne poslovne operacije i kritičnu imovinu.
Pod obrascima otpornosti razumijemo skup najboljih praksi i strategija osmišljenih kako bi se osiguralo da softver može izdržati prekide i održati svoje operacije. Ovi obrasci djeluju poput sigurnosnih mreža, pružajući mehanizme za rukovanje pogreškama, upravljanje opterećenjem i oporavak od kvarova, čime se osigurava da aplikacije ostanu robusne i pouzdane u nepovoljnim uvjetima.
Najčešće strategije otpornosti uključuju pregradu, predmemoriju, zamjenu, ponovni pokušaj i prekidač strujnog kruga. U ovom ću članku detaljnije govoriti o njima, uz primjere problema koje mogu pomoći u rješavanju.
Pogledajmo gornju postavku. Imamo vrlo običnu aplikaciju s nekoliko pozadina iza nas iz kojih možemo dobiti neke podatke. Nekoliko je HTTP klijenata povezanih s ovim pozadinama. Ispada da svi dijele isti skup veza! I također druge resurse kao što su CPU i RAM.
Što će se dogoditi ako jedna od pozadina doživi neku vrstu problema koji rezultiraju velikom latencijom zahtjeva? Zbog dugog vremena odgovora, cijeli skup veza će biti u potpunosti zauzet zahtjevima koji čekaju odgovore od backend1. Kao rezultat toga, zahtjevi namijenjeni zdravoj pozadini2 i pozadini3 neće se moći nastaviti jer je skup podataka iscrpljen. To znači da kvar u jednoj od naših pozadina može uzrokovati kvar u cijeloj aplikaciji. U idealnom slučaju, želimo da samo funkcionalnost povezana s neispravnom pozadinom doživi degradaciju, dok ostatak aplikacije nastavi raditi normalno.
Što je uzorak pregrade?
Pojam, uzorak pregrada, potječe iz brodogradnje, uključuje stvaranje nekoliko izoliranih odjeljaka unutar broda. Ako dođe do curenja u jednom odjeljku, on se puni vodom, ali ostali odjeljci ostaju nepromijenjeni. Ova izolacija sprječava potonuće cijelog plovila zbog jednog proboja.
Uzorak pregrade može se koristiti za izolaciju različitih vrsta resursa unutar aplikacije, sprječavajući da kvar u jednom dijelu utječe na cijeli sustav. Evo kako to možemo primijeniti na naš problem:
Pretpostavimo da naši pozadinski sustavi imaju malu vjerojatnost da će pojedinačno naići na pogreške. Međutim, kada operacija uključuje paralelno postavljanje upita svim tim pozadinama, svaka može nezavisno vratiti pogrešku. Budući da se ove pogreške događaju neovisno, ukupna vjerojatnost pogreške u našoj aplikaciji veća je od vjerojatnosti pogreške bilo koje pojedinačne pozadine. Kumulativna vjerojatnost pogreške može se izračunati pomoću formule P_total=1−(1−p)^n, gdje je n broj pozadinskih sustava.
Na primjer, ako imamo deset pozadina, svaka s vjerojatnošću pogreške od p=0,001 (što odgovara SLA od 99,9%), rezultirajuća vjerojatnost pogreške je:
P_ukupno=1−(1−0,001)^10=0,009955
To znači da naš kombinirani SLA pada na približno 99%, što ilustrira kako se ukupna pouzdanost smanjuje prilikom paralelnog postavljanja upita višestrukim pozadinama. Kako bismo ublažili ovaj problem, možemo implementirati predmemoriju u memoriji.
Predmemorija u memoriji služi kao međuspremnik podataka velike brzine, pohranjuje podatke kojima se često pristupa i eliminira potrebu za njihovim dohvaćanjem iz potencijalno sporih izvora svaki put. Budući da predmemorije pohranjene u memoriji imaju 0% šanse za pogrešku u usporedbi s dohvaćanjem podataka putem mreže, značajno povećavaju pouzdanost naše aplikacije. Štoviše, predmemoriranje smanjuje mrežni promet, dodatno smanjujući mogućnost pogrešaka. Posljedično, korištenjem predmemorije u memoriji, možemo postići još nižu stopu pogreške u našoj aplikaciji u usporedbi s našim pozadinskim sustavima. Dodatno, predmemorije u memoriji nude brže dohvaćanje podataka od mrežnog dohvaćanja, čime se smanjuje kašnjenje aplikacije - značajna prednost.
Za personalizirane podatke, kao što su korisnički profili ili preporuke, korištenje predmemorija u memoriji također može biti vrlo učinkovito. Ali moramo osigurati da svi zahtjevi korisnika dosljedno idu na istu instancu aplikacije kako bi se za njih koristili predmemorirani podaci, što zahtijeva ljepljive sesije. Implementacija ljepljivih sesija može biti izazovna, ali za ovaj scenarij ne trebaju nam složeni mehanizmi. Prihvatljivo je manje ponovno balansiranje prometa, tako da će biti dovoljan algoritam za stabilno balansiranje opterećenja poput dosljednog raspršivanja.
Štoviše, u slučaju kvara čvora, dosljedno raspršivanje osigurava da samo korisnici koji su povezani s pokvarenim čvorom prolaze kroz ponovno balansiranje, minimizirajući smetnje u sustavu. Ovaj pristup pojednostavljuje upravljanje personaliziranim predmemorijama i poboljšava ukupnu stabilnost i performanse naše aplikacije.
Ako su podaci koje namjeravamo spremiti u predmemoriju kritični i koriste se u svakom zahtjevu koji naš sustav obrađuje, kao što su pravila pristupa, planovi pretplate ili drugi vitalni entiteti u našoj domeni—izvor ovih podataka mogao bi predstavljati značajnu točku kvara u našem sustavu. Kako bismo odgovorili na ovaj izazov, jedan pristup je potpuna replikacija ovih podataka izravno u memoriju naše aplikacije.
U ovom scenariju, ako se količinom podataka u izvoru može upravljati, možemo pokrenuti postupak preuzimanjem snimke ovih podataka na početku naše aplikacije. Nakon toga možemo primati događaje ažuriranja kako bismo osigurali da će podaci u predmemoriji ostati sinkronizirani s izvorom. Usvajanjem ove metode povećavamo pouzdanost pristupa ovim ključnim podacima, budući da se svako dohvaćanje događa izravno iz memorije s vjerojatnošću pogreške od 0%. Dodatno, dohvaćanje podataka iz memorije iznimno je brzo, čime se optimizira rad naše aplikacije. Ova strategija učinkovito umanjuje rizik povezan s oslanjanjem na vanjski izvor podataka, osiguravajući dosljedan i pouzdan pristup kritičnim informacijama za rad naše aplikacije.
Međutim, potreba za preuzimanjem podataka o pokretanju aplikacije, čime se odgađa proces pokretanja, krši jedno od načela '12-faktorske aplikacije' koja zagovara brzo pokretanje aplikacije. No, ne želimo izgubiti prednosti korištenja predmemoriranja. Kako bismo riješili ovu dilemu, istražimo moguća rješenja.
Brzo pokretanje je ključno, posebno za platforme poput Kubernetesa, koje se oslanjaju na brzu migraciju aplikacija na različite fizičke čvorove. Srećom, Kubernetes može upravljati aplikacijama koje se sporo pokreću pomoću značajki kao što su sonde za pokretanje.
Drugi izazov s kojim se možemo suočiti je ažuriranje konfiguracija dok je aplikacija pokrenuta. Često je podešavanje vremena predmemorije ili isteka zahtjeva potrebno za rješavanje problema proizvodnje. Čak i ako možemo brzo implementirati ažurirane konfiguracijske datoteke u našu aplikaciju, primjena ovih promjena obično zahtijeva ponovno pokretanje. Uz produljeno vrijeme pokretanja svake aplikacije, ponovno pokretanje može značajno odgoditi implementaciju popravaka našim korisnicima.
Kako bi se to riješilo, jedno je rješenje pohranjivanje konfiguracija u istodobnu varijablu i povremeno ažuriranje pozadinske niti. Međutim, određeni parametri, kao što su istek vremena za HTTP zahtjev, mogu zahtijevati ponovnu inicijalizaciju HTTP-a ili klijenata baze podataka kada se odgovarajuća konfiguracija promijeni, što predstavlja potencijalni izazov. Ipak, neki klijenti, poput Cassandra upravljačkog programa za Javu, podržavaju automatsko ponovno učitavanje konfiguracija, pojednostavljujući ovaj proces.
Implementacija konfiguracija koje se mogu ponovno učitavati može ublažiti negativan utjecaj dugog vremena pokretanja aplikacije i ponuditi dodatne pogodnosti, kao što je olakšavanje implementacije oznake značajke. Ovaj pristup nam omogućuje da održimo pouzdanost aplikacije i odzivnost dok učinkovito upravljamo ažuriranjima konfiguracije.
Sada pogledajmo još jedan problem: u našem sustavu, kada se korisnički zahtjev primi i obradi slanjem upita u pozadinu ili bazu podataka, povremeno se umjesto očekivanih podataka primi odgovor o pogrešci. Nakon toga, naš sustav odgovara korisniku s 'greškom'.
Međutim, u mnogim scenarijima može biti poželjnije prikazati malo zastarjele podatke zajedno s porukom koja ukazuje da postoji odgoda osvježavanja podataka, umjesto da korisnik ostane s velikom crvenom porukom o pogrešci.
Kako bismo riješili ovaj problem i poboljšali ponašanje našeg sustava, možemo implementirati zamjenski obrazac. Koncept iza ovog obrasca uključuje postojanje sekundarnog izvora podataka, koji može sadržavati podatke niže kvalitete ili svježine u usporedbi s primarnim izvorom. Ako je primarni izvor podataka nedostupan ili vraća pogrešku, sustav se može vratiti na dohvaćanje podataka iz ovog sekundarnog izvora, osiguravajući da se korisniku prezentira neki oblik informacija umjesto prikazivanja poruke o pogrešci.
Ako pogledate gornju sliku, primijetit ćete sličnost između problema s kojim se sada suočavamo i onog s kojim smo se susreli s primjerom predmemorije.
Da bismo to riješili, možemo razmotriti implementaciju uzorka poznatog kao ponovni pokušaj. Umjesto oslanjanja na predmemorije, sustav se može dizajnirati tako da automatski ponovno pošalje zahtjev u slučaju pogreške. Ovaj uzorak ponovnog pokušaja nudi jednostavniju alternativu i može učinkovito smanjiti vjerojatnost pogrešaka u našoj aplikaciji. Za razliku od predmemoriranja, koje često zahtijeva složene mehanizme poništenja predmemorije za obradu promjena podataka, ponovni pokušaj neuspjelih zahtjeva relativno je jednostavan za implementaciju. Budući da se poništavanje predmemorije općenito smatra jednim od najizazovnijih zadataka u softverskom inženjerstvu, usvajanje strategije ponovnog pokušaja može pojednostaviti rukovanje pogreškama i poboljšati otpornost sustava.
Međutim, usvajanje strategije ponovnog pokušaja bez razmatranja mogućih posljedica može dovesti do daljnjih komplikacija.
Zamislimo da jedna od naših pozadina doživi kvar. U takvom scenariju, pokretanje ponovnih pokušaja neuspješnoj pozadini moglo bi rezultirati značajnim povećanjem količine prometa. Ovaj iznenadni porast prometa može preopteretiti pozadinu, pogoršati kvar i potencijalno uzrokovati kaskadni učinak u cijelom sustavu.
Da biste se nosili s ovim izazovom, važno je nadopuniti uzorak ponovnog pokušaja s uzorkom prekidača. Prekidač strujnog kruga služi kao zaštitni mehanizam koji prati stopu pogreške nizvodnih usluga. Kada stopa pogreške prijeđe unaprijed definirani prag, prekidač strujnog kruga prekida zahtjeve pogođenoj usluzi na određeno vrijeme. Tijekom tog razdoblja sustav se suzdržava od slanja dodatnih zahtjeva kako bi se omogućio oporavak neuspjele usluge. Nakon određenog intervala, prekidač oprezno dopušta ograničenom broju zahtjeva da prođu, provjeravajući je li se usluga stabilizirala. Ako se usluga oporavila, normalan promet se postupno uspostavlja; inače krug ostaje otvoren, nastavljajući blokirati zahtjeve sve dok usluga ne nastavi s normalnim radom. Integriranjem uzorka prekidača zajedno s logikom ponovnog pokušaja, možemo učinkovito upravljati situacijama grešaka i spriječiti preopterećenje sustava tijekom pozadinskih kvarova.
Zaključno, implementacijom ovih obrazaca otpornosti možemo ojačati svoje aplikacije protiv hitnih slučajeva, održati visoku dostupnost i pružiti besprijekorno iskustvo korisnicima. Nadalje, želio bih naglasiti da je telemetrija još jedan alat koji se ne smije zanemariti kada se osigurava otpornost projekta. Dobri zapisnici i metrika mogu značajno poboljšati kvalitetu usluga i pružiti vrijedne uvide u njihovu izvedbu, pomažući u donošenju informiranih odluka za njihovo daljnje poboljšanje.