![Search icon](https://hackernoon.imgix.net/search-new.png?w=19&h=19)
Odolnost v softwaru se týká schopnosti aplikace pokračovat ve fungování hladce a spolehlivě, a to i v případě neočekávaných problémů nebo selhání. Ve Fintech projektech je odolnost obzvláště důležitá z několika důvodů. Za prvé, společnosti jsou povinny plnit regulační požadavky a finanční regulátoři kladou důraz na provozní odolnost, aby udrželi stabilitu v rámci systému. Navíc šíření digitálních nástrojů a spoléhání se na poskytovatele služeb třetích stran vystavuje fintech podniky zvýšeným bezpečnostním hrozbám. Odolnost také pomáhá zmírňovat rizika výpadků způsobených různými faktory, jako jsou kybernetické hrozby, pandemie nebo geopolitické události, a chrání základní obchodní operace a kritická aktiva.
Vzory odolnosti rozumíme soubor osvědčených postupů a strategií navržených tak, aby zajistily, že software vydrží přerušení a udrží svůj provoz. Tyto vzory fungují jako bezpečnostní sítě, které poskytují mechanismy pro zpracování chyb, řízení zátěže a zotavení ze selhání, čímž zajišťují, že aplikace zůstanou robustní a spolehlivé za nepříznivých podmínek.
Mezi nejběžnější strategie odolnosti patří přepážka, mezipaměť, záložní, opakování a jistič. V tomto článku je proberu podrobněji s příklady problémů, které mohou pomoci vyřešit.
Pojďme se podívat na výše uvedené nastavení. Máme za sebou velmi obyčejnou aplikaci s několika backendy, ze kterých získáme nějaká data. K těmto backendům je připojeno několik HTTP klientů. Ukázalo se, že všechny sdílejí stejný fond připojení! A také další zdroje, jako je CPU a RAM.
Co se stane, pokud jeden z backendů zaznamená nějaké problémy vedoucí k vysoké latenci požadavku? Vzhledem k dlouhé době odezvy bude celý fond připojení plně obsazen požadavky čekajícími na odpovědi z backendu1. V důsledku toho požadavky určené pro zdravý backend2 a backend3 nebudou moci pokračovat, protože fond je vyčerpán. To znamená, že selhání jednoho z našich backendů může způsobit selhání v celé aplikaci. V ideálním případě chceme, aby došlo k degradaci pouze funkčnosti spojené se selháním backendu, zatímco zbytek aplikace nadále normálně fungoval.
Co je to přepážkový vzor?
Termín, přepážkový vzor, pochází ze stavby lodí, zahrnuje vytvoření několika izolovaných oddílů v rámci lodi. Pokud dojde k úniku v jednom oddělení, naplní se vodou, ale ostatní oddělení zůstanou nedotčena. Tato izolace zabraňuje potopení celé nádoby v důsledku jediného porušení.
Vzor přepážky lze použít k izolaci různých typů zdrojů v rámci aplikace, čímž se zabrání tomu, aby selhání jedné části ovlivnilo celý systém. Zde je návod, jak to můžeme použít na náš problém:
Předpokládejme, že naše backendové systémy mají nízkou pravděpodobnost, že se jednotlivě setkají s chybami. Pokud však operace zahrnuje dotazování všech těchto backendů paralelně, každý může nezávisle vrátit chybu. Protože se tyto chyby vyskytují nezávisle, je celková pravděpodobnost chyby v naší aplikaci vyšší než pravděpodobnost chyby jakéhokoli jednotlivého backendu. Kumulativní pravděpodobnost chyby lze vypočítat pomocí vzorce P_total=1−(1−p)^n, kde n je počet backendových systémů.
Například, pokud máme deset backendů, každý s pravděpodobností chyby p=0,001 (odpovídající SLA 99,9 %), výsledná pravděpodobnost chyby je:
P_total=1−(1−0,001)^10=0,009955
To znamená, že naše kombinovaná smlouva SLA klesne na přibližně 99 %, což ukazuje, jak se snižuje celková spolehlivost při paralelním dotazování na více backendů. Abychom tento problém zmírnili, můžeme implementovat mezipaměť v paměti.
Mezipaměť v paměti slouží jako vysokorychlostní datová vyrovnávací paměť, která ukládá často používaná data a eliminuje potřebu je pokaždé načítat z potenciálně pomalých zdrojů. Protože mezipaměti uložené v paměti mají 0% pravděpodobnost chyby ve srovnání s načítáním dat přes síť, výrazně zvyšují spolehlivost naší aplikace. Ukládání do mezipaměti navíc snižuje síťový provoz a dále snižuje pravděpodobnost chyb. V důsledku toho můžeme využitím mezipaměti v paměti dosáhnout ještě nižší chybovosti v naší aplikaci ve srovnání s našimi backendovými systémy. Mezipaměti v paměti navíc nabízejí rychlejší načítání dat než načítání založené na síti, čímž snižují latenci aplikací – významná výhoda.
Pro personalizovaná data, jako jsou uživatelské profily nebo doporučení, může být použití mezipaměti v paměti také vysoce efektivní. Musíme však zajistit, aby všechny požadavky od uživatele konzistentně směřovaly do stejné instance aplikace, aby mohly využívat data uložená v mezipaměti, což vyžaduje pevné relace. Implementace pevných relací může být náročná, ale pro tento scénář nepotřebujeme složité mechanismy. Přerovnání menšího provozu je přijatelné, takže bude stačit stabilní algoritmus vyvažování zátěže, jako je konzistentní hash.
A co víc, v případě selhání uzlu konzistentní hašování zajišťuje, že pouze uživatelé přidružení k selhání uzlu podstoupí rebalancování, čímž se minimalizuje narušení systému. Tento přístup zjednodušuje správu personalizovaných mezipamětí a zvyšuje celkovou stabilitu a výkon naší aplikace.
Pokud jsou data, která máme v úmyslu ukládat do mezipaměti, kritická a používaná v každém požadavku, který náš systém zpracovává, jako jsou zásady přístupu, plány předplatného nebo jiné životně důležité entity v naší doméně, může zdroj těchto dat představovat významný bod selhání našeho systému. K vyřešení tohoto problému je jedním přístupem plně replikovat tato data přímo do paměti naší aplikace.
V tomto scénáři, pokud je objem dat ve zdroji zvládnutelný, můžeme zahájit proces stažením snímku těchto dat na začátku naší aplikace. Následně můžeme přijímat aktualizace událostí, abychom zajistili, že data uložená v mezipaměti zůstanou synchronizována se zdrojem. Přijetím této metody zvyšujeme spolehlivost přístupu k těmto zásadním datům, protože každé načítání probíhá přímo z paměti s 0% pravděpodobností chyby. Načítání dat z paměti je navíc mimořádně rychlé, čímž se optimalizuje výkon naší aplikace. Tato strategie účinně snižuje riziko spojené se spoléháním se na externí zdroj dat a zajišťuje konzistentní a spolehlivý přístup k důležitým informacím pro provoz naší aplikace.
Potřeba stahování dat při spouštění aplikace, čímž se proces spouštění zdržuje, však porušuje jeden z principů „12faktorové aplikace“ obhajující rychlé spouštění aplikací. Nechceme však přijít o výhody používání mezipaměti. Chcete-li vyřešit toto dilema, pojďme prozkoumat možná řešení.
Rychlé spuštění je klíčové zejména pro platformy jako Kubernetes, které spoléhají na rychlou migraci aplikací do různých fyzických uzlů. Naštěstí Kubernetes dokáže spravovat pomalu se spouštějící aplikace pomocí funkcí, jako jsou startovací sondy.
Dalším problémem, kterému můžeme čelit, je aktualizace konfigurací za běhu aplikace. K vyřešení produkčních problémů je často nutné upravit časy mezipaměti nebo časové limity požadavků. I když můžeme rychle nasadit aktualizované konfigurační soubory do naší aplikace, použití těchto změn obvykle vyžaduje restart. S prodlouženou dobou spouštění každé aplikace může postupný restart výrazně zpozdit nasazení oprav našim uživatelům.
Chcete-li tento problém vyřešit, jedním z řešení je uložit konfigurace do souběžné proměnné a nechat ji vlákno na pozadí pravidelně aktualizovat. Některé parametry, jako jsou časové limity požadavků HTTP, však mohou vyžadovat reinicializaci HTTP nebo databázových klientů, když se odpovídající konfigurace změní, což představuje potenciální problém. Přesto někteří klienti, jako je ovladač Cassandra pro Javu, podporují automatické opětovné načítání konfigurací, což tento proces zjednodušuje.
Implementace znovu načítatelných konfigurací může zmírnit negativní dopad dlouhých časů spouštění aplikací a nabídnout další výhody, jako je usnadnění implementace příznaků funkcí. Tento přístup nám umožňuje zachovat spolehlivost a odezvu aplikací a zároveň efektivně spravovat aktualizace konfigurace.
Nyní se podívejme na další problém: v našem systému, když je požadavek uživatele přijat a zpracován odesláním dotazu do backendu nebo databáze, příležitostně se místo očekávaných dat obdrží chybová odpověď. Následně náš systém uživateli odpoví „chybou“.
V mnoha scénářích však může být vhodnější zobrazit mírně zastaralá data spolu se zprávou, že došlo ke zpoždění obnovy dat, než nechat uživatele s velkou červenou chybovou zprávou.
Abychom tento problém vyřešili a zlepšili chování našeho systému, můžeme implementovat vzor Fallback. Koncept tohoto vzoru zahrnuje sekundární zdroj dat, který může obsahovat data nižší kvality nebo aktuálnosti ve srovnání s primárním zdrojem. Pokud je primární zdroj dat nedostupný nebo vrátí chybu, systém se může vrátit k načtení dat z tohoto sekundárního zdroje a zajistit, aby se uživateli namísto zobrazení chybové zprávy zobrazila určitá forma informací.
Pokud se podíváte na obrázek výše, všimnete si podobnosti mezi problémem, kterému nyní čelíme, a tím, na který jsme narazili u příkladu mezipaměti.
Abychom to vyřešili, můžeme zvážit implementaci vzoru známého jako opakování. Namísto spoléhání se na cache může být systém navržen tak, aby automaticky znovu odeslal požadavek v případě chyby. Tento vzor opakování nabízí jednodušší alternativu a může účinně snížit pravděpodobnost chyb v naší aplikaci. Na rozdíl od ukládání do mezipaměti, které ke zpracování změn dat často vyžaduje složité mechanismy zneplatnění mezipaměti, je implementace opakování neúspěšných požadavků relativně jednoduchá. Vzhledem k tomu, že zneplatnění mezipaměti je obecně považováno za jeden z nejnáročnějších úkolů v softwarovém inženýrství, přijetí strategie opakování může zefektivnit zpracování chyb a zlepšit odolnost systému.
Přijetí strategie opakování bez zvážení možných důsledků však může vést k dalším komplikacím.
Představme si, že jeden z našich backendů zažije selhání. V takovém scénáři by zahájení opakovaných pokusů se selháním backendu mohlo vést k výraznému zvýšení objemu provozu. Tento náhlý nárůst provozu může zahltit backend, zhoršit selhání a potenciálně způsobit kaskádový efekt v celém systému.
Abychom se s touto výzvou vyrovnali, je důležité doplnit vzor opakování vzorem jističe. Jistič slouží jako ochranný mechanismus, který monitoruje chybovost navazujících služeb. Když chybovost překročí předem definovanou prahovou hodnotu, přeruší jistič požadavky na dotčenou službu na určitou dobu. Během této doby se systém zdrží odesílání dodatečných požadavků, aby se porouchající služba mohla zotavit. Po uplynutí určeného intervalu jistič opatrně nechá projít omezený počet požadavků a ověří, zda se služba stabilizovala. Pokud se služba obnovila, normální provoz se postupně obnoví; jinak okruh zůstane otevřený a pokračuje v blokování požadavků, dokud služba neobnoví normální provoz. Integrací schématu jističe spolu s logikou opakování můžeme efektivně řídit chybové situace a zabránit přetížení systému během selhání backendu.
Závěrem lze říci, že implementací těchto vzorců odolnosti můžeme posílit naše aplikace proti nouzovým situacím, zachovat vysokou dostupnost a poskytovat uživatelům bezproblémové prostředí. Dále bych rád zdůraznil, že telemetrie je dalším nástrojem, který by neměl být přehlížen při zajišťování odolnosti projektu. Dobré protokoly a metriky mohou výrazně zvýšit kvalitu služeb a poskytnout cenné poznatky o jejich výkonu, což pomáhá činit informovaná rozhodnutí za účelem jejich dalšího zlepšování.