RoadRunner: Nid PHP ei adeiladu i farw, neu Golang i'r adwy

RoadRunner: Nid PHP ei adeiladu i farw, neu Golang i'r adwy

Hei Habr! Rydym yn weithgar yn Badoo gweithio ar berfformiad PHP, gan fod gennym system weddol fawr yn yr iaith hon a bod y mater perfformiad yn fater o arbed arian. Fwy na deng mlynedd yn ôl, fe wnaethom greu PHP-FPM ar gyfer hyn, a oedd ar y dechrau yn set o glytiau ar gyfer PHP, ac yn ddiweddarach aeth i mewn i'r dosbarthiad swyddogol.

Yn ystod y blynyddoedd diwethaf, mae PHP wedi gwneud cynnydd mawr: mae'r casglwr sbwriel wedi gwella, mae lefel y sefydlogrwydd wedi cynyddu - heddiw gallwch chi ysgrifennu daemonau a sgriptiau hirhoedlog yn PHP heb unrhyw broblemau. Roedd hyn yn caniatáu i Spiral Scout fynd ymhellach: nid yw RoadRunner, yn wahanol i PHP-FPM, yn glanhau cof rhwng ceisiadau, sy'n rhoi cynnydd perfformiad ychwanegol (er bod y dull hwn yn cymhlethu'r broses ddatblygu). Ar hyn o bryd rydym yn arbrofi gyda'r offeryn hwn, ond nid oes gennym unrhyw ganlyniadau eto i'w rhannu. I wneud aros amdanynt yn fwy o hwyl, rydym yn cyhoeddi'r cyfieithiad o'r cyhoeddiad RoadRunner gan Spiral Scout.

Mae'r ymagwedd o'r erthygl yn agos atom: wrth ddatrys ein problemau, rydym hefyd yn aml yn defnyddio criw o PHP a Go, gan gael buddion y ddwy iaith a pheidio â chefnu ar un o blaid y llall.

Mwynhewch!

Yn ystod y deng mlynedd diwethaf, rydym wedi creu ceisiadau ar gyfer cwmnïau o'r rhestr Fortune 500, ac ar gyfer busnesau sydd â chynulleidfa o ddim mwy na 500 o ddefnyddwyr. Trwy'r amser hwn, mae ein peirianwyr wedi bod yn datblygu'r backend yn bennaf yn PHP. Ond dwy flynedd yn ôl, cafodd rhywbeth effaith fawr nid yn unig ar berfformiad ein cynnyrch, ond hefyd ar eu gallu i dyfu - fe wnaethom gyflwyno Golang (Go) i'n pentwr technoleg.

Bron yn syth, fe wnaethom ddarganfod bod Go wedi caniatáu inni adeiladu cymwysiadau mwy gyda hyd at 40x o welliannau perfformiad. Ag ef, roeddem yn gallu ymestyn y cynhyrchion presennol a ysgrifennwyd yn PHP, gan eu gwella trwy gyfuno manteision y ddwy iaith.

Byddwn yn dweud wrthych sut mae'r cyfuniad o Go a PHP yn helpu i ddatrys problemau datblygu go iawn a sut mae wedi troi'n offeryn i ni a all gael gwared ar rai o'r problemau sy'n gysylltiedig â PHP model marw.

Eich amgylchedd datblygu PHP dyddiol

Cyn i ni siarad am sut y gallwch chi ddefnyddio Ewch i adfywio'r model marw PHP, gadewch i ni edrych ar eich amgylchedd datblygu PHP diofyn.

Yn y rhan fwyaf o achosion, rydych chi'n rhedeg eich cais gan ddefnyddio cyfuniad o weinydd gwe nginx a gweinydd PHP-FPM. Mae'r cyntaf yn gwasanaethu ffeiliau statig ac yn ailgyfeirio ceisiadau penodol i PHP-FPM, tra bod PHP-FPM ei hun yn gweithredu cod PHP. Efallai eich bod yn defnyddio'r cyfuniad llai poblogaidd o Apache a mod_php. Ond er ei fod yn gweithio ychydig yn wahanol, yr un yw'r egwyddorion.

Gadewch i ni edrych ar sut mae PHP-FPM yn gweithredu cod cais. Pan ddaw cais i mewn, mae PHP-FPM yn cychwyn proses plentyn PHP ac yn pasio manylion y cais fel rhan o'i gyflwr (_GET, _POST, _SERVER, ac ati).

Ni all y cyflwr newid yn ystod gweithrediad sgript PHP, felly yr unig ffordd i gael set newydd o ddata mewnbwn yw trwy glirio cof y broses a'i gychwyn eto.

Mae gan y model gweithredu hwn lawer o fanteision. Nid oes rhaid i chi boeni gormod am y defnydd o gof, mae'r holl brosesau wedi'u hynysu'n llwyr, ac os bydd un ohonynt yn "marw", bydd yn cael ei ail-greu'n awtomatig ac ni fydd yn effeithio ar weddill y prosesau. Ond mae gan y dull hwn hefyd anfanteision sy'n ymddangos wrth geisio graddio'r cais.

Anfanteision ac Aneffeithlonrwydd Amgylchedd PHP Rheolaidd

Os ydych chi'n ddatblygwr PHP proffesiynol, yna rydych chi'n gwybod ble i ddechrau prosiect newydd - gyda'r dewis o fframwaith. Mae'n cynnwys llyfrgelloedd chwistrellu dibyniaeth, ORMs, cyfieithiadau a thempledi. Ac, wrth gwrs, gellir rhoi holl fewnbwn defnyddwyr yn gyfleus mewn un gwrthrych (Symfony/HttpFoundation neu PSR-7). Mae fframweithiau'n cŵl!

Ond mae gan bopeth ei bris. Mewn unrhyw fframwaith lefel menter, i brosesu cais defnyddiwr syml neu fynediad i gronfa ddata, bydd yn rhaid i chi lwytho o leiaf dwsinau o ffeiliau, creu nifer o ddosbarthiadau a dosrannu sawl ffurfweddiad. Ond y peth gwaethaf yw, ar ôl cwblhau pob tasg, bydd angen i chi ailosod popeth a dechrau drosodd: mae'r holl god rydych chi newydd ei gychwyn yn dod yn ddiwerth, gyda'i help ni fyddwch chi'n prosesu cais arall mwyach. Dywedwch hyn wrth unrhyw raglennydd sy'n ysgrifennu mewn rhyw iaith arall, a byddwch yn gweld dryswch ar ei wyneb.

Mae peirianwyr PHP wedi bod yn chwilio am ffyrdd o ddatrys y broblem hon ers blynyddoedd, gan ddefnyddio technegau llwytho diog clyfar, microfframweithiau, llyfrgelloedd wedi'u optimeiddio, storfa, ac ati. Ond yn y diwedd, mae'n rhaid i chi ailosod y cymhwysiad cyfan a dechrau dro ar ôl tro. . (Nodyn y cyfieithydd: bydd y broblem hon yn cael ei datrys yn rhannol gyda dyfodiad rhaglwyth yn PHP 7.4)

A all PHP with Go oroesi mwy nag un cais?

Gallwch chi ysgrifennu sgriptiau PHP sy'n byw yn hirach nag ychydig funudau (hyd at oriau neu ddyddiau): er enghraifft, tasgau cron, parsers CSV, torwyr ciw. Maent i gyd yn gweithio yn ôl yr un senario: maent yn adfer tasg, yn ei chyflawni, ac yn aros am yr un nesaf. Mae'r cod yn aros yn y cof drwy'r amser, gan arbed milieiliadau gwerthfawr gan fod angen llawer o gamau ychwanegol i lwytho'r fframwaith a'r cymhwysiad.

Ond nid yw datblygu sgriptiau hirhoedlog yn hawdd. Mae unrhyw gamgymeriad yn lladd y broses yn llwyr, mae gwneud diagnosis o ollyngiadau cof yn gythruddo, ac nid yw dadfygio F5 bellach yn bosibl.

Mae'r sefyllfa wedi gwella gyda rhyddhau PHP 7: mae casglwr sbwriel dibynadwy wedi ymddangos, mae wedi dod yn haws trin gwallau, ac mae estyniadau cnewyllyn bellach yn atal gollyngiadau. Yn wir, mae angen i beirianwyr fod yn ofalus gyda'r cof a bod yn ymwybodol o faterion y wladwriaeth yn y cod (a oes iaith a all anwybyddu'r pethau hyn?). Eto i gyd, mae gan PHP 7 lai o bethau annisgwyl ar y gweill i ni.

A yw'n bosibl cymryd y model o weithio gyda sgriptiau PHP hirhoedlog, ei addasu i dasgau mwy dibwys fel prosesu ceisiadau HTTP, a thrwy hynny gael gwared ar yr angen i lwytho popeth o'r dechrau gyda phob cais?

I ddatrys y broblem hon, yn gyntaf roedd angen i ni weithredu cymhwysiad gweinydd a allai dderbyn ceisiadau HTTP a'u hailgyfeirio fesul un at y gweithiwr PHP heb ei ladd bob tro.

Roeddem yn gwybod y gallem ysgrifennu gweinydd gwe mewn PHP pur (PHP-PM) neu ddefnyddio estyniad C (Swoole). Ac er bod gan bob dull ei rinweddau ei hun, nid oedd y ddau opsiwn yn addas i ni - roeddem eisiau rhywbeth mwy. Roedd angen mwy na gweinydd gwe yn unig arnom - roeddem yn disgwyl cael ateb a allai ein harbed rhag y problemau sy'n gysylltiedig â “chychwyn caled” yn PHP, y gellid ar yr un pryd ei addasu a'i ymestyn yn hawdd ar gyfer cymwysiadau penodol. Hynny yw, roedd angen gweinydd cais arnom.

Allwch Fynd helpu gyda hyn? Roeddem yn gwybod y gallai oherwydd bod yr iaith yn crynhoi cymwysiadau yn ddeuaidd sengl; mae'n draws-lwyfan; yn defnyddio ei fodel prosesu cyfochrog, cain iawn ei hun (concurrency) a llyfrgell ar gyfer gweithio gyda HTTP; ac yn olaf, bydd miloedd o lyfrgelloedd ffynhonnell agored ac integreiddiadau ar gael i ni.

Anawsterau Cyfuno Dwy Iaith Rhaglennu

Yn gyntaf oll, roedd angen penderfynu sut y bydd dau gais neu fwy yn cyfathrebu â'i gilydd.

Er enghraifft, defnyddio llyfrgell ardderchog Alex Palaestras, roedd yn bosibl rhannu cof rhwng prosesau PHP a Go (tebyg i mod_php yn Apache). Ond mae gan y llyfrgell hon nodweddion sy'n cyfyngu ar ei defnydd ar gyfer datrys ein problem.

Fe benderfynon ni ddefnyddio dull gwahanol, mwy cyffredin: i adeiladu rhyngweithio rhwng prosesau trwy socedi / piblinellau. Mae'r dull hwn wedi bod yn ddibynadwy dros y degawdau diwethaf ac mae wedi'i optimeiddio'n dda ar lefel y system weithredu.

I ddechrau, rydym wedi creu protocol deuaidd syml ar gyfer cyfnewid data rhwng prosesau a thrin gwallau trosglwyddo. Yn ei ffurf symlaf, mae'r math hwn o brotocol yn debyg i llinyn rhwyd с pennawd pecyn maint sefydlog (17 bytes yn ein hachos ni), sy'n cynnwys gwybodaeth am y math o becyn, ei faint a mwgwd deuaidd i wirio cywirdeb y data.

Ar yr ochr PHP defnyddiasom swyddogaeth pecyn, ac ar yr ochr Go, y llyfrgell amgodio/deuaidd.

Roedd yn ymddangos i ni nad oedd un protocol yn ddigon - ac fe wnaethom ychwanegu'r gallu i alw net/rpc mynd gwasanaethau yn uniongyrchol o PHP. Yn ddiweddarach, bu hyn yn gymorth mawr i ni wrth ddatblygu, gan y gallem integreiddio llyfrgelloedd Go yn hawdd i gymwysiadau PHP. Mae canlyniad y gwaith hwn i'w weld, er enghraifft, yn ein cynnyrch ffynhonnell agored arall Goridge.

Dosbarthu tasgau ar draws gweithwyr PHP lluosog

Ar ôl gweithredu'r mecanwaith rhyngweithio, dechreuon ni feddwl am y ffordd fwyaf effeithlon o drosglwyddo tasgau i brosesau PHP. Pan fydd tasg yn cyrraedd, rhaid i weinydd y cais ddewis gweithiwr rhydd i'w chyflawni. Os bydd gweithiwr/proses yn gadael gyda gwall neu'n "marw", rydyn ni'n cael gwared arno ac yn creu un newydd yn ei le. Ac os yw'r gweithiwr/proses wedi'i chwblhau'n llwyddiannus, byddwn yn ei ddychwelyd i'r gronfa o weithwyr sydd ar gael i gyflawni tasgau.

RoadRunner: Nid PHP ei adeiladu i farw, neu Golang i'r adwy

I storio'r gronfa o weithwyr gweithgar, fe wnaethon ni ddefnyddio sianel byffer, i dynnu gweithwyr “marw” annisgwyl o'r pwll, fe wnaethom ychwanegu mecanwaith ar gyfer olrhain gwallau a chyflwr gweithwyr.

O ganlyniad, cawsom weinydd PHP gweithredol a oedd yn gallu prosesu unrhyw geisiadau a gyflwynir ar ffurf ddeuaidd.

Er mwyn i'n cais ddechrau gweithio fel gweinydd gwe, roedd yn rhaid i ni ddewis safon PHP ddibynadwy i gynrychioli unrhyw geisiadau HTTP a oedd yn dod i mewn. Yn ein hachos ni, dim ond ni trawsnewid cais net/http o Ewch i fformat PSR-7fel ei fod yn gydnaws â'r rhan fwyaf o'r fframweithiau PHP sydd ar gael heddiw.

Oherwydd bod PSR-7 yn cael ei ystyried yn ddigyfnewid (byddai rhai yn dweud yn dechnegol nad ydyw), mae'n rhaid i ddatblygwyr ysgrifennu ceisiadau nad ydynt yn trin y cais fel endid byd-eang mewn egwyddor. Mae hyn yn cyd-fynd yn dda â'r cysyniad o brosesau PHP hirhoedlog. Roedd ein gweithrediad terfynol, sydd eto i’w enwi, yn edrych fel hyn:

RoadRunner: Nid PHP ei adeiladu i farw, neu Golang i'r adwy

Yn cyflwyno RoadRunner - gweinydd cais PHP perfformiad uchel

Ein tasg brawf gyntaf oedd backend API, sy'n byrlymu o bryd i'w gilydd mewn ceisiadau anrhagweladwy (yn llawer amlach nag arfer). Er bod nginx yn ddigonol yn y rhan fwyaf o achosion, daethom ar draws 502 o wallau yn rheolaidd oherwydd ni allem gydbwyso'r system yn ddigon cyflym ar gyfer y cynnydd disgwyliedig mewn llwyth.

I ddisodli'r datrysiad hwn, fe wnaethom ddefnyddio ein gweinydd cais PHP / Go cyntaf yn gynnar yn 2018. A chael effaith anhygoel ar unwaith! Nid yn unig y cawsom wared ar y gwall 502 yn gyfan gwbl, ond roeddem yn gallu lleihau nifer y gweinyddwyr o ddwy ran o dair, gan arbed llawer o arian a phils cur pen i beirianwyr a rheolwyr cynnyrch.

Erbyn canol y flwyddyn, roeddem wedi gwella ein datrysiad, ei gyhoeddi ar GitHub o dan drwydded MIT a'i enwi ysgyfarnog, gan bwysleisio ei gyflymder a'i effeithlonrwydd anhygoel.

Sut y gall RoadRunner wella eich pentwr datblygu

Cais ysgyfarnog caniatáu i ni ddefnyddio Middleware net/http ar yr ochr Go i berfformio dilysiad JWT cyn i'r cais gyrraedd PHP, yn ogystal â thrin WebSockets a chyflwr cyfanredol yn fyd-eang yn Prometheus.

Diolch i'r RPC adeiledig, gallwch agor API unrhyw lyfrgelloedd Go ar gyfer PHP heb ysgrifennu deunydd lapio estyniad. Yn bwysicach fyth, gyda RoadRunner gallwch ddefnyddio gweinyddwyr newydd nad ydynt yn HTTP. Mae enghreifftiau'n cynnwys rhedeg trinwyr yn PHP AWS Lambda, creu torwyr ciw dibynadwy, a hyd yn oed ychwanegu gRPC i'n ceisiadau.

Gyda chymorth y cymunedau PHP a Go, fe wnaethom wella sefydlogrwydd yr ateb, cynyddu perfformiad cymhwysiad hyd at 40 gwaith mewn rhai profion, gwella offer dadfygio, integreiddio â fframwaith Symfony, ac ychwanegu cefnogaeth i HTTPS, HTTP/2, ategion, a PSR-17.

Casgliad

Mae rhai pobl yn dal i gael eu dal yn y syniad hen ffasiwn o PHP fel iaith araf, anhylaw sydd ond yn dda ar gyfer ysgrifennu ategion ar gyfer WordPress. Efallai y bydd y bobl hyn hyd yn oed yn dweud bod gan PHP gyfyngiad o'r fath: pan fydd y cymhwysiad yn mynd yn ddigon mawr, mae'n rhaid i chi ddewis iaith fwy “aeddfed” ac ailysgrifennu'r sylfaen cod a gronnwyd dros nifer o flynyddoedd.

I hyn oll rwyf am ateb: meddyliwch eto. Credwn mai dim ond chi sy'n gosod unrhyw gyfyngiadau ar gyfer PHP. Gallwch chi dreulio'ch bywyd cyfan yn trawsnewid o un iaith i'r llall, yn ceisio dod o hyd i'r cyfatebiaeth berffaith i'ch anghenion, neu gallwch chi ddechrau meddwl am ieithoedd fel offer. Efallai mai diffygion tybiedig iaith fel PHP yw'r rheswm dros ei llwyddiant mewn gwirionedd. Ac os ydych chi'n ei gyfuno ag iaith arall fel Go, yna byddwch chi'n creu cynhyrchion llawer mwy pwerus na phe baech chi'n gyfyngedig i ddefnyddio unrhyw un iaith.

Ar ôl gweithio gyda chriw o Go a PHP, gallwn ddweud ein bod yn eu caru. Nid ydym yn bwriadu aberthu un am y llall - i'r gwrthwyneb, byddwn yn edrych am ffyrdd o gael hyd yn oed mwy o werth o'r pentwr deuol hwn.

UPD: rydym yn croesawu crëwr RoadRunner a chyd-awdur yr erthygl wreiddiol - Lachesis

Ffynhonnell: hab.com

Ychwanegu sylw