RoadRunner: PHP ölmək üçün qurulmayıb, ya da Qolanq xilas etmək üçün

RoadRunner: PHP ölmək üçün qurulmayıb, ya da Qolanq xilas etmək üçün

Hey Habr! Biz Badoo-da aktivik PHP performansı üzərində işləyir, çünki bu dildə kifayət qədər böyük bir sistemimiz var və performans məsələsi pula qənaət məsələsidir. On ildən çox əvvəl biz bunun üçün PHP-FPM yaratdıq, o, əvvəlcə PHP üçün yamaqlar dəsti idi, sonra isə rəsmi paylanmaya daxil oldu.

Son illərdə PHP böyük irəliləyiş əldə edib: zibil toplayıcı təkmilləşdi, stabillik səviyyəsi yüksəldi - bu gün PHP-də heç bir problem olmadan demonlar və uzunömürlü skriptlər yaza bilərsiniz. Bu, Spiral Scout-a daha da irəli getməyə imkan verdi: RoadRunner, PHP-FPM-dən fərqli olaraq, sorğular arasında yaddaşı təmizləmir, bu da əlavə performans qazancı verir (baxmayaraq ki, bu yanaşma inkişaf prosesini çətinləşdirir). Hazırda bu aləti sınaqdan keçiririk, lakin hələ paylaşacağımız heç bir nəticəmiz yoxdur. Onları gözləməyi daha əyləncəli etmək üçün, biz Spiral Scout-dan RoadRunner elanının tərcüməsini dərc edirik.

Məqalədən yanaşma bizə yaxındır: problemlərimizi həll edərkən biz də çox vaxt bir dəstə PHP və Go-dan istifadə edirik, hər iki dilin üstünlüklərini əldə edirik və birini digərinin xeyrinə tərk etmirik.

Enjoy!

Son on ildə biz siyahıdan şirkətlər üçün ərizələr yaratmışıq Fortune 500, və auditoriyası 500 istifadəçidən çox olmayan bizneslər üçün. Bütün bu müddət ərzində mühəndislərimiz backend-i əsasən PHP-də inkişaf etdirirdilər. Lakin iki il əvvəl bir şey təkcə məhsullarımızın performansına deyil, həm də onların miqyasına böyük təsir göstərdi - biz Golang (Go) texnologiyasını texnoloji yığınımıza təqdim etdik.

Demək olar ki, dərhal biz Go-nun 40x-ə qədər performans təkmilləşdirmələri ilə daha böyük proqramlar yaratmağa imkan verdiyini aşkar etdik. Bununla biz PHP-də yazılmış mövcud məhsulları genişləndirə, hər iki dilin üstünlüklərini birləşdirərək onları təkmilləşdirə bildik.

Go və PHP-nin birləşməsi real inkişaf problemlərini həll etməyə necə kömək etdiyini və onun bizim üçün bəzi problemlərdən qurtula bilən bir vasitəyə çevrildiyini sizə xəbər verəcəyik. PHP ölən model.

Gündəlik PHP inkişaf mühitiniz

PHP-nin ölməkdə olan modelini canlandırmaq üçün Go-dan necə istifadə edə biləcəyiniz haqqında danışmazdan əvvəl gəlin, standart PHP inkişaf mühitinizə nəzər salaq.

Əksər hallarda siz proqramınızı nginx veb serveri və PHP-FPM serverinin birləşməsindən istifadə edərək işlədirsiniz. Birincisi statik fayllara xidmət edir və xüsusi sorğuları PHP-FPM-ə yönləndirir, PHP-FPM isə özü PHP kodunu icra edir. Siz Apache və mod_php-in daha az populyar birləşməsindən istifadə edirsiniz. Ancaq bir az fərqli işləsə də, prinsiplər eynidir.

Gəlin PHP-FPM-nin proqram kodunu necə icra etdiyinə nəzər salaq. Sorğu daxil olduqda, PHP-FPM PHP uşaq prosesini işə salır və sorğunun təfərrüatlarını öz vəziyyətinin bir hissəsi kimi ötürür (_GET, _POST, _SERVER və s.).

PHP skriptinin icrası zamanı vəziyyət dəyişə bilməz, ona görə də yeni giriş məlumat dəstini əldə etməyin yalnız bir yolu var: proses yaddaşını təmizləmək və onu yenidən işə salmaqla.

Bu icra modelinin bir çox üstünlükləri var. Yaddaş istehlakı ilə bağlı çox narahat olmaq lazım deyil, bütün proseslər tamamilə təcrid olunur və onlardan biri "ölür"sə, avtomatik olaraq yenidən yaradılacaq və qalan proseslərə təsir göstərməyəcəkdir. Lakin bu yanaşmanın tətbiqi genişləndirməyə çalışarkən ortaya çıxan çatışmazlıqları da var.

Adi PHP mühitinin çatışmazlıqları və səmərəsizliyi

Əgər siz peşəkar PHP tərtibatçısınızsa, o zaman yeni layihəyə haradan başlayacağınızı bilirsiniz - çərçivə seçimi ilə. O, asılılıq inyeksiya kitabxanalarından, ORM-lərdən, tərcümələrdən və şablonlardan ibarətdir. Və əlbəttə ki, bütün istifadəçi daxiletmələri rahatlıqla bir obyektə yerləşdirilə bilər (Symfony/HttpFoundation və ya PSR-7). Çərçivələr əladır!

Amma hər şeyin öz qiyməti var. İstənilən müəssisə səviyyəli çərçivədə sadə istifadəçi sorğusunu emal etmək və ya verilənlər bazasına daxil olmaq üçün siz ən azı onlarla fayl yükləməli, çoxsaylı siniflər yaratmalı və bir neçə konfiqurasiyanı təhlil etməli olacaqsınız. Amma ən pisi odur ki, hər tapşırığı yerinə yetirdikdən sonra hər şeyi sıfırlayıb yenidən başlamalı olacaqsınız: indicə başlatdığınız bütün kodlar faydasız olur, onun köməyi ilə siz artıq başqa sorğunu emal etməyəcəksiniz. Bunu hansısa başqa dildə yazan hər hansı proqramçıya deyin, onun üzündə çaşqınlıq görərsiniz.

PHP mühəndisləri illərdir ki, ağıllı tənbəl yükləmə texnikalarından, mikroçərçivlərdən, optimallaşdırılmış kitabxanalardan, keşdən və s. istifadə edərək bu problemi həll etməyin yollarını axtarırlar. Amma sonda siz hələ də bütün tətbiqi sıfırlamalı və təkrar-təkrar başlamalısınız. . (Tərcüməçinin qeydi: bu problemin gəlişi ilə qismən həll olunacaq preload PHP 7.4-də)

Go ilə PHP birdən çox sorğuya davam edə bilərmi?

Bir neçə dəqiqədən (saatlara və ya günlərə qədər) ömür sürən PHP skriptlərini yazmaq mümkündür: məsələn, cron tapşırıqları, CSV analizatorları, növbə kəsicilər. Onların hamısı eyni ssenari üzrə işləyir: tapşırığı götürür, yerinə yetirir və növbətisini gözləyirlər. Kod hər zaman yaddaşda qalır, qiymətli millisaniyələrə qənaət edir, çünki çərçivə və tətbiqi yükləmək üçün bir çox əlavə addımlar tələb olunur.

Lakin uzunömürlü skriptlər hazırlamaq asan deyil. İstənilən xəta prosesi tamamilə öldürür, yaddaş sızmasının diaqnozu qəzəbləndirir və F5-də sazlama artıq mümkün deyil.

PHP 7-nin buraxılması ilə vəziyyət yaxşılaşdı: etibarlı zibil toplayıcı peyda oldu, səhvləri idarə etmək daha asan oldu və nüvə uzantıları artıq sızdırmazdır. Düzdür, mühəndislər hələ də yaddaşla diqqətli olmalı və koddakı dövlət məsələlərindən xəbərdar olmalıdırlar (bunlara məhəl qoymayan dil varmı?). Yenə də PHP 7-nin bizim üçün daha az sürprizləri var.

Uzunömürlü PHP skriptləri ilə işləmək modelini götürmək, onu HTTP sorğularının işlənməsi kimi daha mənasız işlərə uyğunlaşdırmaq və bununla da hər sorğu ilə hər şeyi sıfırdan yükləmək ehtiyacından xilas olmaq mümkündürmü?

Bu problemi həll etmək üçün əvvəlcə HTTP sorğularını qəbul edə bilən və hər dəfə onu öldürmədən onları bir-bir PHP işçisinə yönləndirə bilən server tətbiqini həyata keçirməli olduq.

Biz bilirdik ki, biz təmiz PHP (PHP-PM) və ya C uzantısından (Swoole) istifadə edərək veb server yaza bilərik. Hər bir metodun öz üstünlükləri olsa da, hər iki variant bizə uyğun gəlmədi - daha çox şey istədik. Bizə sadəcə veb-serverdən daha çox ehtiyacımız var idi - biz gözləyirdik ki, bizi PHP-də “çətin başlanğıc” ilə bağlı problemlərdən xilas edə biləcək, eyni zamanda asanlıqla uyğunlaşdırıla və xüsusi proqramlar üçün genişləndirilə bilər. Yəni bizə proqram serveri lazım idi.

Go bu işdə kömək edə bilərmi? Biz bilirdik ki, bu dil proqramları tək binarlara tərtib edir; çarpaz platformadır; HTTP ilə işləmək üçün öz, çox zərif, paralel emal modelindən (koncurrency) və kitabxanadan istifadə edir; və nəhayət, minlərlə açıq mənbəli kitabxanalar və inteqrasiyalar bizim üçün əlçatan olacaq.

İki proqramlaşdırma dilini birləşdirməyin çətinlikləri

İlk növbədə, iki və ya daha çox tətbiqin bir-biri ilə necə əlaqə quracağını müəyyən etmək lazım idi.

Məsələn, istifadə əla kitabxana Alex Palaestras, PHP və Go prosesləri arasında yaddaşı paylaşmaq mümkün idi (Apache-də mod_php-ə bənzər). Lakin bu kitabxananın problemimizi həll etmək üçün istifadəsini məhdudlaşdıran xüsusiyyətlər var.

Fərqli, daha ümumi bir yanaşmadan istifadə etmək qərarına gəldik: prizlər / boru kəmərləri vasitəsilə proseslər arasında qarşılıqlı əlaqə qurmaq. Bu yanaşma son onilliklər ərzində etibarlı olduğunu sübut etdi və əməliyyat sistemi səviyyəsində yaxşı optimallaşdırılıb.

Başlamaq üçün biz proseslər arasında məlumat mübadiləsi və ötürmə xətalarının idarə edilməsi üçün sadə ikili protokol yaratdıq. Ən sadə formada bu tip protokol oxşardır şəbəkə xətti с sabit ölçülü paket başlığı (bizim vəziyyətimizdə 17 bayt), paketin növü, ölçüsü və məlumatların bütövlüyünü yoxlamaq üçün ikili maska ​​haqqında məlumatları ehtiva edir.

PHP tərəfində istifadə etdik paket funksiyası, və getmək tərəfində, kitabxana kodlaşdırma/ikili.

Bizə elə gəldi ki, bir protokol kifayət deyil - və zəng etmək imkanı əlavə etdik net/rpc go xidmətləri birbaşa PHP-dən. Sonralar bu, inkişafda bizə çox kömək etdi, çünki biz Go kitabxanalarını PHP proqramlarına asanlıqla inteqrasiya edə bildik. Bu işin nəticəsini, məsələn, digər açıq mənbəli məhsulumuzda görmək olar Qoridə.

Bir çox PHP işçisi arasında tapşırıqların paylanması

Qarşılıqlı təsir mexanizmini həyata keçirdikdən sonra biz tapşırıqları PHP proseslərinə köçürməyin ən səmərəli yolu haqqında düşünməyə başladıq. Tapşırıq gəldikdə, proqram serveri onu yerinə yetirmək üçün pulsuz işçi seçməlidir. Əgər işçi/proses xəta ilə çıxış edirsə və ya “ölürsə” biz ondan xilas oluruq və onun yerinə yenisini yaradırıq. Və əgər işçi/proses uğurla başa çatıbsa, biz onu tapşırıqları yerinə yetirmək üçün mövcud işçilər hovuzuna qaytarırıq.

RoadRunner: PHP ölmək üçün qurulmayıb, ya da Qolanq xilas etmək üçün

Aktiv işçilərin hovuzunu saxlamaq üçün istifadə etdik tamponlanmış kanal, gözlənilmədən "ölü" işçiləri hovuzdan çıxarmaq üçün səhvləri və işçilərin vəziyyətini izləmək üçün bir mexanizm əlavə etdik.

Nəticədə ikili formada təqdim olunan istənilən sorğuları emal edə bilən işləyən PHP serveri əldə etdik.

Tətbiqimizin veb server kimi işləməyə başlaması üçün hər hansı daxil olan HTTP sorğularını təmsil etmək üçün etibarlı PHP standartı seçməli olduq. Bizim vəziyyətimizdə sadəcə çevirmək Go formatından net/http sorğusu PSR-7belə ki, o, bu gün mövcud olan PHP çərçivələrinin əksəriyyəti ilə uyğun gəlir.

PSR-7 dəyişməz hesab edildiyi üçün (bəziləri texniki cəhətdən belə olmadığını söyləyir), tərtibatçılar sorğuya prinsipcə qlobal bir varlıq kimi baxmayan proqramlar yazmalıdırlar. Bu, uzunömürlü PHP prosesləri konsepsiyasına çox uyğun gəlir. Hələ adı açıqlanmayan son tətbiqimiz belə görünürdü:

RoadRunner: PHP ölmək üçün qurulmayıb, ya da Qolanq xilas etmək üçün

RoadRunner təqdimatı - yüksək performanslı PHP proqram serveri

İlk test tapşırığımız vaxtaşırı gözlənilməz sorğularla (adi haldan daha tez-tez) partlayan API backend idi. Əksər hallarda nginx kifayət qədər olsa da, gözlənilən yük artımı üçün sistemi kifayət qədər tez tarazlaya bilmədiyimiz üçün müntəzəm olaraq 502 səhvlə qarşılaşdıq.

Bu həlli əvəz etmək üçün biz ilk PHP/Go proqram serverimizi 2018-ci ilin əvvəlində yerləşdirdik. Və dərhal inanılmaz effekt əldə etdi! Biz nəinki 502 səhvindən tamamilə qurtula bildik, həm də mühəndislər və məhsul menecerləri üçün çoxlu pula və baş ağrısı həblərinə qənaət edərək serverlərin sayını üçdə iki azalda bildik.

İlin ortasına qədər biz həllimizi təkmilləşdirdik, onu MIT lisenziyası ilə GitHub-da dərc etdik və adını verdik. ROADRUNNER, bununla da onun inanılmaz sürətini və səmərəliliyini vurğulayır.

RoadRunner inkişaf yığınınızı necə təkmilləşdirə bilər

Tətbiq ROADRUNNER sorğu PHP-ə çatmazdan əvvəl JWT yoxlamasını yerinə yetirmək, həmçinin Prometheus-da qlobal olaraq WebSockets və ümumi vəziyyəti idarə etmək üçün Go tərəfində Middleware net/http-dən istifadə etməyə imkan verdi.

Daxili RPC sayəsində siz PHP üçün istənilən Go kitabxanalarının API-ni genişləndirmə sarğıları yazmadan aça bilərsiniz. Daha da əhəmiyyətlisi, RoadRunner ilə siz yeni HTTP olmayan serverləri yerləşdirə bilərsiniz. Nümunələrə PHP-də işləyən işləyiciləri göstərmək olar AWS Lambda, etibarlı növbə kəsiciləri yaratmaq və hətta əlavə etmək gRPC tətbiqlərimizə.

PHP və Go icmalarının köməyi ilə biz həllin dayanıqlığını yaxşılaşdırdıq, bəzi testlərdə tətbiq performansını 40 dəfəyə qədər artırdıq, sazlama alətlərini təkmilləşdirdik, Symfony çərçivəsi ilə inteqrasiyanı həyata keçirdik və HTTPS, HTTP/2, plaginlər və PSR-17.

Nəticə

Bəzi insanlar hələ də köhnəlmiş PHP anlayışına qapılırlar ki, yavaş, çətin dildir, ancaq WordPress üçün plaginlər yazmaq üçün əlverişlidir. Bu insanlar hətta deyə bilər ki, PHP-nin belə bir məhdudiyyəti var: proqram kifayət qədər böyük olduqda, daha "yetkin" bir dil seçməli və uzun illər ərzində yığılmış kod bazasını yenidən yazmalısınız.

Bütün bunlara cavab vermək istəyirəm: bir daha düşünün. Biz inanırıq ki, PHP üçün hər hansı məhdudiyyəti yalnız siz təyin edirsiniz. Bütün həyatınızı bir dildən digərinə keçidlə keçirə, ehtiyaclarınız üçün mükəmməl uyğunluğu tapmağa çalışa və ya dilləri alət kimi düşünməyə başlaya bilərsiniz. PHP kimi bir dilin ehtimal edilən qüsurları əslində onun uğurunun səbəbi ola bilər. Və onu Go kimi başqa bir dillə birləşdirsəniz, hər hansı bir dildən istifadə etməklə məhdudlaşdığınızdan daha güclü məhsullar yaradacaqsınız.

Bir dəstə Go və PHP ilə işləmişik, deyə bilərik ki, biz onları sevirik. Biz birini digəri üçün qurban verməyi planlaşdırmırıq - əksinə, bu ikili yığından daha çox dəyər əldə etməyin yollarını axtaracağıq.

UPD: RoadRunner yaradıcısını və orijinal məqalənin həmmüəllifini salamlayırıq - Lachesis

Mənbə: www.habr.com

Добавить комментарий