![Search icon](https://hackernoon.imgix.net/search-new.png?w=19&h=19)
Odolnosť v softvéri sa týka schopnosti aplikácie pokračovať vo fungovaní hladko a spoľahlivo, dokonca aj pri neočakávaných problémoch alebo zlyhaniach. Vo Fintech projektoch je odolnosť obzvlášť dôležitá z niekoľkých dôvodov. Po prvé, spoločnosti sú povinné spĺňať regulačné požiadavky a finanční regulátori zdôrazňujú prevádzkovú odolnosť, aby udržali stabilitu v rámci systému. Navyše, šírenie digitálnych nástrojov a spoliehanie sa na poskytovateľov služieb tretích strán vystavuje fintech podniky zvýšeným bezpečnostným hrozbám. Odolnosť tiež pomáha zmierniť riziká výpadkov spôsobených rôznymi faktormi, ako sú kybernetické hrozby, pandémie alebo geopolitické udalosti, pričom chráni hlavné obchodné operácie a kritické aktíva.
Vzormi odolnosti rozumieme súbor osvedčených postupov a stratégií navrhnutých tak, aby zabezpečili, že softvér vydrží prerušenia a udrží svoju prevádzku. Tieto vzory fungujú ako bezpečnostné siete, ktoré poskytujú mechanizmy na zvládanie chýb, riadenie záťaže a obnovu po zlyhaní, čím zaisťujú, že aplikácie zostanú robustné a spoľahlivé aj za nepriaznivých podmienok.
Medzi najbežnejšie stratégie odolnosti patrí prepážka, vyrovnávacia pamäť, núdzový režim, opakovaný pokus a istič. V tomto článku ich rozoberiem podrobnejšie a uvediem príklady problémov, ktoré môžu pomôcť vyriešiť.
Pozrime sa na vyššie uvedené nastavenie. Máme za sebou celkom obyčajnú aplikáciu s niekoľkými backendmi, z ktorých môžeme získať nejaké dáta. K týmto backendom je pripojených niekoľko HTTP klientov. Ukazuje sa, že všetky zdieľajú rovnaký fond pripojení! A tiež ďalšie zdroje ako CPU a RAM.
Čo sa stane, ak jeden z koncových serverov zaznamená nejaké problémy, ktoré vedú k vysokej latencii požiadaviek? Kvôli vysokej dobe odozvy bude celý fond pripojení plne obsadený požiadavkami čakajúcimi na odpovede z backendu1. V dôsledku toho požiadavky určené pre zdravý backend2 a backend3 nebudú môcť pokračovať, pretože fond je vyčerpaný. To znamená, že zlyhanie jedného z našich backendov môže spôsobiť zlyhanie v celej aplikácii. V ideálnom prípade chceme, aby sa degradovala iba funkčnosť spojená so zlyhávajúcim backendom, zatiaľ čo zvyšok aplikácie bude naďalej normálne fungovať.
Čo je to prepážkový vzor?
Pojem, prepážkový vzor, pochádza zo stavby lodí, zahŕňa vytvorenie niekoľkých izolovaných oddelení v rámci lode. Ak dôjde k úniku v jednom oddelení, naplní sa vodou, ale ostatné oddelenia zostanú nedotknuté. Táto izolácia zabraňuje potopeniu celej nádoby v dôsledku jediného porušenia.
Vzor prepážky možno použiť na izoláciu rôznych typov zdrojov v rámci aplikácie, čím sa zabráni, aby zlyhanie jednej časti ovplyvnilo celý systém. Tu je návod, ako to môžeme použiť na náš problém:
Predpokladajme, že naše backendové systémy majú nízku pravdepodobnosť, že sa individuálne stretnú s chybami. Ak však operácia zahŕňa dopytovanie všetkých týchto koncových zariadení paralelne, každý môže nezávisle vrátiť chybu. Pretože sa tieto chyby vyskytujú nezávisle, celková pravdepodobnosť chyby v našej aplikácii je vyššia ako pravdepodobnosť chyby akéhokoľvek jednotlivého backendu. Kumulatívna pravdepodobnosť chyby sa môže vypočítať pomocou vzorca P_total=1−(1−p)^n, kde n je počet backendových systémov.
Napríklad, ak máme desať backendov, každý s pravdepodobnosťou chyby p=0,001 (zodpovedá SLA 99,9 %), výsledná pravdepodobnosť chyby je:
P_total=1−(1−0,001)^10=0,009955
To znamená, že naša kombinovaná zmluva SLA klesne na približne 99 %, čo ilustruje, ako sa znižuje celková spoľahlivosť pri paralelnom dopytovaní viacerých backendov. Na zmiernenie tohto problému môžeme implementovať vyrovnávaciu pamäť v pamäti.
Pamäťová vyrovnávacia pamäť slúži ako vysokorýchlostná vyrovnávacia pamäť údajov, ktorá ukladá často používané údaje a eliminuje potrebu ich zakaždým získavať z potenciálne pomalých zdrojov. Keďže vyrovnávacie pamäte uložené v pamäti majú 0% pravdepodobnosť chyby v porovnaní s načítaním údajov cez sieť, výrazne zvyšujú spoľahlivosť našej aplikácie. Okrem toho ukladanie do vyrovnávacej pamäte znižuje sieťovú prevádzku, čím ďalej znižuje pravdepodobnosť chýb. V dôsledku toho môžeme použitím vyrovnávacej pamäte v pamäti dosiahnuť ešte nižšiu chybovosť v našej aplikácii v porovnaní s našimi backend systémami. Okrem toho vyrovnávacie pamäte v pamäti ponúkajú rýchlejšie získavanie údajov ako načítanie založené na sieti, čím sa znižuje latencia aplikácie, čo je pozoruhodná výhoda.
Pre personalizované údaje, ako sú užívateľské profily alebo odporúčania, môže byť použitie vyrovnávacej pamäte v pamäti tiež vysoko efektívne. Musíme však zabezpečiť, aby všetky požiadavky od používateľa dôsledne smerovali do rovnakej inštancie aplikácie, aby sa využívali údaje uložené vo vyrovnávacej pamäti, čo si vyžaduje pevné relácie. Implementácia lepivých relácií môže byť náročná, ale pre tento scenár nepotrebujeme zložité mechanizmy. Menšie vyváženie návštevnosti je prijateľné, takže bude stačiť stabilný algoritmus vyvažovania záťaže, ako je konzistentné hashovanie.
A čo viac, v prípade zlyhania uzla konzistentné hashovanie zaisťuje, že iba používatelia priradení k zlyhávajúcemu uzlu podstúpia opätovné vyváženie, čím sa minimalizuje narušenie systému. Tento prístup zjednodušuje správu personalizovaných vyrovnávacích pamätí a zlepšuje celkovú stabilitu a výkon našej aplikácie.
Ak sú údaje, ktoré máme v úmysle ukladať do vyrovnávacej pamäte, kritické a používané pri každej požiadavke, ktorú náš systém spracováva, ako sú politiky prístupu, plány predplatného alebo iné dôležité entity v našej doméne – zdroj týchto údajov môže predstavovať významný bod zlyhania v našom systéme. Na vyriešenie tejto výzvy je jedným z prístupov úplná replikácia týchto údajov priamo do pamäte našej aplikácie.
V tomto scenári, ak je objem údajov v zdroji zvládnuteľný, môžeme spustiť proces stiahnutím snímky týchto údajov na začiatku našej aplikácie. Následne môžeme prijímať aktualizácie udalostí, aby sme zabezpečili, že údaje vo vyrovnávacej pamäti zostanú synchronizované so zdrojom. Prijatím tejto metódy zvyšujeme spoľahlivosť prístupu k týmto kľúčovým údajom, pretože každé vyhľadávanie prebieha priamo z pamäte s 0% pravdepodobnosťou chyby. Okrem toho je získavanie údajov z pamäte mimoriadne rýchle, čím sa optimalizuje výkon našej aplikácie. Táto stratégia účinne znižuje riziko spojené so spoliehaním sa na externý zdroj údajov a zabezpečuje konzistentný a spoľahlivý prístup k dôležitým informáciám pre fungovanie našej aplikácie.
Potreba sťahovať údaje pri štarte aplikácie, čím sa oneskoruje proces spustenia, však porušuje jednu z princípov „12-faktorovej aplikácie“, ktorá obhajuje rýchle spustenie aplikácie. Nechceme však prísť o výhody používania ukladania do vyrovnávacej pamäte. Ak chcete vyriešiť túto dilemu, poďme preskúmať potenciálne riešenia.
Rýchle spustenie je kľúčové, najmä pre platformy ako Kubernetes, ktoré sa spoliehajú na rýchlu migráciu aplikácií do rôznych fyzických uzlov. Našťastie Kubernetes dokáže spravovať pomaly sa spúšťajúce aplikácie pomocou funkcií, ako sú štartovacie sondy.
Ďalšou výzvou, ktorej môžeme čeliť, je aktualizácia konfigurácií počas spustenia aplikácie. Na vyriešenie produkčných problémov je často potrebné upraviť časy vyrovnávacej pamäte alebo časové limity požiadaviek. Aj keď môžeme rýchlo nasadiť aktualizované konfiguračné súbory do našej aplikácie, použitie týchto zmien zvyčajne vyžaduje reštart. S predĺženým časom spustenia každej aplikácie môže postupný reštart výrazne oddialiť nasadenie opráv našim používateľom.
Na vyriešenie tohto problému je jedným z riešení ukladať konfigurácie do súbežnej premennej a pravidelne ju aktualizovať vláknom na pozadí. Niektoré parametre, ako napríklad časové limity požiadaviek HTTP, však môžu vyžadovať opätovnú inicializáciu HTTP alebo databázových klientov, keď sa príslušná konfigurácia zmení, čo predstavuje potenciálny problém. Niektorí klienti, ako napríklad ovládač Cassandra pre Java, však podporujú automatické opätovné načítanie konfigurácií, čím sa tento proces zjednodušuje.
Implementácia konfigurácií s možnosťou opätovného načítania môže zmierniť negatívny vplyv dlhých časov spustenia aplikácií a ponúknuť ďalšie výhody, ako napríklad uľahčenie implementácie príznaku funkcie. Tento prístup nám umožňuje zachovať spoľahlivosť a odozvu aplikácií a zároveň efektívne spravovať aktualizácie konfigurácie.
Teraz sa pozrime na ďalší problém: v našom systéme, keď je požiadavka používateľa prijatá a spracovaná odoslaním dotazu do backendu alebo databázy, príležitostne sa namiesto očakávaných údajov dostane chybová odpoveď. Následne náš systém odpovedá používateľovi „chybou“.
V mnohých scenároch však môže byť vhodnejšie zobraziť mierne zastarané údaje spolu so správou oznamujúcou oneskorenie obnovenia údajov, než nechať používateľovi veľké červené chybové hlásenie.
Aby sme tento problém vyriešili a zlepšili správanie nášho systému, môžeme implementovať záložný vzor. Koncept tohto vzoru zahŕňa sekundárny zdroj údajov, ktorý môže obsahovať údaje nižšej kvality alebo aktuálnosti v porovnaní s primárnym zdrojom. Ak primárny zdroj údajov nie je dostupný alebo vráti chybu, systém sa môže vrátiť k získaniu údajov z tohto sekundárneho zdroja, čím sa zabezpečí, že sa používateľovi namiesto zobrazenia chybového hlásenia zobrazí určitá forma informácií.
Ak sa pozriete na obrázok vyššie, všimnete si podobnosť medzi problémom, ktorému teraz čelíme, a tým, s ktorým sme sa stretli pri príklade vyrovnávacej pamäte.
Aby sme to vyriešili, môžeme zvážiť implementáciu vzoru známeho ako opakovanie. Namiesto spoliehania sa na vyrovnávacie pamäte môže byť systém navrhnutý tak, aby automaticky znova odoslal požiadavku v prípade chyby. Tento vzor opakovania ponúka jednoduchšiu alternatívu a môže účinne znížiť pravdepodobnosť chýb v našej aplikácii. Na rozdiel od ukladania do vyrovnávacej pamäte, ktoré si často vyžaduje zložité mechanizmy zneplatnenia vyrovnávacej pamäte na spracovanie zmien údajov, je implementácia opakovaných neúspešných požiadaviek pomerne jednoduchá. Keďže zneplatnenie vyrovnávacej pamäte je všeobecne považované za jednu z najnáročnejších úloh v softvérovom inžinierstve, prijatie stratégie opakovania môže zefektívniť spracovanie chýb a zlepšiť odolnosť systému.
Prijatie stratégie opakovania bez zohľadnenia možných dôsledkov však môže viesť k ďalším komplikáciám.
Predstavme si, že jeden z našich backendov zažije zlyhanie. V takomto scenári by iniciovanie opakovaných pokusov o zlyhávajúci backend mohlo viesť k výraznému zvýšeniu objemu prevádzky. Tento náhly nárast prevádzky môže zahltiť backend, zhoršiť zlyhanie a potenciálne spôsobiť kaskádový efekt v celom systéme.
Na zvládnutie tejto výzvy je dôležité doplniť vzor opakovania vzorom ističa. Istič slúži ako ochranný mechanizmus, ktorý monitoruje chybovosť nadväzujúcich služieb. Keď chybovosť prekročí vopred definovaný prah, istič preruší požiadavky na dotknutú službu na určitú dobu. Počas tohto obdobia sa systém zdrží odosielania dodatočných požiadaviek, aby umožnil zlyhávajúcej službe čas na obnovenie. Po určenom intervale istič opatrne nechá prejsť obmedzeným počtom požiadaviek a overí, či sa služba stabilizovala. Ak sa služba obnovila, normálna prevádzka sa postupne obnoví; v opačnom prípade zostane okruh otvorený a pokračuje v blokovaní požiadaviek, kým služba neobnoví normálnu prevádzku. Integráciou vzoru ističa spolu s logikou opakovania môžeme efektívne riadiť chybové situácie a predchádzať preťaženiu systému počas zlyhania backendu.
Na záver, implementáciou týchto vzorov odolnosti môžeme posilniť naše aplikácie proti núdzovým situáciám, udržiavať vysokú dostupnosť a poskytovať používateľom bezproblémovú skúsenosť. Okrem toho by som rád zdôraznil, že telemetria je ďalším nástrojom, ktorý by sa pri poskytovaní odolnosti projektu nemal prehliadať. Dobré protokoly a metriky môžu výrazne zvýšiť kvalitu služieb a poskytnúť cenné informácie o ich výkonnosti, čo pomôže prijímať informované rozhodnutia na ich ďalšie zlepšenie.