RoadRunner: PHP ei ole loodud surema või Golang appi

RoadRunner: PHP ei ole loodud surema või Golang appi

Tere Habr! Oleme Badoos aktiivsed PHP jõudluse kallal töötamine, kuna meil on selles keeles üsna suur süsteem ja jõudluse probleem on raha säästmise probleem. Rohkem kui kümme aastat tagasi lõime selle jaoks PHP-FPM, mis algul oli PHP paikade komplekt ja hiljem läks ametlikku distributsiooni.

Viimastel aastatel on PHP teinud suuri edusamme: paranenud on prügikoguja, tõusnud stabiilsuse tase – tänapäeval saab PHP-s ilma probleemideta kirjutada deemoneid ja pikaealisi skripte. See võimaldas Spiral Scoutil minna kaugemale: erinevalt PHP-FPM-ist ei puhasta RoadRunner päringute vahel mälu, mis annab täiendava jõudluse kasvu (kuigi see lähenemine muudab arendusprotsessi keerulisemaks). Katsetame praegu selle tööriistaga, kuid meil pole veel tulemusi, mida jagada. Et nende ootamine oleks lõbusam, avaldame Spiral Scouti RoadRunneri kuulutuse tõlke.

Artiklist tulenev lähenemine on meile lähedane: oma probleemide lahendamisel kasutame kõige sagedamini ka hunnikut PHP-d ja Go-d, kasutades mõlema keele eeliseid ega hülgamata üht teise kasuks.

Naudi!

Viimase kümne aasta jooksul oleme koostanud nimekirjast ettevõtetele rakendusi Fortune 500ja ettevõtetele, mille vaatajaskond ei ületa 500 kasutajat. Kogu selle aja on meie insenerid taustaprogrammi arendanud peamiselt PHP-s. Kuid kaks aastat tagasi ei mõjutanud miski mitte ainult meie toodete jõudlust, vaid ka nende mastaapsust – me tutvustasime Golangi (Go) oma tehnoloogiavirna.

Peaaegu kohe avastasime, et Go võimaldas meil luua suuremaid rakendusi kuni 40-kordse jõudluse täiustusega. Selle abil saime laiendada olemasolevaid PHP-s kirjutatud tooteid, täiustades neid, ühendades mõlema keele eelised.

Räägime teile, kuidas Go ja PHP kombinatsioon aitab lahendada tegelikke arendusprobleeme ning kuidas sellest on saanud meie jaoks tööriist, mis suudab vabaneda mõnest PHP suremas mudel.

Sinu igapäevane PHP arenduskeskkond

Enne kui räägime sellest, kuidas saate Go abil PHP sureva mudeli taaselustada, heidame pilgu teie PHP vaikearenduskeskkonnale.

Enamikul juhtudel käitate oma rakendust nginxi veebiserveri ja PHP-FPM-serveri kombinatsiooni abil. Esimene teenindab staatilisi faile ja suunab konkreetsed päringud PHP-FPM-ile, samas kui PHP-FPM ise täidab PHP-koodi. Võimalik, et kasutate Apache ja mod_php vähem populaarset kombinatsiooni. Kuid kuigi see töötab veidi teisiti, on põhimõtted samad.

Vaatame, kuidas PHP-FPM rakenduse koodi täidab. Kui päring saabub, initsialiseerib PHP-FPM PHP alamprotsessi ja edastab päringu üksikasjad selle oleku osana (_GET, _POST, _SERVER jne).

Olek ei saa PHP-skripti täitmise ajal muutuda, seega on uue sisendandmete komplekti saamiseks ainult üks võimalus: protsessimälu tühjendamine ja selle uuesti lähtestamine.

Sellel täitmismudelil on palju eeliseid. Mälu tarbimise pärast ei pea liigselt muretsema, kõik protsessid on täiesti isoleeritud ja kui üks neist "sureb", siis luuakse see automaatselt uuesti ja see ei mõjuta ülejäänud protsesse. Kuid sellel lähenemisviisil on ka puudusi, mis ilmnevad rakenduse skaleerimisel.

Tavalise PHP keskkonna miinused ja ebaefektiivsused

Kui olete professionaalne PHP arendaja, siis teate, kust alustada uut projekti – raamistiku valikuga. See koosneb sõltuvuse sisestamise teekidest, ORM-idest, tõlgetest ja mallidest. Ja loomulikult saab kogu kasutaja sisendi mugavalt paigutada ühte objekti (Symfony/HttpFoundation või PSR-7). Raamid on lahedad!

Kuid igal asjal on oma hind. Igas ettevõttetaseme raamistikus peate lihtsa kasutajapäringu või andmebaasile juurdepääsu töötlemiseks laadima vähemalt kümneid faile, looma arvukalt klasse ja sõeluma mitut konfiguratsiooni. Kuid kõige hullem on see, et pärast iga ülesande täitmist peate kõik lähtestama ja alustama otsast peale: kogu äsja algatatud kood muutub kasutuks, selle abiga te enam teist taotlust ei töötle. Öelge seda igale programmeerijale, kes kirjutab mõnes muus keeles, ja te näete tema näol hämmeldust.

PHP insenerid on aastaid otsinud viise selle probleemi lahendamiseks, kasutades nutikaid laiska laadimise tehnikaid, mikroraamistikke, optimeeritud teeke, vahemälu jne. Kuid lõpuks peate ikkagi kogu rakenduse lähtestama ja uuesti ja uuesti alustama. . (Tõlkija märkus: see probleem lahendatakse osaliselt, kui ilmub eelkoormus PHP 7.4-s)

Kas PHP koos Go-ga suudab üle elada rohkem kui ühe taotluse?

Võimalik on kirjutada PHP-skripte, mis elavad kauem kui paar minutit (kuni tunde või päevi): näiteks cron-ülesandeid, CSV-parsereid, järjekorra katkestajaid. Nad kõik töötavad sama stsenaariumi järgi: nad toovad ülesande, täidavad selle ja ootavad järgmist. Kood asub kogu aeg mälus, säästes väärtuslikke millisekundeid, kuna raamistiku ja rakenduse laadimiseks on vaja palju täiendavaid samme.

Kuid pikaealiste skriptide väljatöötamine pole lihtne. Iga tõrge tapab protsessi täielikult, mälulekke diagnoosimine on raevukas ja F5 silumine pole enam võimalik.

Olukord on paranenud PHP 7 väljatulekuga: ilmunud on töökindel prügikorjaja, vigade käsitlemine on muutunud lihtsamaks ja kerneli laiendused on nüüd lekkekindlad. Tõsi, insenerid peavad siiski olema mäluga ettevaatlikud ja olema teadlikud koodis esinevatest olekuprobleemidest (kas on olemas keel, mis suudab neid asju ignoreerida?). Siiski on PHP 7-l meile vähem üllatusi varuks.

Kas on võimalik võtta pikaealiste PHP-skriptidega töötamise mudelit, kohandada seda triviaalsemate ülesannetega, nagu HTTP-päringute töötlemine, ja seeläbi vabaneda vajadusest laadida iga päringuga kõike nullist?

Selle probleemi lahendamiseks pidime esmalt juurutama serverirakenduse, mis suudab vastu võtta HTTP-päringuid ja suunata need ükshaaval PHP-töötaja juurde, ilma seda iga kord tapmata.

Teadsime, et saame kirjutada veebiserveri puhta PHP-ga (PHP-PM) või C-laiendiga (Swoole). Ja kuigi igal meetodil on oma eelised, ei sobinud meile mõlemad võimalused - tahtsime midagi enamat. Vajasime enamat kui lihtsalt veebiserverit – ootasime, et saame lahenduse, mis päästaks meid probleemidest, mis on seotud PHP „raske käivitamisega“, mida saab samal ajal hõlpsasti kohandada ja laiendada konkreetsete rakenduste jaoks. See tähendab, et meil oli vaja rakendusserverit.

Kas Go saab sellega aidata? Teadsime, et see saab, sest keel kompileerib rakendused üksikuteks binaarfailideks; see on platvormideülene; kasutab HTTP-ga töötamiseks oma väga elegantset paralleeltöötlusmudelit (samaaegsust) ja teeki; ja lõpuks on meie käsutuses tuhanded avatud lähtekoodiga teegid ja integratsioonid.

Kahe programmeerimiskeele kombineerimise raskused

Kõigepealt oli vaja kindlaks teha, kuidas kaks või enam rakendust omavahel suhtlevad.

Näiteks kasutades suurepärane raamatukogu Alex Palaestras, oli võimalik jagada mälu PHP ja Go protsesside vahel (sarnaselt mod_php-ga Apache'is). Kuid sellel teegil on funktsioone, mis piiravad selle kasutamist meie probleemi lahendamiseks.

Otsustasime kasutada teistsugust, levinumat lähenemist: luua protsessidevaheline interaktsioon pistikupesade/torujuhtmete kaudu. See lähenemisviis on viimastel aastakümnetel osutunud usaldusväärseks ja seda on hästi optimeeritud operatsioonisüsteemi tasemel.

Alustuseks lõime protsessidevaheliseks andmete vahetamiseks ja edastusvigade käsitlemiseks lihtsa binaarprotokolli. Lihtsamal kujul on seda tüüpi protokoll sarnane võrgustring с fikseeritud suurusega paketi päis (meie puhul 17 baiti), mis sisaldab teavet paketi tüübi, suuruse ja binaarmaski kohta andmete terviklikkuse kontrollimiseks.

PHP poolel kasutasime pakkimisfunktsioonja mineku poolel raamatukogu kodeering/binaarne.

Meile tundus, et ühest protokollist ei piisa – ja lisasime helistamisvõimaluse net/rpc go teenused otse PHP-st. Hiljem aitas see meid arendamisel palju, kuna saime hõlpsasti integreerida Go teeke PHP rakendustesse. Selle töö tulemust saab näha näiteks meie teises avatud lähtekoodiga tootes Gorridge.

Ülesannete jaotamine mitme PHP töötaja vahel

Pärast interaktsioonimehhanismi rakendamist hakkasime mõtlema, kuidas oleks kõige tõhusam viis ülesannete ülekandmiseks PHP protsessidesse. Kui ülesanne saabub, peab rakendusserver valima selle täitmiseks vaba töötaja. Kui töötaja/protsess väljub veaga või "sureb", siis vabaneme sellest ja loome selle asemele uue. Ja kui töötaja/protsess on edukalt lõpule viidud, tagastame selle tööülesannete täitmiseks saadaolevate töötajate hulka.

RoadRunner: PHP ei ole loodud surema või Golang appi

Aktiivsete töötajate kogumi hoidmiseks kasutasime puhverdatud kanal, et ootamatult surnud töötajad kogumist eemaldada, lisasime mehhanismi vigade ja töötajate seisundi jälgimiseks.

Selle tulemusena saime töötava PHP-serveri, mis on võimeline töötlema kõiki binaarsel kujul esitatud päringuid.

Selleks, et meie rakendus hakkaks töötama veebiserverina, pidime valima usaldusväärse PHP standardi, mis esindaks kõiki sissetulevaid HTTP päringuid. Meie puhul me lihtsalt teisendada net/http päring aadressilt Go to format PSR-7nii et see ühildub enamiku tänapäeval saadaolevate PHP raamistikega.

Kuna PSR-7 peetakse muutumatuks (mõned ütlevad, et tehniliselt pole see nii), peavad arendajad kirjutama rakendusi, mis ei käsitle taotlust põhimõtteliselt globaalse üksusena. See sobib hästi pikaealiste PHP protsesside kontseptsiooniga. Meie lõplik teostus, mida pole veel nimetanud, nägi välja selline:

RoadRunner: PHP ei ole loodud surema või Golang appi

Tutvustame RoadRunnerit – suure jõudlusega PHP rakendusserver

Meie esimene katseülesanne oli API-taustaprogramm, mis esitab perioodiliselt ettearvamatuid taotlusi (tavalisest palju sagedamini). Kuigi nginx oli enamikul juhtudel piisav, ilmnes meil regulaarselt 502 viga, kuna me ei suutnud süsteemi eeldatava koormuse kasvu jaoks piisavalt kiiresti tasakaalustada.

Selle lahenduse asendamiseks juurutasime oma esimese PHP/Go rakendusserveri 2018. aasta alguses. Ja sai kohe uskumatu efekti! Me mitte ainult ei vabanenud veast 502 täielikult, vaid saime serverite arvu kahe kolmandiku võrra vähendada, säästes sellega palju raha ja peavalutablette inseneride ja tootejuhtide jaoks.

Aasta keskpaigaks olime oma lahendust täiustanud, avaldanud selle GitHubis MIT litsentsi all ja andnud sellele nime RoadRunner, rõhutades sellega selle uskumatut kiirust ja tõhusust.

Kuidas RoadRunner saab teie arenduspakki täiustada

Taotlus RoadRunner võimaldas meil kasutada vahevara net/http-d Go poolel, et teostada JWT-kinnitust enne, kui päring jõuab PHP-ni, ning hallata Prometheuses globaalselt WebSockette ja koondolekut.

Tänu sisseehitatud RPC-le saate avada mis tahes PHP jaoks mõeldud Go teeki API ilma laiendusmähiseid kirjutamata. Veelgi olulisem on see, et RoadRunneriga saate juurutada uusi mitte-HTTP-servereid. Näited hõlmavad töötlejate käitamist PHP-s AWS Lambda, luues usaldusväärseid järjekorra katkestajaid ja isegi lisades gRPC meie rakendustele.

PHP ja Go kommuunide abil parandasime lahenduse stabiilsust, suurendasime rakenduste jõudlust mõnes testis kuni 40 korda, täiustasime silumistööriistu, juurutasime integratsiooni Symfony raamistikuga ning lisasime HTTPS-i, HTTP/2 toe, pistikprogrammid ja PSR-17.

Järeldus

Mõned inimesed on endiselt tabatud vananenud arusaamast PHP-st kui aeglasest ja kohmakast keelest, mis sobib ainult WordPressi pistikprogrammide kirjutamiseks. Need inimesed võivad isegi öelda, et PHP-l on selline piirang: kui rakendus muutub piisavalt suureks, peate valima "küpsema" keele ja kirjutama paljude aastate jooksul kogunenud koodibaasi ümber.

Sellele kõigele tahan vastata: mõtle uuesti. Usume, et ainult teie määrate PHP-le piiranguid. Võite veeta kogu oma elu ühelt keelelt teisele üleminekul, püüdes leida oma vajadustele sobivat vastet või hakata mõtlema keeltest kui tööriistadest. Sellise keele nagu PHP oletatavad vead võivad tegelikult olla selle edu põhjuseks. Ja kui ühendate selle mõne teise keelega, näiteks Go, loote palju võimsamaid tooteid kui piirduks ühe keele kasutamisega.

Olles töötanud hulga Go ja PHP-ga, võime öelda, et me armastame neid. Me ei plaani üht teisele ohverdada – vastupidi, otsime võimalusi, kuidas sellest kahest stäkist veelgi rohkem väärtust saada.

UPD: tervitame RoadRunneri loojat ja originaalartikli kaasautorit - Lachesis

Allikas: www.habr.com

Lisa kommentaar