RoadRunner: PHP өлу үшін немесе Голанг құтқару үшін жасалмаған

RoadRunner: PHP өлу үшін немесе Голанг құтқару үшін жасалмаған

Эй Хабр! Біз Badoo-да белсендіміз PHP өнімділігімен жұмыс істеу, өйткені бізде бұл тілде жеткілікті үлкен жүйе бар және өнімділік мәселесі ақшаны үнемдеу мәселесі болып табылады. Он жылдан астам бұрын біз бұл үшін PHP-FPM құрдық, ол бастапқыда PHP үшін патчтар жиынтығы болды, кейін ресми дистрибутивке енді.

Соңғы жылдары РНР үлкен прогреске қол жеткізді: қоқыс жинағыш жақсарды, тұрақтылық деңгейі артты – бүгінде РНР тілінде демондар мен ұзақ өмір сүретін сценарийлерді еш қиындықсыз жазуға болады. Бұл Spiral Scout-қа әрі қарай жүруге мүмкіндік берді: RoadRunner, PHP-FPM-ге қарағанда, сұраулар арасында жадты тазартпайды, бұл қосымша өнімділікті арттырады (бірақ бұл тәсіл әзірлеу процесін қиындатады). Біз қазір бұл құралмен тәжірибе жасап жатырмыз, бірақ бөлісетін нәтижелер әлі жоқ. Оларды күтуді қызықты ету үшін, біз Spiral Scout-тен RoadRunner хабарландыруының аудармасын жариялаймыз.

Мақаладағы тәсіл бізге жақын: өз проблемаларымызды шешкен кезде біз екі тілдің де артықшылықтарын алып, біреуін екіншісінің пайдасына қалдырмай, көптеген PHP және Go бағдарламаларын қолданамыз.

Тамашалаңыз!

Соңғы он жылда біз тізімдегі компанияларға қосымшалар жасадық Fortune 500, және аудиториясы 500 пайдаланушыдан аспайтын бизнес үшін. Осы уақыт ішінде біздің инженерлер негізінен PHP-де бэкендті әзірледі. Бірақ екі жыл бұрын бір нәрсе біздің өнімдеріміздің өнімділігіне ғана емес, сонымен бірге олардың ауқымдылығына да үлкен әсер етті - біз Golang (Go) технология стекке енгіздік.

Бірден дерлік Go бізге өнімділікті 40 есе жақсартатын үлкенірек қосымшаларды құруға мүмкіндік беретінін білдік. Оның көмегімен біз екі тілдің артықшылықтарын біріктіру арқылы оларды жетілдіре отырып, бар PHP өнімдерімізді кеңейте алдық.

Біз сізге Go және PHP комбинациясы нақты даму мәселелерін шешуге қалай көмектесетінін және оның біз үшін кейбір мәселелерден арылуға болатын құралға айналғанын айтып береміз. PHP өліп жатқан модель.

Сіздің күнделікті PHP әзірлеу ортаңыз

PHP өліп жатқан үлгісін қалпына келтіру үшін Go қолданбасын қалай пайдалануға болатыны туралы айтпас бұрын, әдепкі PHP әзірлеу ортаңызды қарастырайық.

Көп жағдайда қолданбаны nginx веб-сервері мен PHP-FPM серверінің тіркесімін пайдаланып іске қосасыз. Біріншісі статикалық файлдарға қызмет етеді және арнайы сұрауларды PHP-FPM-ге қайта бағыттайды, ал PHP-FPM өзі PHP кодын орындайды. Сіз Apache және mod_php азырақ танымал комбинациясын пайдаланып жатқан боларсыз. Бірақ ол сәл басқаша жұмыс істегенімен, принциптері бірдей.

PHP-FPM қолданбалы кодты қалай орындайтынын қарастырайық. Сұраныс түскен кезде PHP-FPM PHP еншілес процесін инициализациялайды және оның күйінің бөлігі ретінде сұраудың мәліметтерін береді (_GET, _POST, _SERVER және т.б.).

РНР сценарийін орындау кезінде күй өзгермейді, сондықтан кіріс деректерінің жаңа жинағын алудың жалғыз жолы - процесс жадын тазалау және оны қайтадан инициализациялау.

Бұл орындау моделінің көптеген артықшылықтары бар. Жадты тұтыну туралы көп алаңдамаудың қажеті жоқ, барлық процестер толығымен оқшауланған, ал егер олардың біреуі «өлсе» ол автоматты түрде қайта жасалады және қалған процестерге әсер етпейді. Бірақ бұл тәсілдің қолданбаны масштабтауға тырысқанда пайда болатын кемшіліктері де бар.

Тұрақты РНР ортасының кемшіліктері мен тиімсіздігі

Егер сіз кәсіби РНР әзірлеушісі болсаңыз, онда сіз жаңа жобаны неден бастау керектігін білесіз - құрылымды таңдау арқылы. Ол тәуелділік енгізу кітапханаларынан, ORM, аудармалар мен үлгілерден тұрады. Және, әрине, пайдаланушының барлық енгізуін ыңғайлы түрде бір нысанға қоюға болады (Symfony/HttpFoundation немесе PSR-7). Фреймворктар керемет!

Бірақ әр нәрсенің өз бағасы бар. Кез келген кәсіпорын деңгейіндегі құрылымда қарапайым пайдаланушы сұрауын немесе дерекқорға кіруді өңдеу үшін сізге кемінде ондаған файлдарды жүктеуге, көптеген сыныптарды жасауға және бірнеше конфигурацияларды талдауға тура келеді. Бірақ ең сорақысы, әрбір тапсырманы орындағаннан кейін бәрін қалпына келтіріп, қайтадан бастау керек болады: сіз жаңадан бастаған кодтың бәрі пайдасыз болады, оның көмегімен сіз енді басқа сұрауды өңдемейсіз. Мұны басқа тілде жазатын кез келген бағдарламашыға айтыңыз, сонда сіз оның жүзінен таңданысты көресіз.

PHP инженерлері ақылды жалқау жүктеу әдістерін, микрофремворктерді, оңтайландырылған кітапханаларды, кэшті және т.б. пайдалана отырып, бұл мәселені шешудің жолдарын жылдар бойы іздеді. Бірақ соңында сіз әлі де бүкіл қолданбаны қалпына келтіріп, қайта-қайта бастауыңыз керек. (Аудармашының ескертуі: бұл мәселе жартылай шешілетін болады шегімен PHP 7.4)

Go көмегімен PHP бірнеше сұрауларға төтеп бере ала ма?

Бірнеше минуттан ұзақ (сағат немесе тәулікке дейін) өмір сүретін PHP сценарийлерін жазуға болады: мысалы, cron тапсырмалары, CSV талдаушылары, кезек ажыратқыштар. Олардың барлығы бірдей сценарий бойынша жұмыс істейді: олар тапсырманы шығарып, оны орындайды және келесісін күтеді. Код барлық уақытта жадта болады, ол бағалы миллисекундтарды үнемдейді, өйткені жақтау мен қолданбаны жүктеу үшін көптеген қосымша қадамдар қажет.

Бірақ ұзақ өмір сүретін сценарийлерді әзірлеу оңай емес. Кез келген қате процесті толығымен жояды, жадтың ағып кетуін диагностикалау ашуландырады және F5 отладкасы енді мүмкін емес.

PHP 7 шығарылымымен жағдай жақсарды: сенімді қоқыс жинағыш пайда болды, қателерді өңдеу оңайырақ болды және ядро ​​кеңейтімдері енді ағып кетпейді. Рас, инженерлер әлі де жадқа мұқият болу керек және кодтағы күй мәселелерін білуі керек (осы нәрселерді елемейтін тіл бар ма?). Дегенмен, PHP 7-де біз үшін күтпеген тосынсыйлар аз.

Ұзақ өмір сүретін PHP сценарийлерімен жұмыс істеу үлгісін алуға, оны HTTP сұрауларын өңдеу сияқты тривиальды тапсырмаларға бейімдеуге және сол арқылы әрбір сұраумен барлығын нөлден жүктеу қажеттілігінен арылуға болады ма?

Бұл мәселені шешу үшін бізге алдымен HTTP сұрауларын қабылдай алатын және оларды әр уақытта өлтірмей PHP жұмысшысына бір-бірден қайта бағыттай алатын сервер қосымшасын енгізу қажет болды.

Біз веб-серверді таза PHP (PHP-PM) тілінде немесе C кеңейтімін (Swoole) пайдалана алатынымызды білдік. Әр әдістің өзіндік артықшылықтары болса да, екі нұсқа да бізге сәйкес келмеді - біз одан да көп нәрсені қаладық. Бізге жай ғана веб-сервер ғана емес, бізге PHP-дегі «қиын бастауға» байланысты мәселелерден құтқаратын шешімді алуды күткен едік, оны бір уақытта нақты қолданбалар үшін оңай бейімдеуге және кеңейтуге болады. Яғни бізге қолданбалы сервер қажет болды.

Go бұған көмектесе ала ма? Біз бұл мүмкін екенін білдік, өйткені тіл қосымшаларды бір екілік файлдарға құрастырады; бұл кросс-платформа; HTTP-мен жұмыс істеуге арналған өзіндік, өте талғампаз, параллельді өңдеу моделін (конкурстық) және кітапхананы пайдаланады; ақырында, мыңдаған ашық бастапқы кітапханалар мен интеграциялар бізге қолжетімді болады.

Екі бағдарламалау тілін біріктірудің қиындықтары

Ең алдымен, екі немесе одан да көп қолданбалардың бір-бірімен қалай байланысатынын анықтау қажет болды.

Мысалы, пайдалану тамаша кітапхана Алекс Палаэстрас, PHP және Go процестері арасында жадты бөлісу мүмкін болды (Apache-дегі mod_php-ге ұқсас). Бірақ бұл кітапханада біздің мәселемізді шешу үшін пайдалануды шектейтін мүмкіндіктер бар.

Біз басқа, кең таралған тәсілді қолдануды шештік: розеткалар / құбырлар арқылы процестер арасындағы өзара әрекеттесу. Бұл тәсіл соңғы онжылдықтарда сенімді болып шықты және операциялық жүйе деңгейінде жақсы оңтайландырылған.

Бастау үшін біз процестер арасында деректер алмасуға және жіберу қателерін өңдеуге арналған қарапайым екілік протоколды жасадық. Қарапайым түрде бұл хаттама түрі ұқсас желі жолы с бекітілген өлшемді пакет тақырыбы (біздің жағдайда 17 байт), онда пакеттің түрі, оның өлшемі және деректердің тұтастығын тексеруге арналған екілік маска туралы ақпарат бар.

PHP жағында біз қолдандық бума функциясы, ал жүріс жағында кітапхана кодтау/екілік.

Бізге бір хаттама жеткіліксіз болып көрінді - біз қоңырау шалу мүмкіндігін қостық net/rpc go қызметтерін тікелей PHP-ден алады. Кейінірек бұл бізге дамуда көп көмектесті, өйткені біз Go кітапханаларын PHP қолданбаларына оңай біріктіре алдық. Бұл жұмыстың нәтижесін, мысалы, басқа ашық бастапқы өнімімізден көруге болады Горидж.

Бірнеше PHP жұмысшылары арасында тапсырмаларды тарату

Өзара әрекеттесу механизмін жүзеге асырғаннан кейін біз PHP процестеріне тапсырмаларды берудің ең тиімді әдісі туралы ойлана бастадық. Тапсырма келгенде, қолданба сервері оны орындау үшін бос жұмысшыны таңдауы керек. Егер жұмысшы/процесс қатемен шықса немесе «өліп қалса», біз одан құтыламыз және оның орнына жаңасын жасаймыз. Егер жұмысшы/процесс сәтті аяқталса, біз оны тапсырмаларды орындау үшін қолжетімді жұмысшылар пулына қайтарамыз.

RoadRunner: PHP өлу үшін немесе Голанг құтқару үшін жасалмаған

Белсенді жұмысшылардың пулын сақтау үшін біз пайдаландық буферленген арна, күтпеген жерден «өлген» жұмысшыларды бассейннен алып тастау үшін біз қателер мен жұмысшылардың күйін бақылау механизмін қостық.

Нәтижесінде біз екілік пішінде ұсынылған кез келген сұрауларды өңдеуге қабілетті жұмыс істейтін PHP серверін алдық.

Қолданбамыз веб-сервер ретінде жұмыс істей бастау үшін бізге кез келген кіріс HTTP сұрауларын көрсететін сенімді PHP стандартын таңдау керек болды. Біздің жағдайда, біз жай ғана түрлендіру net/http сұрауынан пішімге өту PSR-7ол бүгінгі таңда қол жетімді PHP фреймворктерінің көпшілігімен үйлесімді болуы үшін.

PSR-7 өзгермейтін болып саналатындықтан (кейбіреулер техникалық тұрғыдан олай емес дейді), әзірлеушілер сұрауды негізінен ғаламдық нысан ретінде қарастырмайтын қолданбаларды жазуы керек. Бұл ұзақ өмір сүретін PHP процестерінің тұжырымдамасына жақсы сәйкес келеді. Біздің түпкілікті іске асыруымыз әлі аталмаған:

RoadRunner: PHP өлу үшін немесе Голанг құтқару үшін жасалмаған

RoadRunner-мен таныстыру - жоғары өнімді РНР қолданбалы сервері

Біздің бірінші сынақ тапсырмамыз мезгіл-мезгіл болжау мүмкін емес сұрауларға (әдеттегіден әлдеқайда жиі) түсетін API сервері болды. Көп жағдайда nginx жеткілікті болғанымен, біз жүйелі түрде 502 қатеге тап болдық, өйткені біз жүктеменің күтілетін өсуі үшін жүйені тез теңестіре алмадық.

Бұл шешімді ауыстыру үшін біз 2018 жылдың басында бірінші PHP/Go қолданбалы серверін орналастырдық. Және бірден керемет әсер алды! Біз 502 қатеден толығымен құтылып қана қоймай, инженерлер мен өнім менеджерлері үшін көп ақша мен бас ауруына қарсы таблеткаларды үнемдей отырып, серверлер санын үштен екіге азайта алдық.

Жылдың ортасына қарай біз шешімімізді жетілдірдік, оны MIT лицензиясы бойынша GitHub сайтында жариялап, оны атадық. жолшы, осылайша оның керемет жылдамдығы мен тиімділігін баса көрсетеді.

RoadRunner әзірлеу стекіңізді қалай жақсарта алады

Қолданба жолшы сұрау PHP-ге жеткенге дейін JWT тексеруін орындау үшін, сонымен қатар Prometheus жүйесінде ғаламдық деңгейде WebSockets және агрегаттық күйді өңдеу үшін Go жағындағы Middleware net/http пайдалануға мүмкіндік берді.

Кірістірілген RPC арқасында сіз кеңейтім орамдарын жазбай-ақ PHP үшін кез келген Go кітапханаларының API интерфейсін аша аласыз. Ең бастысы, RoadRunner көмегімен HTTP емес жаңа серверлерді орналастыруға болады. Мысалдарға PHP-де жұмыс істейтін өңдеушілер жатады AWS Lambda, сенімді кезек ажыратқыштарды жасау және тіпті қосу gRPC біздің қосымшаларымызға.

PHP және Go қауымдастықтарының көмегімен біз шешімнің тұрақтылығын жақсарттық, кейбір сынақтарда қолданба өнімділігін 40 есеге дейін арттырдық, жөндеу құралдарын жақсарттық, Symfony негізімен интеграцияны енгіздік және HTTPS, HTTP/2, плагиндер және PSR-17.

қорытынды

Кейбір адамдар әлі де WordPress плагиндерін жазу үшін ғана қолайлы баяу, қолайсыз тіл ретінде PHP-нің ескірген түсінігінде қалады. Бұл адамдар тіпті PHP-де мұндай шектеулер бар деп айтуы мүмкін: қолданба жеткілікті үлкен болған кезде, сізге «жетілген» тілді таңдауға және көптеген жылдар бойы жинақталған код базасын қайта жазуға тура келеді.

Осының бәріне мен жауап бергім келеді: қайтадан ойланыңыз. Біз PHP үшін кез келген шектеулерді тек сіз орнатасыз деп есептейміз. Сіз бүкіл өміріңізді бір тілден екінші тілге көшумен, қажеттіліктеріңізге тамаша сәйкестік табуға тырысумен өткізе аласыз немесе тілдерді құрал ретінде қарастыра бастай аласыз. РНР сияқты тілдің болжамды кемшіліктері оның табысының себебі болуы мүмкін. Егер сіз оны Go сияқты басқа тілмен біріктірсеңіз, кез келген тілді пайдаланумен шектелгеннен гөрі әлдеқайда күшті өнімдер жасайсыз.

Go және PHP топтамасымен жұмыс істегендіктен, біз оларды жақсы көреміз деп айта аламыз. Біз бірін екіншісі үшін құрбан етуді жоспарламаймыз - керісінше, біз осы қос стектен одан да көп құндылық алудың жолдарын іздейтін боламыз.

UPD: біз RoadRunner жасаушысын және түпнұсқа мақаланың бірлескен авторын қарсы аламыз - Лахесис

Ақпарат көзі: www.habr.com

пікір қалдыру