RoadRunner: PHP nav izveidots, lai mirtu, vai Golang, lai glābtu

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.

PHP pusē mēs izmantojām iepakojuma funkcija, un Go pusē - bibliotēka kodējums/binārs.

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.

RoadRunner: PHP nav izveidots, lai mirtu, vai Golang, lai glābtu

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:

RoadRunner: PHP nav izveidots, lai mirtu, vai Golang, lai glābtu

IepazÄ«stinām ar RoadRunner ā€” augstas veiktspējas PHP lietojumprogrammu serveris

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

Avots: www.habr.com

Pievieno komentāru