RoadRunner: PHP nav izveidots, lai mirtu, vai Golang, lai glÄbtu
Sveiks, Habr! MÄs esam aktÄ«vi Badoo strÄdÄ pie PHP veiktspÄjas, jo mums ir diezgan liela sistÄma Å”ajÄ valodÄ un veiktspÄjas jautÄjums ir naudas taupÄ«Å”anas jautÄjums. Pirms vairÄk nekÄ desmit gadiem mÄs Å”im nolÅ«kam izveidojÄm PHP-FPM, kas sÄkotnÄji bija PHP ielÄpu komplekts, bet vÄlÄk kļuva par daļu no oficiÄlÄs izplatÄ«Å”anas.
PÄdÄjos gados PHP ir guvis lielu progresu: ir uzlabojies atkritumu savÄcÄjs, palielinÄjies stabilitÄtes lÄ«menis - Å”odien PHP var bez problÄmÄm rakstÄ«t dÄmonus un ilgmūžīgus skriptus. Tas ļÄva Spiral Scout iet tÄlÄk: RoadRunner atŔķirÄ«bÄ no PHP-FPM neiztÄ«ra atmiÅu starp pieprasÄ«jumiem, kas nodroÅ”ina papildu veiktspÄjas priekÅ”rocÄ«bas (lai gan Ŕī pieeja sarežģī izstrÄdes procesu). PaÅ”laik mÄs eksperimentÄjam ar Å”o rÄ«ku, taÄu mums vÄl nav rezultÄtu, ko kopÄ«got. Lai bÅ«tu jautrÄk viÅus gaidÄ«t, MÄs publicÄjam RoadRunner paziÅojuma tulkojumu no Spiral Scout.
Raksta pieeja mums ir tuva: risinot savas problÄmas, mÄs arÄ« visbiežÄk izmantojam PHP un Go kombinÄciju, iegÅ«stot abu valodu priekÅ”rocÄ«bas un neatsakoties no vienas par labu otrai.
Enjoy!
PÄdÄjo desmit gadu laikÄ esam izveidojuÅ”i aplikÄcijas uzÅÄmumiem no saraksta Laime 500, un uzÅÄmumiem, kuru auditorija nepÄrsniedz 500 lietotÄju. Visu Å”o laiku mÅ«su inženieri aizmugursistÄmu izstrÄdÄja galvenokÄrt PHP. TaÄu pirms diviem gadiem kaut kas ļoti ietekmÄja ne tikai mÅ«su produktu veiktspÄju, bet arÄ« to mÄrogojamÄ«bu ā mÄs ieviesÄm Golang (Go) savÄ tehnoloÄ£iju komplektÄ.
GandrÄ«z uzreiz mÄs atklÄjÄm, ka Go ļÄva mums izveidot lielÄkas lietojumprogrammas ar lÄ«dz pat 40 reizÄm ÄtrÄku veiktspÄju. Ar to mÄs varÄjÄm paplaÅ”inÄt esoÅ”os produktus, kas rakstÄ«ti PHP, uzlabojot tos, apvienojot abu valodu priekÅ”rocÄ«bas.
MÄs jums pastÄstÄ«sim, kÄ Go un PHP kombinÄcija palÄ«dz atrisinÄt reÄlas izstrÄdes problÄmas un kÄ tÄ ir kļuvusi par mÅ«su rÄ«ku, kas var novÄrst dažas problÄmas, kas saistÄ«tas ar PHP mirstoÅ”s modelis.
JÅ«su ikdienas PHP izstrÄdes vide
Pirms runÄjam par to, kÄ jÅ«s varat izmantot Go, lai atdzÄ«vinÄtu PHP mirstoÅ”o modeli, apskatÄ«sim jÅ«su standarta PHP izstrÄdes vidi.
VairumÄ gadÄ«jumu lietojumprogramma tiek palaista, izmantojot nginx tÄ«mekļa servera un PHP-FPM servera kombinÄciju. Pirmais apkalpo statiskos failus un novirza konkrÄtus pieprasÄ«jumus uz PHP-FPM, un pats PHP-FPM izpilda PHP kodu. VarbÅ«t jÅ«s izmantojat mazÄk populÄru kombinÄciju no Apache un mod_php. Bet, lai gan tas darbojas nedaudz savÄdÄk, principi ir vienÄdi.
ApskatÄ«sim, kÄ PHP-FPM izpilda lietojumprogrammas kodu. Kad tiek saÅemts pieprasÄ«jums, PHP-FPM inicializÄ pakÄrtoto PHP procesu un nodod pieprasÄ«juma informÄciju kÄ daļu no tÄ stÄvokļa (_GET, _POST, _SERVER utt.).
PHP skripta izpildes laikÄ stÄvoklis nevar mainÄ«ties, tÄpÄc ir tikai viens veids, kÄ iegÅ«t jaunu ievaddatu kopu: notÄ«rot procesa atmiÅu un atkÄrtoti inicializÄjot to.
Å im izpildes modelim ir daudz priekÅ”rocÄ«bu. Jums nav Ä«paÅ”i jÄuztraucas par atmiÅas patÄriÅu, visi procesi ir pilnÄ«bÄ izolÄti, un, ja kÄds no tiem nomirst, tas tiks automÄtiski izveidots no jauna, neietekmÄjot pÄrÄjos procesus. TaÄu Å”ai pieejai ir arÄ« trÅ«kumi, kas parÄdÄs, mÄÄ£inot mÄrogot lietojumprogrammu.
ParastÄs PHP vides trÅ«kumi un neefektivitÄte
Ja nodarbojies ar profesionÄlo izaugsmi PHP, tad zini, kur uzsÄkt jaunu projektu ā izvÄloties ietvaru. Tas sastÄv no bibliotÄkÄm atkarÄ«bas ievadÄ«Å”anai, ORM, tulkojumiem un veidnÄm. Un, protams, visu lietotÄja ievadÄ«to informÄciju var Ärti ievietot vienÄ objektÄ (Symfony/HttpFoundation vai PSR-7). Frameworks ir forÅ”s!
Bet visam ir sava cena. JebkurÄ uzÅÄmuma lÄ«meÅa sistÄmÄ, lai apstrÄdÄtu vienkÄrÅ”u lietotÄja pieprasÄ«jumu vai piekļūtu datubÄzei, jums bÅ«s jÄielÄdÄ vismaz desmitiem failu, jÄizveido daudzas klases un parsÄt vairÄkas konfigurÄcijas. Bet sliktÄkais ir tas, ka pÄc katra uzdevuma pabeigÅ”anas jums viss bÅ«s jÄatiestata un jÄsÄk no jauna: viss tikko uzsÄktais kods kļūst bezjÄdzÄ«gs, ar tÄ palÄ«dzÄ«bu jÅ«s vairs neapstrÄdÄsit citu pieprasÄ«jumu. PastÄstiet to jebkuram programmÄtÄjam, kurÅ” raksta jebkurÄ citÄ valodÄ, un jÅ«s redzÄsit apjukumu viÅa sejÄ.
PHP inženieri ir pavadÄ«juÅ”i vairÄkus gadus, meklÄjot veidus, kÄ atrisinÄt Å”o problÄmu, izmantojot gudras slinkas ielÄdes metodes, mikroietvarus, optimizÄtas bibliotÄkas, keÅ”atmiÅas utt. Bet galu galÄ jums joprojÄm ir jÄatiestata visa lietojumprogramma un jÄsÄk no jauna, atkal un atkal. (TulkotÄja piezÄ«me: Ŕī problÄma tiks daļÄji atrisinÄta, parÄdoties pirmsielÄdÄÅ”ana PHP 7.4)
Vai PHP ar Go var izturÄt vairÄk nekÄ vienu pieprasÄ«jumu?
Ir iespÄjams rakstÄ«t PHP skriptus, kas kalpos ilgÄk par dažÄm minÅ«tÄm (lÄ«dz stundÄm vai dienÄm): piemÄram, cron uzdevumus, CSV parsÄtÄjus, rindu kÄrtotÄjus. ViÅi visi strÄdÄ saskaÅÄ ar vienu un to paÅ”u scenÄriju: viÅi izgÅ«st uzdevumu, izpilda to un gaida nÄkamo. Kods atrodas atmiÅÄ, ietaupot vÄrtÄ«gas milisekundes, jo ir nepiecieÅ”amas daudzas papildu darbÄ«bas, lai ielÄdÄtu sistÄmu un lietojumprogrammu.
TaÄu izstrÄdÄt ilgstoÅ”us skriptus nav tik vienkÄrÅ”i. Jebkura kļūda pilnÄ«bÄ nogalina procesu, atmiÅas noplÅ«des diagnosticÄÅ”ana padara jÅ«s traku, un jÅ«s vairs nevarat izmantot F5 atkļūdoÅ”anu.
SituÄcija ir uzlabojusies lÄ«dz ar PHP 7 izlaiÅ”anu: ir parÄdÄ«jies uzticams atkritumu savÄcÄjs, kļuvis vieglÄk rÄ«koties ar kļūdÄm, un kodola paplaÅ”inÄjumi tagad ir aizsargÄti pret noplÅ«dÄm. Tiesa, inženieriem joprojÄm ir jÄbÅ«t uzmanÄ«giem ar atmiÅu un jÄapzinÄs stÄvokļa problÄmas kodÄ (vai ir valoda, kurÄ mums par Ŕīm lietÄm nav jÄuztraucas?). Un tomÄr PHP 7 mÅ«s sagaida mazÄk pÄrsteigumu.
Vai ir iespÄjams izmantot modeli darbam ar ilgstoÅ”iem PHP skriptiem, pielÄgot to tÄdiem triviÄliem uzdevumiem kÄ HTTP pieprasÄ«jumu apstrÄde un tÄdÄjÄdi novÄrst nepiecieÅ”amÄ«bu ielÄdÄt visu no nulles katram pieprasÄ«jumam?
Lai atrisinÄtu Å”o problÄmu, mums vispirms bija jÄievieÅ” servera lietojumprogramma, kas varÄtu pieÅemt HTTP pieprasÄ«jumus un pÄrsÅ«tÄ«t tos pa vienam PHP darbiniekam, to katru reizi neiznÄ«cinot.
MÄs zinÄjÄm, ka mÄs varam rakstÄ«t tÄ«mekļa serveri tÄ«rÄ PHP (PHP-PM) vai izmantojot C paplaÅ”inÄjumu (Swoole). Un, lai gan katrai metodei ir savas priekÅ”rocÄ«bas, abas iespÄjas mums nebija piemÄrotas - mÄs gribÄjÄm kaut ko vairÄk. Mums bija vajadzÄ«gs vairÄk nekÄ tikai tÄ«mekļa serveris - mÄs cerÄjÄm iegÅ«t risinÄjumu, kas varÄtu mÅ«s glÄbt no problÄmÄm, kas saistÄ«tas ar PHP āgrÅ«to sÄkumuā, ko tajÄ paÅ”Ä laikÄ var viegli pielÄgot un paplaÅ”inÄt konkrÄtÄm lietojumprogrammÄm. Tas ir, mums bija nepiecieÅ”ams lietojumprogrammu serveris.
Vai Go var palÄ«dzÄt Å”ajÄ jautÄjumÄ? MÄs zinÄjÄm, ka tas var, jo valoda apkopo lietojumprogrammas atseviŔķos binÄros failos; tÄ ir starpplatforma; izmanto savu, ļoti eleganto, paralÄlÄs apstrÄdes modeli (vienlaicÄ«gumu) un bibliotÄku darbam ar HTTP; un visbeidzot, mums bÅ«s pieejamas tÅ«kstoÅ”iem atvÄrtÄ pirmkoda bibliotÄku un integrÄciju.
Divu programmÄÅ”anas valodu apvienoÅ”anas grÅ«tÄ«bas
Pirmais solis bija noteikt, kÄ divas vai vairÄkas lietojumprogrammas sazinÄsies viena ar otru.
PiemÄram, izmantojot brÄ«niŔķīga bibliotÄka Alex Palaestras varÄtu ieviest atmiÅas koplietoÅ”anu starp PHP un Go procesiem (lÄ«dzÄ«gi kÄ mod_php programmÄ Apache). TaÄu Å”ai bibliotÄkai ir funkcijas, kas ierobežo tÄs izmantoÅ”anu mÅ«su problÄmas risinÄÅ”anai.
MÄs nolÄmÄm izmantot citu, izplatÄ«tÄku pieeju: veidot mijiedarbÄ«bu starp procesiem, izmantojot kontaktligzdas/cauruļvadus. Å Ä« pieeja ir pierÄdÄ«jusi savu uzticamÄ«bu pÄdÄjo desmitgažu laikÄ un ir labi optimizÄta operÄtÄjsistÄmas lÄ«menÄ«.
SÄkumÄ mÄs izveidojÄm vienkÄrÅ”u binÄro protokolu datu apmaiÅai starp procesiem un pÄrsÅ«tÄ«Å”anas kļūdu apstrÄdei. VienkÄrÅ”ÄkajÄ formÄ Å”Äda veida protokols ir lÄ«dzÄ«gs tÄ«kla virkne Ń fiksÄta izmÄra paketes galvene (mÅ«su gadÄ«jumÄ 17 baiti), kas satur informÄciju par paketes veidu, tÄs lielumu un binÄro masku datu integritÄtes pÄrbaudei.
Mums Ŕķita, ka ar vienu protokolu ir par maz ā tÄpÄc pievienojÄm zvanÄ«Å”anas iespÄju Dodieties uz net/rpc pakalpojumiem tieÅ”i no PHP. Tas vÄlÄk mums ļoti palÄ«dzÄja attÄ«stÄ«bÄ, jo mÄs varÄjÄm viegli integrÄt Go bibliotÄkas PHP lietojumprogrammÄs. Å Ä« darba rezultÄtu var redzÄt, piemÄram, citÄ mÅ«su atvÄrtÄ pirmkoda produktÄ Goridža.
Uzdevumu sadale vairÄkiem PHP darbiniekiem
PÄc mijiedarbÄ«bas mehÄnisma ievieÅ”anas sÄkÄm domÄt, kÄ visefektÄ«vÄk pÄrnest uzdevumus uz PHP procesiem. Kad tiek saÅemts uzdevums, lietojumprogrammu serverim ir jÄizvÄlas brÄ«vs darbinieks, lai to pabeigtu. Ja darbinieks/process beidzas ar kļūdu vai ānomirstā, mÄs no tÄ atbrÄ«vojamies un izveidojam jaunu, lai to aizstÄtu. Un, ja darbinieks/process ir veiksmÄ«gi pabeigts, mÄs to nododam atpakaļ to darbinieku pulkam, kas ir pieejami uzdevumu veikÅ”anai.
MÄs izmantojÄm aktÄ«vo darbinieku kopuma glabÄÅ”anai buferÄtais kanÄls, lai noÅemtu negaidÄ«ti āmiruÅ”osā darbiniekus no kopas, mÄs pievienojÄm kļūdu un darbinieku stÄvokļu izsekoÅ”anas mehÄnismu.
RezultÄtÄ mÄs saÅÄmÄm funkcionÄjoÅ”u PHP serveri, kas spÄj apstrÄdÄt visus pieprasÄ«jumus, kas iesniegti binÄrÄ formÄ.
Lai mÅ«su lietojumprogramma darbotos kÄ tÄ«mekļa serveris, mums bija jÄizvÄlas uzticams PHP standarts, kas attÄlo visus ienÄkoÅ”os HTTP pieprasÄ«jumus. MÅ«su gadÄ«jumÄ mÄs vienkÄrÅ”i pÄrveidot net/http pieprasÄ«jums no Iet uz formÄtu PSR-7lai tas bÅ«tu savietojams ar lielÄko daļu mÅ«sdienÄs pieejamo PHP ietvaru.
TÄ kÄ PSR-7 tiek uzskatÄ«ts par nemainÄ«gu (daži teiktu, ka tehniski tas tÄ nav), izstrÄdÄtÄjiem ir jÄraksta lietojumprogrammas, kurÄs pieprasÄ«jums pÄc bÅ«tÄ«bas netiek uzskatÄ«ts par globÄlu vienÄ«bu. Tas lieliski saskan ar ilgstoÅ”u PHP procesu koncepciju. MÅ«su galÄ«gÄ ievieÅ”ana, kas vÄl nebija nosaukta, izskatÄ«jÄs Å”Ädi:
MÅ«su pirmais testa uzdevums bija API aizmugursistÄma, kas periodiski piedzÄ«voja negaidÄ«tus pieprasÄ«jumu pÄrrÄvumus (daudz biežÄk nekÄ parasti). Lai gan vairumÄ gadÄ«jumu nginx bija pietiekami, mÄs regulÄri saskÄrÄmies ar 502 kļūdÄm, jo āāmÄs nevarÄjÄm pietiekami Ätri lÄ«dzsvarot sistÄmu, lai nodroÅ”inÄtu paredzamo slodzes pieaugumu.
Lai aizstÄtu Å”o risinÄjumu, 2018. gada sÄkumÄ mÄs izvietojÄm savu pirmo PHP/Go lietojumprogrammu serveri. Un uzreiz mÄs saÅÄmÄm neticamu efektu! MÄs ne tikai pilnÄ«bÄ atbrÄ«vojÄmies no 502. kļūdas, bet arÄ« varÄjÄm par divÄm treÅ”daļÄm samazinÄt serveru skaitu, ietaupot daudz naudas un galvassÄpes inženieriem un produktu menedžeriem.
LÄ«dz gada vidum mÄs bijÄm pilnveidojuÅ”i savu risinÄjumu, publicÄjuÅ”i to vietnÄ GitHub saskaÅÄ ar MIT licenci un nosaukuÅ”i to Roadrunner, tÄdÄjÄdi uzsverot tÄ neticamo Ätrumu un efektivitÄti.
KÄ RoadRunner var uzlabot jÅ«su izstrÄdes kopu
iesniegums Roadrunner ļÄva mums Go pusÄ izmantot Middleware net/http, lai veiktu JWT verifikÄciju, pirms pieprasÄ«jums pat sasniedz PHP, kÄ arÄ« apstrÄdÄtu WebSockets un globÄlo stÄvokļu apkopoÅ”anu programmÄ Prometheus.
Pateicoties iebÅ«vÄtajam RPC, varat atvÄrt jebkuras Go bibliotÄkas API, kas paredzÄtas PHP, nerakstot paplaÅ”inÄjumu iesaiÅojumus. VÄl svarÄ«gÄk ir tas, ka RoadRunner var izmantot, lai izvietotu jaunus serverus, kas nav HTTP serveri. KÄ piemÄrus var minÄt apdarinÄtÄju palaiÅ”anu PHP AWS Lambda, izveidojot uzticamus rindu veidotÄjus un pat pievienojot grRPC mÅ«su lietojumprogrammÄm.
Ar PHP un Go kopienu palÄ«dzÄ«bu esam palielinÄjuÅ”i risinÄjuma stabilitÄti, dažos testos palielinÄjuÅ”i lietojumprogrammu veiktspÄju lÄ«dz pat 40 reizÄm, uzlabojuÅ”i atkļūdoÅ”anas rÄ«kus, ieviesuÅ”i integrÄciju ar Symfony ietvaru un pievienojuÅ”i HTTPS, HTTP/ atbalstu. 2, spraudÅi un PSR-17.
SecinÄjums
Dažus cilvÄkus joprojÄm uztver novecojis PHP kÄ lÄnas, apgrÅ«tinoÅ”as valodas, kas piemÄrota tikai WordPress spraudÅu rakstÄ«Å”anai. Å ie cilvÄki pat varÄtu teikt, ka PHP ir ierobežojums: kad lietojumprogramma kļūst pietiekami liela, jums ir jÄizvÄlas ānobrieduÅ”Äkaā valoda un jÄpÄrraksta daudzu gadu laikÄ uzkrÄtÄ kodu bÄze.
Uz to visu gribu atbildÄt: padomÄ vÄlreiz. MÄs uzskatÄm, ka tikai jÅ«s varat iestatÄ«t PHP ierobežojumus. JÅ«s varat pavadÄ«t visu savu dzÄ«vi, lÄkÄjot no vienas valodas uz citu, mÄÄ£inot atrast savÄm vajadzÄ«bÄm perfektu atbilstÄ«bu, vai arÄ« varat sÄkt domÄt par valodÄm kÄ par instrumentiem. TÄdas valodas kÄ PHP uztvertie trÅ«kumi patiesÄ«bÄ var bÅ«t tÄs panÄkumu iemesls. Un, ja to apvienojat ar citu valodu, piemÄram, Go, varat izveidot daudz jaudÄ«gÄkus produktus nekÄ tad, ja izmantotu tikai vienu valodu.
StrÄdÄjot ar Go un PHP kombinÄciju, mÄs varam teikt, ka mÄs tos mÄ«lam. MÄs neplÄnojam upurÄt vienu otras labÄ, bet drÄ«zÄk meklÄjam veidus, kÄ iegÅ«t vÄl lielÄku vÄrtÄ«bu no Ŕīs dubultÄs steks.
UPD: mÄs sveicam RoadRunner veidotÄju un oriÄ£inÄlÄ raksta lÄ«dzautoru - Lachesis