RoadRunner: PHP pole loodud surema, või Golang kiirustab appi

RoadRunner: PHP pole loodud surema, või Golang kiirustab appi

Tere, Habr! Me Badoo-s töötame aktiivselt PHP jõudluse kallal, kuna meil on sellel keelel piisavalt suur süsteem ja jõudluse küsimus on rahaküsimus. Üle kümne aasta tagasi lõime selleks PHP-FPM-i, mis algselt oli PHP jaoks mõeldud plaastrite kogum ja hiljem sai ametlikku väljastusse.

Viimastel aastatel on PHP palju edenenud: prügikoristus on paranenud, stabiilsus on kasvanud - täna saab PHP-s ilma suuremate probleemideta kirjutada deemonite ja pikaajalisi skripte. See võimaldas Spiral Scoutil edasi liikuda: RoadRunner, erinevalt PHP-FPM-ist, ei puhasta mälu päringute vahel, mis annab lisahüve jõudluses (kuigi see lähenemine muudab arendusprotsessi keerulisemaks). Me katsetame praegu seda tööriista, kuid me ei ole veel tulemusi, mida jagada. Ootamise lõbusamaks muutmiseks, avalikustame Spiral Scouti RoadRunneri esitlusest tõlgitud versiooni.

Artiklis esitatud lähenemine on meile lähedane: oma probleemide lahendamisel kasutame me samuti enamasti PHP ja Go kombinatsiooni, saavutades kasu mõlemast keelest, loobumata ühest teise kasuks.

Naudi!

Viimase kümne aasta jooksul oleme loonud rakendusi nii Fortune 500 nimekirjas olevatele ettevõtetele Fortune 500, kui ka ettevõtetele, mille auditoorium ei ületa 500 kasutajat. Kogu selle aja on meie insenerid toetanud peamiselt PHP taustapinda. Kuid kaks aastat tagasi mõjutas miski märkimisväärselt mitte ainult meie toodete jõudlust, vaid ka nende skaleeritavust - tutvustasime Golangi (Go) meie tehnoloogilisse virna.

Peaaegu kohe avastasime, et Go võimaldab meil luua suuremaid rakendusi, suurendades jõudlust kuni 40 korda. Selle abil saime laiendada olemasolevaid PHP-s kirjutatud tooteid, parandades neid, kasutades mõlema keele eeliseid.

Me räägime sellest, kuidas Go ja PHP kombinatsioon aitab lahendada tegelikke arenduse väljakutseid ja kuidas sellest on saanud meie tööriist, mis suudab vähendada PHP-ga seotud probleeme, mis on seotud PHP 'suretamise' mudeliga.

Teie igapäevane PHP arenduskeskkond

Enne kui räägime, kuidas Go abil on võimalik elustada PHP 'suretamise' mudelit, vaatame teie standardset PHP arenduskeskkonda.

Enamikul juhtudel käivitate rakenduse koos veebiserveri nginx ja PHP-FPM serveriga. Esimene teenindab staatilisi faile ja suunab PHP-FPM-ile spetsiifilisi päringuid, samas kui PHP-FPM käivitab PHP-koodi. Võib-olla kasutate vähem populaarset kombinatsiooni Apache'ist ja mod_php'ist. Kuigi see töötab veidi teisiti, on põhimõtted samad.

Vaadakem, kuidas PHP-FPM rakenduse koodi käivitab. Kui saabub päring, käivitab PHP-FPM alamsüsteemi PHP-protsessi ja edastab päringu detailid osana selle olekust (_GET, _POST, _SERVER jne).

Olek ei saa PHP-skripti täitmise ajal muutuda, seega on uue sisendkomplekti saamiseks ainult üks võimalus: puhastada protsessi mälu ja algatada see uuesti.

Selle täitmismudeli puhul on palju eeliseid. Te ei pea väga muretsema mälu tarbimise pärast, kõik protsessid on täielikult isoleeritud ja kui mõni neist 'sureb', siis loob see automaatselt uue ning see ei mõjuta teisi protsesse. Kuid sellel lähenemisel on ka puudusi, mis ilmnevad rakenduse skaleerimisel.

Tavapärase PHP-keskkonna puudused ja ebaefektiivsus

Kui tegelete professionaalse PHP-arendusega, siis teate, et uue projekti alustamiseks tuleb alustada raamistiku valimisega. See on teegikomplekt sõltuvuste sisestamiseks, ORM-id, tõlked ja templandid. Ja muidugi võib kõik kasutaja sisendandmed mugavalt panna ühte objekti (Symfony/HttpFoundation või PSR-7). Raamistike kasutamine on äge!

Kuid kõigel on hind. Igapäevases ettevõtte taseme raamistikus peab lihtsa kasutaja päringu või andmebaasi päringu töötlemiseks laadima vähemalt tosin failidest, looma arvukalt klasse ja parsima mitmeid konfiguratsioone. Kuid kõige hullem on see, et pärast iga ülesande täitmist tuleb kõik nullida ja alustada uuesti: kogu just algatatud kood muutub väärtusetuks, te ei saa selle abil enam ühegi teise päringut töödelda. Rääkige sellest ükskõik kellele, kes kodeerib mõnes teises keeles, ja te näete tema näos arusaamatust.

PHP-insenerid on aastaid otsinud viise selle probleemi lahendamiseks, kasutanud nutikaid 'laiskade' laadimise meetodeid, mikromooduleid, optimeeritud raamatukogusid, vahemälu jne. Kuid lõpuks tuleb ikkagi kogu rakendus tagasi nullida ja uuesti alustada, ikka ja jälle. (Tõlkija märkuse: osaliselt lahendab see probleem preload PHP 7.4)

Kas PHP suudab Go abil vastu pidada rohkem kui ühele päringule?

Võib kirjutada PHP skripte, mis elavad kauem kui mõned minutid (kuni tundide või päevadeni): näiteks cron-ülesanded, CSV-parserid, järjekorra töötlejad. Kõik need toimivad ühesuguse stsenaariumi alusel: nad toovad ülesande, täidavad selle, ootavad järgmist. Kood on pidevalt mälus, säästes väärtuslikke millisekunde, kuna raamistiku ja rakenduse laadimiseks on vajalik teostada mitmeid lisaülesandeid.

Kuid pikaajaliste skriptide arendamine pole nii lihtne. Iga viga tapab protsessi täielikult, mälulekke diagnoosimine ajab vihale ja F5-debugimise kasutamine pole enam võimalik.

Olek on paranenud koos PHP 7 ilmumisega: ilmus usaldusväärne prügikogumise süsteem, vigade töötlemine on lihtsam ja tuumalaienemised on nüüd lekkete eest kaitstud. Tõsi, inseneridel tuleb endiselt ettevaatlikult mälu käsitleda ja meeles pidada koodiseisundi probleeme (kas on olemas keel, kus nendele asjadele tähelepanu ei pöörata?). Siiski on PHP 7-s meid vähem ootamatustega.

Kas on võimalik võtta pikaajaliste PHP-skriptide töömudel, kohandada see lihtsate ülesannete, näiteks HTTP-päringute töötlemise jaoks ja seeläbi vabaneda vajadusest iga päringu puhul kõik uuesti laadida?

Selle probleemi lahendamiseks pidi esmalt rakendama serverirakendust, mis suudab vastu võtta HTTP-päringuid ja suunata need üksteise järel PHP-töötlejale, tapmata seda iga kord.

Me teadsime, et suudame kirjutada veebiserveri puhta PHP (PHP-PM) või C-lisandiga (Swoole). Kuigi kummalgi meetodil on omad eelised, ei rahuldanud mõlemad variandid meid — soovisime midagi enamat. Vajasime mitte lihtsalt veebiserverit — meie eesmärk oli leida lahendus, mis vabastaks meid PHP „raskest käivitamisest“ ja oleks samas kergesti kohandatav ja laiendatav konkreetsete rakenduste jaoks. Seega oli meil vajalik rakenduste server.

Kas Go võib selles aidata? Me teadsime, et võib, sest see keel kompileerib rakendused ühtsetesse binaarfailidesse; see on platvormidevaheline; kasutab oma väga elegantset, paralleelse töötlemise mudelit ja HTTP töötluse raamatukogu; ning lõpuks, meil on juurdepääs tuhandetele avatud lähtekoodiga raamatukogudele ja integratsioonidele.

Kahe programmeerimiskeele ühendamise raskused

Esimene samm oli määrata, kuidas kaks või enam rakendust omavahel suhtlevad.

Näiteks, kasutades suurepärast raamatukogu Alex Palästra, oli võimalik rakendada PHP ja Go protsesside vahel mälu jagamist (sarnaselt mod_php-ga Apache's). Kuid sellel raamatukogul on omadused, mis piiravad selle rakenduse sobivust meie ülesande lahendamiseks.

Otsustasime kasutada teist, laialdasemalt levinud lähenemist: ehitada interaktsioon protsesside vahel läbi socket'ite/torude. See lähenemine on viimase paarikümne aasta jooksul tõestanud oma usaldusväärsust ja on hästi optimeeritud operatsioonisüsteemi tasemel.

Alustuseks lõime lihtsa binaarprotokolli andmevahetuseks protsesside vahel ja edastamise vigade töötlemiseks. Oma kõige lihtsamas vormis sarnaneb selline protokoll netstring jot fikseeritud suurusega paketi päisega (meie puhul 17 baiti), mis sisaldab teavet paketi tüübi, selle suuruse ja andmete terviklikkuse kontrollimiseks mõeldud binaarset maski.

PHP poolel kasutasime funktsiooni pack, samas kui Go poolel kasutasime raamatukogu encoding/binary.

Üks protokoll ei tundunud piisav — lisasime võimaluse kutsuda Go-teenuseid net/rpc otse PHP-st. Hiljem aitas see meid arenduses, kuna saime hõlpsasti integreerida Go-raamatukogusid PHP-rakendustesse. Selle töö tulemust võib näha näiteks meie teises avatud lähtekoodiga tootes Goridge.

Ülesannete jaotamine mitme PHP-töötaja vahel

Pärast suhtlemismehhanismi rakendamist hakkasime mõtlema, kuidas edastada PHP-protsessidele ülesandeid võimalikult efektiivselt. Kui ülesanne saabub, peab rakenduse server valima töötaja, kes selle täitmise jaoks vaba on. Kui töötaja/protsess lõpetas töö veaga või „suri“, vabaneme temast ja loome uue asemele. Kui töötaja/protsess töötas edukalt, saadame ta tagasi töötajate rikka, kes on valmis ülesandeid täitma.

RoadRunner: PHP pole loodud surema, või Golang kiirustab appi

Aktiivsete töötajate kogu salvestamiseks kasutasime puhverdatud kanalit, töötajate ootamatult „suretud” eemaldamiseks lisasime veahaldus- ja olekute jälgimise mehhanismi.

Tulemuseks saime töötava PHP-serveri, mis suudab töödelda igasuguseid binaarseid päringuid.

Et meie rakendus saaks töötada nagu veebiserver, pidime valima usaldusväärse PHP-standardi igasuguste sissetulevate HTTP-päringute esitlemiseks. Meie puhul muudame net/http-päringu Go's PSR-7 formaati , et see oleks kooskõlas enamikku tänapäevaste PHP-raamistikega.Kuna PSR-7 on muutumatu (keegi ütleb, et tehniliselt see nii pole), peavad arendajad kirjutama rakendusi, mis ei käsitle päringut globaalse olendina. See sobib suurepäraselt pikaajaliste PHP-protsesside kontseptsiooniga. Meie lõplik teostus, mis ei ole veel nime saanud, nägi välja nii:

Tutvustame RoadRunnerit —

RoadRunner: PHP pole loodud surema, või Golang kiirustab appi

kõrge jõudlusega PHP-rakenduste server Meie esimene testülesanne oli API-tagumine, kus tekkis perioodiliselt ettearvamatuid ülekoormusi (kordades rohkem kui tavaliselt). Kuigi enamikul juhtudel oli nginx'i võimalustest piisavalt, seisime pidevalt silmitsi veaga 502, sest ei suutnud süsteemi piisavalt kiiresti tasakaalustada oodatava koormuse suurenemisega.

Selle lahenduse asendamiseks alustasime 2018. aasta alguses oma esimest PHP/Go-rakenduste serverit. Ja kohe saime uskumatuid tulemusi! Me mitte ainult ei vabanenud veast 502, vaid suutsime vähendada serverite arvu kolmandiku võrra, säästes palju raha ja valu tablettide jaoks inseneridele ja tootjate juhtidele.

Для замены этого решения в начале 2018 года мы развернули наш первый PHP/Go-сервер приложений. И сразу получили невероятный эффект! Мы не только полностью избавились от ошибки 502, но ещё и смогли на две трети уменьшить количество серверов, сэкономив кучу денег и таблеток от головной боли для инженеров и менеджеров продуктов.

Aasta keskpaiku täiustasime oma lahendust, avaldasime selle GitHubis MIT-i litsentsi all ja kutsusime seda RoadRunner, rõhutades seeläbi selle uskumatut kiirus ja efektiivsust.

Kuidas RoadRunner saab teie arendussteki parandada

Rakendamine RoadRunner lubas meil kasutada Middleware net/http Go pool, et teha JWT-sertifikaati juba enne, kui päring jõuab PHP-sse, samuti WebSocketite töötlemiseks ja globaalsete olekute koondamiseks Prometheuses.

Sisseehitatud RPC võimaldab avada API igasuguste Go-teekide jaoks PHP-s ilma laienduste-mähiste kirjutamiseta. Veelgi olulisem on see, et RoadRunneriga on võimalik juurutada uusi servereid, mis ei piirdu HTTP-ga. Näiteks võime mainida PHP töötlejate käivitamist AWS Lambda, usaldusväärsete järjekorraparserside loomist ja isegi lisamist gRPC meie rakendustesse.

Koos PHP ja Go kogudega oleme tõstnud lahenduse stabiilsust, mõningates testides suurendanud rakenduste jõudlust kuni 40 korda, täiustanud silumisvahendeid, rakendanud integratsiooni Symfony raamistiku ja lisanud HTTPS-i, HTTP/2, pistikute ja PSR-17 toe.

Kokkuvõte

Mõned inimesed on endiselt kinni vananenud arvamuses, et PHP on aeglane ja mahukas keel, mis sobib ainult WordPressi pistikprogrammide kirjutamiseks. Need inimesed võivad isegi öelda, et PHP-l on selline piirang: kui rakendus muutub piisavalt suureks, peab valima „küpsema” keele ja ümber kirjutama aastate jooksul kogunenud koodibaasi.

Sellele kõigile tahaksime vastata: mõelge uuesti. Me usume, et ainult teie ise seab mõned piirangud PHP-le. Te võite kulutada kogu elu, liikudes ühest keelest teise, püüdes leida oma vajadustega ideaalset kombinatsiooni, või hakata keeli käsitlema kui tööriistu. Kujuteldavad puudused nagu PHP võivad tegelikult olla selle eduka kasvu põhjused. Ja kui ühendada see mõne teise keelega nagu Go, siis loote palju võimsamaid tooteid, kui kui piirdute vaid ühe keele kasutamisega.

Töötades Go ja PHP kombinatsiooniga, võime kinnitada, et oleme neid armastanud. Me ei kavatse ühte ohverdada teise nimel — vastupidi, me otsime viise, kuidas sellest kahefstrahi veelgi rohkem kasu saada.

UPD: tervitame RoadRunneri loojat ja originaali artikli kaasautor - Lachezis

Allikas: habr.com

Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster