RoadRunner. PHP-ն ստեղծված չէ մեռնելու, կամ Գոլանգը փրկելու համար

RoadRunner. PHP-ն ստեղծված չէ մեռնելու, կամ Գոլանգը փրկելու համար

Հե՜յ Հաբր։ Մենք ակտիվ ենք Badoo-ում աշխատում է PHP-ի կատարման վրա, քանի որ մենք ունենք բավականին մեծ համակարգ այս լեզվով, և կատարողականի խնդիրը գումար խնայելու խնդիր է: Ավելի քան տասը տարի առաջ մենք դրա համար ստեղծեցինք PHP-FPM, որը սկզբում PHP-ի համար patches-ի հավաքածու էր, իսկ ավելի ուշ մտավ պաշտոնական բաշխում:

Վերջին տարիներին PHP-ն մեծ առաջընթաց է գրանցել՝ բարելավվել է աղբահանիչը, բարձրացել է կայունության մակարդակը. այսօր PHP-ում կարելի է առանց խնդիրների գրել դևերներ և երկարակյաց սկրիպտներ։ Սա թույլ տվեց Spiral Scout-ին ավելի հեռուն գնալ. RoadRunner-ը, ի տարբերություն PHP-FPM-ի, չի մաքրում հիշողությունը հարցումների միջև, ինչը լրացուցիչ արդյունավետություն է տալիս (չնայած այս մոտեցումը բարդացնում է զարգացման գործընթացը): Մենք այս պահին փորձարկում ենք այս գործիքը, բայց դեռևս որևէ արդյունք չունենք կիսվելու: Նրանց սպասելը ավելի զվարճալի դարձնելու համար, մենք հրապարակում ենք RoadRunner հայտարարության թարգմանությունը Spiral Scout-ից:

Հոդվածի մոտեցումը մեզ մոտ է. մեր խնդիրները լուծելիս մենք նաև ամենից հաճախ օգտագործում ենք մի շարք PHP և Go՝ ստանալով երկու լեզուների առավելությունները և չթողնելով մեկը մյուսի օգտին:

Enjoy!

Վերջին տասը տարիներին մենք ցուցակից հավելվածներ ենք ստեղծել ընկերությունների համար Բախտը 500, և 500-ից ոչ ավելի օգտվող լսարան ունեցող ձեռնարկությունների համար: Այս ամբողջ ընթացքում մեր ինժեներները մշակում էին backend-ը հիմնականում PHP-ում։ Բայց երկու տարի առաջ ինչ-որ բան մեծ ազդեցություն ունեցավ ոչ միայն մեր արտադրանքի կատարողականի, այլև դրանց մասշտաբայնության վրա. մենք մտցրեցինք Golang (Go) մեր տեխնոլոգիական փաթեթ:

Գրեթե անմիջապես մենք հայտնաբերեցինք, որ Go-ն մեզ թույլ է տվել ստեղծել ավելի մեծ հավելվածներ՝ մինչև 40 անգամ կատարողականի բարելավմամբ: Դրա միջոցով մենք կարողացանք ընդլայնել մեր առկա PHP արտադրանքները՝ բարելավելով դրանք՝ համատեղելով երկու լեզուների առավելությունները:

Մենք ձեզ կպատմենք, թե ինչպես է Go-ի և PHP-ի համադրությունն օգնում լուծել զարգացման իրական խնդիրները և ինչպես է այն մեզ համար վերածվել գործիքի, որը կարող է ազատվել որոշ խնդիրներից, որոնք կապված են դրա հետ։ PHP մեռնող մոդել.

Ձեր ամենօրյա PHP մշակման միջավայրը

Նախքան խոսենք այն մասին, թե ինչպես կարող եք օգտագործել Go-ը PHP մեռնող մոդելը վերակենդանացնելու համար, եկեք նայենք ձեր լռելյայն PHP մշակման միջավայրին:

Շատ դեպքերում, դուք գործարկում եք ձեր հավելվածը՝ օգտագործելով nginx վեբ սերվերի և PHP-FPM սերվերի համակցությունը: Առաջինը սպասարկում է ստատիկ ֆայլեր և վերահղում կոնկրետ հարցումները դեպի PHP-FPM, մինչդեռ PHP-FPM-ն ինքն է կատարում PHP կոդը: Հնարավոր է, որ դուք օգտագործում եք Apache-ի և mod_php-ի քիչ հայտնի համադրությունը: Բայց չնայած այն մի փոքր այլ կերպ է աշխատում, սկզբունքները նույնն են:

Եկեք նայենք, թե ինչպես է PHP-FPM-ն կատարում հավելվածի կոդը: Երբ հարցումը գալիս է, PHP-FPM-ն սկզբնավորում է PHP-ի երեխային գործընթացը և փոխանցում հարցման մանրամասները՝ որպես դրա վիճակի մաս (_GET, _POST, _SERVER և այլն):

Վիճակը չի կարող փոխվել PHP սկրիպտի կատարման ընթացքում, ուստի մուտքային տվյալների նոր հավաքածու ստանալու միակ միջոցը գործընթացի հիշողությունը մաքրելն է և այն նորից սկզբնավորել:

Կատարման այս մոդելը շատ առավելություններ ունի. Պետք չէ շատ անհանգստանալ հիշողության սպառման համար, բոլոր գործընթացները լիովին մեկուսացված են, և եթե դրանցից մեկը «մեռնի», այն ավտոմատ կերպով կվերստեղծվի, և դա չի ազդի մնացած գործընթացների վրա: Բայց այս մոտեցումն ունի նաև թերություններ, որոնք ի հայտ են գալիս հավելվածը մասշտաբավորելիս:

Կանոնավոր PHP միջավայրի թերություններն ու անարդյունավետությունները

Եթե ​​դուք պրոֆեսիոնալ PHP ծրագրավորող եք, ապա գիտեք, թե որտեղից սկսել նոր նախագիծ՝ շրջանակի ընտրությամբ: Այն բաղկացած է կախվածության ներարկման գրադարաններից, ORM-ներից, թարգմանություններից և ձևանմուշներից: Եվ, իհարկե, օգտագործողի բոլոր մուտքերը կարող են հարմար կերպով տեղադրվել մեկ օբյեկտի մեջ (Symfony/HttpFoundation կամ PSR-7): Շրջանակները հիանալի են:

Բայց ամեն ինչ իր գինն ունի։ Ձեռնարկության մակարդակի ցանկացած շրջանակում օգտատիրոջ պարզ հարցումը կամ տվյալների բազա մուտք գործելու համար դուք պետք է բեռնեք առնվազն տասնյակ ֆայլեր, ստեղծեք բազմաթիվ դասեր և վերլուծեք մի քանի կոնֆիգուրացիաներ: Բայց ամենավատն այն է, որ յուրաքանչյուր առաջադրանքն ավարտելուց հետո ձեզ հարկավոր է ամեն ինչ զրոյացնել և սկսել նորից. ձեր իսկ նախաձեռնած ամբողջ ծածկագիրը դառնում է անօգուտ, դրա օգնությամբ դուք այլևս այլ հարցում չեք մշակի: Ասեք սա ցանկացած ծրագրավորողի, ով գրում է որևէ այլ լեզվով, և դուք կտեսնեք տարակուսանք նրա դեմքին:

PHP-ի ինժեներները տարիներ շարունակ փնտրում են այս խնդիրը լուծելու ուղիներ՝ օգտագործելով խելացի ծույլ բեռնման տեխնիկա, միկրոշրջանակներ, օպտիմիզացված գրադարաններ, քեշ և այլն: (Թարգմանչի նշում. Գալով այս խնդիրը մասամբ կլուծվի Նախաբեռնել PHP 7.4-ում)

Կարո՞ղ է PHP-ն Go-ով գոյատևել մեկից ավելի հարցում:

Հնարավոր է գրել PHP սկրիպտներ, որոնք ապրում են ավելի երկար, քան մի քանի րոպե (մինչև ժամ կամ օր). օրինակ՝ cron առաջադրանքներ, CSV վերլուծիչներ, հերթերի անջատիչներ։ Նրանք բոլորն աշխատում են նույն սցենարով. նրանք վերբերում են առաջադրանքը, կատարում այն ​​և սպասում հաջորդին: Կոդը մշտապես մնում է հիշողության մեջ՝ խնայելով թանկարժեք միլիվայրկյանները, քանի որ շրջանակը և հավելվածը բեռնելու համար անհրաժեշտ են բազմաթիվ լրացուցիչ քայլեր:

Սակայն երկարակյաց սցենարներ մշակելը հեշտ չէ: Ցանկացած սխալ ամբողջությամբ սպանում է գործընթացը, հիշողության արտահոսքի ախտորոշումը զայրացնում է, և F5 վրիպազերծումն այլևս հնարավոր չէ:

Իրավիճակը բարելավվել է PHP 7-ի թողարկմամբ. հայտնվել է հուսալի աղբահան, սխալների հետ վարվելն ավելի հեշտ է դարձել, և միջուկի ընդլայնումները այժմ արտահոսք չեն: Ճիշտ է, ինժեներները դեռ պետք է զգույշ լինեն հիշողության հետ և տեղյակ լինեն կոդում առկա վիճակի մասին (կա՞ լեզու, որը կարող է անտեսել այս բաները): Այդուհանդերձ, PHP 7-ն ավելի քիչ անակնկալներ ունի մեզ համար:

Հնարավո՞ր է արդյոք վերցնել երկարակյաց PHP սկրիպտների հետ աշխատելու մոդելը, հարմարեցնել այն ավելի մանր խնդիրներին, ինչպիսիք են HTTP հարցումների մշակումը, և դրանով իսկ ազատվել ամեն հարցում ամեն ինչ զրոյից բեռնելու անհրաժեշտությունից:

Այս խնդիրը լուծելու համար մենք նախ պետք է ներդրեինք սերվերային հավելված, որը կարող էր ընդունել HTTP հարցումները և դրանք մեկ առ մեկ վերահղել դեպի PHP աշխատող՝ առանց ամեն անգամ սպանելու այն:

Մենք գիտեինք, որ կարող ենք վեբ սերվեր գրել մաքուր PHP-ով (PHP-PM) կամ օգտագործելով C ընդլայնում (Swoole): Եվ չնայած յուրաքանչյուր մեթոդ ունի իր արժանիքները, երկու տարբերակներն էլ մեզ չէին համապատասխանում. մենք ավելին էինք ուզում: Մեզ ավելին էր պետք, քան պարզապես վեբ սերվեր. մենք ակնկալում էինք, որ կստանանք լուծում, որը կփրկի մեզ PHP-ի «դժվար մեկնարկի» հետ կապված խնդիրներից, որը միևնույն ժամանակ հեշտությամբ կարող է հարմարեցվել և ընդլայնվել հատուկ հավելվածների համար: Այսինքն, մեզ անհրաժեշտ էր հավելվածի սերվեր:

Կարո՞ղ է Go-ն օգնել այս հարցում: Մենք գիտեինք, որ դա կարող է, քանի որ լեզուն դիմումները կազմում է առանձին երկուականների; այն խաչաձեւ հարթակ է; օգտագործում է իր սեփական, շատ էլեգանտ, զուգահեռ մշակման մոդելը (համաժամանակյա) և գրադարան HTTP-ի հետ աշխատելու համար. և վերջապես, մեզ հասանելի կլինեն հազարավոր բաց կոդով գրադարաններ և ինտեգրումներ:

Ծրագրավորման երկու լեզուների համադրման դժվարությունները

Առաջին հերթին անհրաժեշտ էր որոշել, թե ինչպես են երկու կամ ավելի հավելվածները շփվելու միմյանց հետ։

Օրինակ, օգտագործելով գերազանց գրադարան Alex Palaestras, հնարավոր էր հիշողությունը կիսել PHP և Go պրոցեսների միջև (նման է mod_php-ին Apache-ում): Բայց այս գրադարանն ունի առանձնահատկություններ, որոնք սահմանափակում են դրա օգտագործումը մեր խնդիրը լուծելու համար:

Մենք որոշեցինք օգտագործել այլ, ավելի տարածված մոտեցում՝ պրոցեսների միջև փոխազդեցություն կառուցել վարդակների/խողովակաշարերի միջոցով: Այս մոտեցումն ապացուցել է, որ հուսալի է վերջին տասնամյակների ընթացքում և լավ օպտիմիզացվել է օպերացիոն համակարգի մակարդակով:

Սկզբից մենք ստեղծեցինք պարզ երկուական արձանագրություն՝ գործընթացների միջև տվյալների փոխանակման և փոխանցման սխալները լուծելու համար: Իր ամենապարզ ձևով արձանագրության այս տեսակը նման է ցանցային պարան с ֆիքսված չափի փաթեթի վերնագիր (մեր դեպքում՝ 17 բայթ), որը պարունակում է տեղեկատվություն փաթեթի տեսակի, դրա չափի և երկուական դիմակ՝ տվյալների ամբողջականությունը ստուգելու համար։

PHP-ի կողմից մենք օգտագործեցինք փաթեթի գործառույթ, իսկ Go-ի կողմում՝ գրադարանը կոդավորում / երկուական.

Մեզ թվաց, որ մեկ արձանագրությունը բավարար չէ, և մենք ավելացրինք զանգելու հնարավորությունը net/rpc go ծառայություններ անմիջապես PHP-ից. Հետագայում սա մեզ շատ օգնեց զարգացման գործում, քանի որ մենք կարողացանք հեշտությամբ ինտեգրել Go գրադարանները PHP հավելվածներում: Այս աշխատանքի արդյունքը կարելի է տեսնել, օրինակ, մեր մյուս բաց կոդով արտադրանքում Գորիջ.

Առաջադրանքների բաշխում բազմաթիվ PHP աշխատողների վրա

Փոխազդեցության մեխանիզմի ներդրումից հետո մենք սկսեցինք մտածել առաջադրանքները PHP գործընթացներին փոխանցելու ամենաարդյունավետ միջոցի մասին։ Երբ առաջադրանքը գալիս է, հավելվածի սերվերը պետք է ընտրի անվճար աշխատող՝ այն կատարելու համար: Եթե ​​աշխատողը/գործընթացը դուրս է գալիս սխալով կամ «մեռնում է», մենք ազատվում ենք դրանից և ստեղծում նորը, որը փոխարինելու է նրան: Եվ եթե աշխատողը/գործընթացը հաջողությամբ ավարտվել է, մենք այն վերադարձնում ենք առաջադրանքները կատարելու համար հասանելի աշխատողների խումբ:

RoadRunner. PHP-ն ստեղծված չէ մեռնելու, կամ Գոլանգը փրկելու համար

Ակտիվ աշխատողների լողավազանը պահելու համար մենք օգտագործել ենք բուֆերացված ալիք, անսպասելի «մահացած» աշխատողներին լողավազանից հեռացնելու համար մենք ավելացրել ենք աշխատողների սխալներին և վիճակներին հետևելու մեխանիզմ:

Արդյունքում մենք ստացանք աշխատող PHP սերվեր, որը կարող է մշակել երկուական ձևով ներկայացված ցանկացած հարցում:

Որպեսզի մեր հավելվածը սկսեր աշխատել որպես վեբ սերվեր, մենք պետք է ընտրեինք հուսալի PHP ստանդարտ՝ ներկայացնելու ցանկացած մուտքային HTTP հարցում: Մեր դեպքում մենք պարզապես փոխակերպել net/http հարցումը Go to format-ից PSR-7այնպես, որ այն համատեղելի է այսօր հասանելի PHP շրջանակների մեծ մասի հետ:

Քանի որ PSR-7-ը համարվում է անփոփոխ (ոմանք կարող են ասել, որ տեխնիկապես դա այդպես չէ), մշակողները պետք է գրեն հավելվածներ, որոնք սկզբունքորեն չեն վերաբերվում հարցումին որպես գլոբալ միավորի: Սա հիանալի կերպով համապատասխանում է երկարատև PHP գործընթացների հայեցակարգին: Մեր վերջնական իրականացումը, որը դեռ պետք է անվանվի, այսպիսի տեսք ուներ.

RoadRunner. PHP-ն ստեղծված չէ մեռնելու, կամ Գոլանգը փրկելու համար

Ներկայացնում ենք RoadRunner-ը. բարձր արդյունավետությամբ PHP հավելվածի սերվեր

Մեր առաջին փորձնական առաջադրանքը API backend-ն էր, որը պարբերաբար պայթում է անկանխատեսելիորեն (սովորականից շատ ավելի հաճախ): Թեև nginx-ը շատ դեպքերում բավարար էր, մենք պարբերաբար բախվում էինք 502 սխալի, քանի որ մենք չկարողացանք համակարգը արագ հավասարակշռել բեռի ակնկալվող աճի համար:

Այս լուծումը փոխարինելու համար մենք գործարկեցինք մեր առաջին PHP/Go հավելվածի սերվերը 2018 թվականի սկզբին: Եվ անմիջապես ստացավ անհավանական էֆեկտ: Մենք ոչ միայն ամբողջությամբ ազատվեցինք 502 սխալից, այլև կարողացանք երկու երրորդով կրճատել սերվերների քանակը՝ խնայելով մեծ գումարներ և գլխացավի հաբեր ինժեներների և արտադրանքի մենեջերների համար:

Տարվա կեսերին մենք բարելավեցինք մեր լուծումը, այն հրապարակեցինք GitHub-ում MIT լիցենզիայի ներքո և անվանեցինք այն։ RoadRunner, դրանով իսկ ընդգծելով դրա անհավանական արագությունն ու արդյունավետությունը։

Ինչպես կարող է RoadRunner-ը բարելավել ձեր զարգացման փաթեթը

Դիմում RoadRunner թույլ է տվել մեզ օգտագործել Middleware net/http Go-ի կողմում՝ JWT ստուգում իրականացնելու համար, նախքան հարցումը կհասնի PHP-ին, ինչպես նաև կարգավորել WebSockets-ը և ագրեգատ վիճակը գլոբալ Prometheus-ում:

Ներկառուցված RPC-ի շնորհիվ դուք կարող եք բացել ցանկացած Go գրադարանի API PHP-ի համար՝ առանց ընդլայնման փաթաթաներ գրելու: Ավելի կարևոր է, որ RoadRunner-ի միջոցով դուք կարող եք տեղադրել նոր ոչ HTTP սերվերներ: Օրինակները ներառում են PHP-ում գործարկվող մշակիչներ AWS Lambda, ստեղծելով հուսալի հերթերի անջատիչներ և նույնիսկ ավելացնելով gRPC մեր դիմումներին:

PHP և Go համայնքների օգնությամբ մենք բարելավեցինք լուծման կայունությունը, որոշ թեստերում ավելացրինք հավելվածի կատարումը մինչև 40 անգամ, բարելավեցինք վրիպազերծման գործիքները, իրականացրեցինք ինտեգրում Symfony շրջանակի հետ և ավելացրեցինք աջակցություն HTTPS, HTTP/2, պլագիններ և PSR-17:

Ամփոփում

Որոշ մարդիկ դեռևս ընկած են PHP-ի հնացած հասկացության մեջ՝ որպես դանդաղ, անգործունակ լեզվի, որը միայն լավ է WordPress-ի համար պլագիններ գրելու համար: Այս մարդիկ նույնիսկ կարող են ասել, որ PHP-ն ունի այդպիսի սահմանափակում. երբ հավելվածը բավականաչափ մեծանում է, դուք պետք է ընտրեք ավելի «հասուն» լեզու և վերաշարադրեք երկար տարիների ընթացքում կուտակված կոդերի բազան։

Այս ամենին ուզում եմ պատասխանել՝ նորից մտածիր։ Մենք հավատում ենք, որ PHP-ի համար սահմանափակումներ միայն դուք եք դնում: Դուք կարող եք անցկացնել ձեր ամբողջ կյանքը՝ անցնելով մի լեզվից մյուսին, փորձելով գտնել ձեր կարիքների համար կատարյալ համընկնում, կամ կարող եք սկսել լեզուները որպես գործիքներ մտածել: PHP-ի նման լեզվի ենթադրյալ թերությունները կարող են իրականում լինել նրա հաջողության պատճառը: Եվ եթե այն համադրեք մեկ այլ լեզվի հետ, ինչպիսին Go-ն է, ապա դուք կստեղծեք շատ ավելի հզոր արտադրանք, քան եթե սահմանափակվեիք որևէ մեկ լեզվով օգտագործելով:

Աշխատելով Go-ի և PHP-ի մի շարք հետ, մենք կարող ենք ասել, որ մենք սիրում ենք դրանք: Մենք չենք նախատեսում զոհաբերել մեկը մյուսի համար, ընդհակառակը, մենք ուղիներ ենք փնտրելու այս երկակի կույտից էլ ավելի մեծ արժեք ստանալու համար:

UPD: Մենք ողջունում ենք RoadRunner-ի ստեղծողին և բնօրինակ հոդվածի համահեղինակին. Լաչեսիս

Source: www.habr.com

Добавить комментарий