Ծրագրային ապահովման մեջ ճկունությունը վերաբերում է հավելվածի կարողությանը սահուն և հուսալիորեն շարունակելու գործել նույնիսկ անսպասելի խնդիրների կամ ձախողումների դեպքում: Fintech նախագծերում ճկունությունը հատկապես մեծ նշանակություն ունի մի քանի պատճառներով։ Նախ, ընկերությունները պարտավոր են բավարարել կարգավորող պահանջները, իսկ ֆինանսական կարգավորիչները շեշտում են գործառնական ճկունությունը՝ համակարգի ներսում կայունությունը պահպանելու համար: Ավելին, թվային գործիքների տարածումը և երրորդ կողմի ծառայություններ մատուցողներից կախվածությունը Fintech բիզնեսներին ենթարկում է անվտանգության ուժեղացված սպառնալիքների: Ճկունությունը նաև օգնում է մեղմել տարբեր գործոնների պատճառով առաջացած անջատումների ռիսկերը, ինչպիսիք են կիբեր սպառնալիքները, համաճարակները կամ աշխարհաքաղաքական իրադարձությունները, պաշտպանելով հիմնական բիզնես գործառնությունները և կարևոր ակտիվները:
Ճկունության օրինաչափություններով մենք հասկանում ենք լավագույն փորձի և ռազմավարությունների մի շարք, որոնք նախատեսված են ապահովելու, որ ծրագրակազմը կարող է դիմակայել խափանումներին և պահպանել իր գործունեությունը: Այս օրինաչափությունները գործում են որպես անվտանգության ցանցեր՝ ապահովելով սխալները կարգավորելու, բեռը կառավարելու և ձախողումներից վերականգնելու մեխանիզմներ՝ դրանով իսկ ապահովելով, որ հավելվածները մնան ամուր և հուսալի անբարենպաստ պայմաններում:
Ճկունության ամենատարածված ռազմավարությունները ներառում են միջնորմ, քեշ, հետադարձ, կրկնակի փորձ և անջատիչ: Այս հոդվածում ես դրանք ավելի մանրամասն կքննարկեմ՝ ներկայացնելով խնդիրների օրինակներ, որոնք կարող են օգնել լուծելու համար:
Եկեք նայենք վերը նշված պարամետրին: Մենք ունենք մի շատ սովորական հավելված, որի հետևում կան մի քանի հետնամասեր, որոնցից որոշակի տվյալներ ստանալու համար: Կան մի քանի HTTP հաճախորդներ, որոնք կապված են այս հետնամասերի հետ: Պարզվում է, որ նրանք բոլորը կիսում են նույն կապի լողավազանը: Եվ նաև այլ ռեսուրսներ, ինչպիսիք են CPU-ն և RAM-ը:
Ի՞նչ տեղի կունենա, եթե հետին պլաններից որևէ մեկի մոտ ինչ-որ խնդիրներ առաջանան, որոնք կհանգեցնեն հարցումների մեծ ուշացման: Արձագանքման բարձր ժամանակի պատճառով կապի ողջ լողավազանը ամբողջությամբ կզբաղեցվի backend1-ի պատասխաններին սպասող հարցումներով: Արդյունքում առողջ backend2-ի և backend3-ի համար նախատեսված հարցումները չեն կարող շարունակվել, քանի որ լողավազանը սպառվել է: Սա նշանակում է, որ մեր հետին պլաններից մեկի ձախողումը կարող է ձախողում առաջացնել ամբողջ հավելվածում: Իդեալում, մենք ցանկանում ենք, որ միայն ձախողված հետին պլանի հետ կապված ֆունկցիոնալությունը դեգրադացիայի ենթարկվի, մինչդեռ հավելվածի մնացած մասը շարունակի նորմալ աշխատել:
Ի՞նչ է միջնորմային նախշը:
«Bulkhead pattern» տերմինը բխում է նավաշինությունից, այն ներառում է նավի ներսում մի քանի մեկուսացված խցիկների ստեղծում: Եթե մի խցիկում արտահոսք է տեղի ունենում, այն լցվում է ջրով, բայց մյուս խցիկները մնում են անփոփոխ: Այս մեկուսացումը կանխում է ամբողջ նավի խորտակումը մեկ խախտման պատճառով:
«Bulkhead» օրինակը կարող է օգտագործվել հավելվածի ներսում տարբեր տեսակի ռեսուրսներ մեկուսացնելու համար՝ կանխելով մի մասի ձախողումը ամբողջ համակարգի վրա: Ահա թե ինչպես կարող ենք կիրառել այն մեր խնդրին.
Ենթադրենք, մեր backend համակարգերը առանձին-առանձին սխալների հանդիպելու փոքր հավանականություն ունեն: Այնուամենայնիվ, երբ գործողությունը ներառում է այս բոլոր հետին մասերի զուգահեռ հարցումը, յուրաքանչյուրը կարող է ինքնուրույն սխալ վերադարձնել: Քանի որ այս սխալները տեղի են ունենում ինքնուրույն, մեր հավելվածում սխալի ընդհանուր հավանականությունը ավելի բարձր է, քան որևէ մեկ հետին պլանի սխալի հավանականությունը: Կուտակային սխալի հավանականությունը կարելի է հաշվարկել՝ օգտագործելով P_total=1−(1−p)^n բանաձևը, որտեղ n-ը backend համակարգերի թիվն է։
Օրինակ, եթե մենք ունենք տասը հետին պլան, որոնցից յուրաքանչյուրը p=0,001 սխալի հավանականությամբ (համապատասխանում է SLA-ին 99,9%), արդյունքում սխալի հավանականությունը հետևյալն է.
P_total=1−(1−0.001)^10=0.009955
Սա նշանակում է, որ մեր համակցված SLA-ն իջնում է մինչև մոտավորապես 99%, ինչը ցույց է տալիս, թե ինչպես է ընդհանուր հուսալիությունը նվազում, երբ զուգահեռաբար բազմաթիվ հետնամասեր հարցումներ ենք կատարում: Այս խնդիրը մեղմելու համար մենք կարող ենք ներդնել հիշողության քեշ:
Հիշողության քեշը ծառայում է որպես գերարագ տվյալների բուֆեր՝ պահելով հաճախակի մուտք գործվող տվյալները և վերացնելով դրանք ամեն անգամ պոտենցիալ դանդաղ աղբյուրներից վերցնելու անհրաժեշտությունը: Քանի որ հիշողության մեջ պահվող քեշերը ունեն 0% սխալի հավանականություն՝ համեմատած ցանցով տվյալների առբերման հետ, դրանք զգալիորեն մեծացնում են մեր հավելվածի հուսալիությունը: Ավելին, քեշավորումը նվազեցնում է ցանցի երթևեկությունը՝ ավելի նվազեցնելով սխալների հավանականությունը: Հետևաբար, օգտագործելով հիշողության քեշը, մենք կարող ենք հասնել սխալի էլ ավելի ցածր մակարդակի մեր հավելվածում՝ համեմատած մեր backend համակարգերի հետ: Բացի այդ, հիշողության քեշերն առաջարկում են տվյալների ավելի արագ որոնում, քան ցանցի վրա հիմնված առբերումը, դրանով իսկ նվազեցնելով հավելվածի հետաձգումը, ինչը նշանակալի առավելություն է:
Անհատականացված տվյալների համար, ինչպիսիք են օգտատերերի պրոֆիլները կամ առաջարկությունները, հիշողության քեշերի օգտագործումը նույնպես կարող է շատ արդյունավետ լինել: Բայց մենք պետք է ապահովենք, որ օգտատերերի բոլոր հարցումները հետևողականորեն գնում են նույն հավելվածի օրինակ՝ օգտագործելու նրանց համար պահված տվյալները, ինչը պահանջում է կպչուն նիստեր: Կպչուն նիստերի իրականացումը կարող է դժվար լինել, բայց այս սցենարի համար մեզ բարդ մեխանիզմներ պետք չեն: Աննշան երթևեկության վերաբալանսը ընդունելի է, ուստի կայուն բեռների հավասարակշռման ալգորիթմը, ինչպիսին է հետևողական հեշինգը, բավարար կլինի:
Ավելին, հանգույցի ձախողման դեպքում հետևողական հեշինգը ապահովում է, որ միայն ձախողված հանգույցի հետ կապված օգտվողները վերաբալանսի ենթարկվեն՝ նվազագույնի հասցնելով համակարգի խափանումները: Այս մոտեցումը պարզեցնում է անհատականացված քեշերի կառավարումը և բարձրացնում մեր հավելվածի ընդհանուր կայունությունն ու կատարումը:
Եթե տվյալները, որոնք մենք մտադիր ենք քեշավորել, կարևոր են և օգտագործվում են մեր համակարգի կողմից մշակված յուրաքանչյուր հարցումում, ինչպիսիք են մուտքի քաղաքականությունը, բաժանորդագրության պլանները կամ մեր տիրույթի այլ կարևոր օբյեկտները, ապա այս տվյալների աղբյուրը կարող է զգալի ձախողում դառնալ մեր համակարգում: Այս մարտահրավերը լուծելու համար մոտեցումներից մեկն այս տվյալները ամբողջությամբ վերարտադրելն է անմիջապես մեր հավելվածի հիշողության մեջ:
Այս սցենարում, եթե աղբյուրի տվյալների ծավալը կառավարելի է, մենք կարող ենք գործընթացը սկսել՝ ներբեռնելով այս տվյալների նկարը մեր հավելվածի սկզբում: Հետագայում մենք կարող ենք թարմացումների իրադարձություններ ստանալ՝ ապահովելու համար, որ քեշավորված տվյալները մնան աղբյուրի հետ համաժամանակացված: Ընդունելով այս մեթոդը՝ մենք բարձրացնում ենք այս կարևոր տվյալների հասանելիության հուսալիությունը, քանի որ յուրաքանչյուր որոնում տեղի է ունենում անմիջապես հիշողությունից՝ 0% սխալի հավանականությամբ: Բացի այդ, հիշողությունից տվյալների առբերումը չափազանց արագ է, դրանով իսկ օպտիմալացնելով մեր հավելվածի աշխատանքը: Այս ռազմավարությունը արդյունավետորեն նվազեցնում է արտաքին տվյալների աղբյուրին ապավինելու հետ կապված ռիսկը՝ ապահովելով հետևողական և հուսալի մուտք դեպի կարևոր տեղեկատվություն մեր հավելվածի գործունեության համար:
Այնուամենայնիվ, հավելվածի գործարկման վերաբերյալ տվյալները ներբեռնելու անհրաժեշտությունը, դրանով իսկ հետաձգելով գործարկման գործընթացը, խախտում է «12-գործոնային հավելվածի» սկզբունքներից մեկը, որը պաշտպանում է հավելվածների արագ գործարկումը: Սակայն մենք չենք ցանկանում կորցնել քեշավորման օգտագործման առավելությունները: Այս երկընտրանքը լուծելու համար եկեք ուսումնասիրենք հնարավոր լուծումները:
Արագ գործարկումը շատ կարևոր է հատկապես Kubernetes-ի նման հարթակների համար, որոնք ապավինում են հավելվածների արագ տեղափոխմանը տարբեր ֆիզիկական հանգույցներ: Բարեբախտաբար, Kubernetes-ը կարող է կառավարել դանդաղ գործարկվող հավելվածները՝ օգտագործելով այնպիսի գործառույթներ, ինչպիսիք են գործարկման զոնդերը:
Մյուս մարտահրավերը, որին մենք կարող ենք հանդիպել, հավելվածի գործարկման ընթացքում կոնֆիգուրացիաների թարմացումն է: Հաճախ, քեշի ժամանակի ճշգրտումը կամ հարցումների ժամանակի վերջնաժամկետը անհրաժեշտ է արտադրության խնդիրները լուծելու համար: Նույնիսկ եթե մենք կարողանանք արագ տեղակայել թարմացված կազմաձևման ֆայլերը մեր հավելվածում, այս փոփոխությունները կիրառելը սովորաբար պահանջում է վերագործարկում: Յուրաքանչյուր հավելվածի գործարկման երկարացված ժամանակի հետ մեկտեղ շարժական վերագործարկումը կարող է զգալիորեն հետաձգել մեր օգտատերերի համար ուղղումների տեղադրումը:
Այս խնդրի լուծման համար լուծումներից մեկն այն է, որ կոնֆիգուրացիաները պահպանվեն միաժամանակյա փոփոխականում և ֆոնային շարանը պարբերաբար թարմացնեն այն: Այնուամենայնիվ, որոշ պարամետրեր, ինչպիսիք են HTTP-ի հարցումների ժամանակի ընդհատումները, կարող են պահանջել HTTP-ի կամ տվյալների բազայի սպասառուների վերսկսումը, երբ համապատասխան կոնֆիգուրացիան փոխվում է, ինչը պոտենցիալ մարտահրավեր է: Այնուամենայնիվ, որոշ հաճախորդներ, ինչպիսիք են Cassandra-ի դրայվերը Java-ի համար, աջակցում են կոնֆիգուրացիաների ավտոմատ վերաբեռնումը՝ հեշտացնելով այս գործընթացը:
Վերալիցքավորվող կոնֆիգուրացիաների ներդրումը կարող է մեղմել հավելվածների գործարկման երկար ժամանակների բացասական ազդեցությունը և առաջարկել լրացուցիչ առավելություններ, ինչպիսիք են գործառույթների դրոշի ներդրման դյուրացումը: Այս մոտեցումը մեզ հնարավորություն է տալիս պահպանել հավելվածի հուսալիությունը և արձագանքողությունը՝ միաժամանակ արդյունավետ կերպով կառավարելով կազմաձևման թարմացումները:
Հիմա եկեք նայենք մեկ այլ խնդրի. մեր համակարգում, երբ օգտվողի հարցումը ստացվում և մշակվում է՝ հարցում ուղարկելով հետին պլան կամ տվյալների բազա, երբեմն սխալի պատասխան է ստացվում ակնկալվող տվյալների փոխարեն: Հետագայում մեր համակարգը պատասխանում է օգտագործողին «սխալով»:
Այնուամենայնիվ, շատ սցենարներում կարող է ավելի նախընտրելի լինել փոքր-ինչ հնացած տվյալները ցուցադրել հաղորդագրության հետ մեկտեղ, որը ցույց է տալիս, որ տվյալների թարմացման ուշացում կա, այլ ոչ թե օգտվողին թողնել մեծ կարմիր սխալի հաղորդագրություն:
Այս խնդիրը լուծելու և մեր համակարգի վարքագիծը բարելավելու համար մենք կարող ենք կիրառել Fallback օրինակը: Այս օրինաչափության հիմքում ընկած հայեցակարգը ներառում է տվյալների երկրորդական աղբյուր ունենալը, որը կարող է պարունակել ավելի ցածր որակի կամ թարմության տվյալներ՝ համեմատած առաջնային աղբյուրի հետ: Եթե տվյալների առաջնային աղբյուրը անհասանելի է կամ սխալ է վերադարձնում, համակարգը կարող է վերադառնալ տվյալ երկրորդական աղբյուրից տվյալների առբերմանը, ապահովելով, որ սխալի հաղորդագրություն ցուցադրելու փոխարեն տեղեկատվության ինչ-որ ձև ներկայացվի օգտվողին:
Եթե նայեք վերևի նկարին, ապա կնկատեք նմանություն մեր առջև ծառացած խնդրի և այն խնդրի միջև, որը մենք հանդիպեցինք քեշի օրինակով:
Այն լուծելու համար մենք կարող ենք դիտարկել օրինակի ներդրումը, որը հայտնի է որպես կրկնել փորձ: Քեշերին ապավինելու փոխարեն համակարգը կարող է նախագծվել այնպես, որ սխալի դեպքում հարցումը ավտոմատ կերպով վերաուղարկի: Կրկնակի այս օրինակն առաջարկում է ավելի պարզ այլընտրանք և կարող է արդյունավետորեն նվազեցնել մեր հավելվածում սխալների հավանականությունը: Ի տարբերություն քեշավորման, որը հաճախ պահանջում է քեշի անվավերացման բարդ մեխանիզմներ՝ տվյալների փոփոխությունները կարգավորելու համար, ձախողված հարցումները նորից փորձելը համեմատաբար պարզ է իրականացնել: Քանի որ քեշի անվավերացումը լայնորեն համարվում է ծրագրային ապահովման ճարտարագիտության ամենադժվար առաջադրանքներից մեկը, կրկնակի փորձի ռազմավարության ընդունումը կարող է հեշտացնել սխալների կառավարումը և բարելավել համակարգի ճկունությունը:
Այնուամենայնիվ, կրկին փորձելու ռազմավարության ընդունումը՝ առանց հնարավոր հետևանքները հաշվի առնելու, կարող է հանգեցնել հետագա բարդությունների:
Եկեք պատկերացնենք, որ մեր հետին պլաններից մեկը ձախողում է ապրում: Նման սցենարի դեպքում ձախողված հետին պլանում կրկնվող փորձերը կարող են հանգեցնել երթևեկության ծավալի զգալի աճի: Երթևեկության այս հանկարծակի աճը կարող է ճնշել հետին մասը՝ խորացնելով ձախողումը և պոտենցիալ կասկադային էֆեկտ առաջացնելով համակարգում:
Այս մարտահրավերին դիմակայելու համար կարևոր է կրկնակի փորձարկման օրինակը լրացնել անջատիչի օրինակով: Անջատիչը ծառայում է որպես պաշտպանիչ մեխանիզմ, որը վերահսկում է հոսանքով ներքև գտնվող ծառայությունների սխալի մակարդակը: Երբ սխալի մակարդակը գերազանցում է նախապես սահմանված շեմը, անջատիչն ընդհատում է ազդակիր ծառայության հարցումները որոշակի տևողությամբ: Այս ժամանակահատվածում համակարգը ձեռնպահ է մնում հավելյալ հարցումներ ուղարկելուց՝ թույլ տալու համար, որ չհաջողվի սպասարկման ժամանակը վերականգնել: Նշանակված ընդմիջումից հետո անջատիչը զգուշորեն թույլ է տալիս անցնել սահմանափակ թվով հարցումներ՝ ստուգելով, թե արդյոք ծառայությունը կայունացել է: Եթե ծառայությունը վերականգնվել է, նորմալ երթեւեկությունը աստիճանաբար վերականգնվում է. հակառակ դեպքում, միացումը մնում է բաց՝ շարունակելով արգելափակել հարցումները, մինչև ծառայությունը չվերսկսի բնականոն աշխատանքը: Ինտեգրելով անջատիչի օրինաչափությունը կրկին փորձելու տրամաբանության հետ մեկտեղ՝ մենք կարող ենք արդյունավետորեն կառավարել սխալի իրավիճակները և կանխել համակարգի ծանրաբեռնվածությունը հետնամասի խափանումների ժամանակ:
Եզրափակելով՝ կիրառելով այս ճկունության օրինաչափությունները՝ մենք կարող ենք ուժեղացնել մեր հավելվածները արտակարգ իրավիճակների դեմ, պահպանել բարձր հասանելիություն և անթերի փորձ մատուցել օգտատերերին: Բացի այդ, ես կցանկանայի ընդգծել, որ հեռաչափությունը ևս մեկ գործիք է, որը չպետք է անտեսվի նախագծի ճկունություն ապահովելիս: Լավ տեղեկամատյանները և չափումները կարող են զգալիորեն բարձրացնել ծառայությունների որակը և արժեքավոր պատկերացումներ տալ դրանց կատարման վերաբերյալ՝ օգնելով տեղեկացված որոշումներ կայացնել՝ դրանք հետագա բարելավելու համար: