RoadRunner: PHP ölmek veya Golang kurtarmak için yapılmadı

RoadRunner: PHP ölmek veya Golang kurtarmak için yapılmadı

Ey Habr! Badoo'da aktifiz PHP performansı üzerinde çalışmak, çünkü bu dilde oldukça büyük bir sistemimiz var ve performans sorunu para tasarrufu sorunu. On yılı aşkın bir süre önce, bunun için ilk başta PHP için bir dizi yama olan ve daha sonra resmi dağıtıma giren PHP-FPM'yi yarattık.

Son yıllarda PHP büyük ilerleme kaydetti: çöp toplayıcı gelişti, kararlılık düzeyi arttı - bugün PHP'de arka plan programları ve uzun ömürlü betikler sorunsuz bir şekilde yazabilirsiniz. Bu, Spiral Scout'un daha da ileri gitmesine izin verdi: RoadRunner, PHP-FPM'den farklı olarak, istekler arasında belleği temizlemez, bu da ek bir performans kazancı sağlar (bu yaklaşım geliştirme sürecini karmaşıklaştırsa da). Şu anda bu aracı deniyoruz, ancak henüz paylaşacak bir sonucumuz yok. Onları beklemeyi daha eğlenceli hale getirmek için, Spiral Scout'tan RoadRunner duyurusunun çevirisini yayınlıyoruz.

Makaledeki yaklaşım bize yakın: sorunlarımızı çözerken, çoğu zaman bir grup PHP ve Go kullanıyoruz, her iki dilin de avantajlarından yararlanıyoruz ve birini diğerinin lehine terk etmiyoruz.

Keyfini çıkarın!

Son on yılda listeden şirketler için uygulamalar oluşturduk. Fortune 500ve 500'den fazla kullanıcısı olmayan bir hedef kitleye sahip işletmeler için. Bunca zaman boyunca, mühendislerimiz arka ucu ağırlıklı olarak PHP'de geliştiriyor. Ancak iki yıl önce, bir şeyin yalnızca ürünlerimizin performansı üzerinde değil, aynı zamanda ölçeklenebilirlikleri üzerinde de büyük etkisi oldu - Golang'ı (Go) teknoloji grubumuza dahil ettik.

Neredeyse anında, Go'nun 40 kata kadar performans iyileştirmesiyle daha büyük uygulamalar geliştirmemize izin verdiğini keşfettik. Bununla birlikte, mevcut PHP ürünlerimizi her iki dilin faydalarını birleştirerek geliştirerek genişletebildik.

Go ve PHP kombinasyonunun gerçek geliştirme problemlerini çözmeye nasıl yardımcı olduğunu ve bizim için nasıl bazı problemlerden kurtulabilecek bir araca dönüştüğünü anlatacağız. PHP ölen model.

Günlük PHP geliştirme ortamınız

PHP ölmekte olan modeli canlandırmak için Go'yu nasıl kullanabileceğinizden bahsetmeden önce, varsayılan PHP geliştirme ortamınıza bir göz atalım.

Çoğu durumda, uygulamanızı nginx web sunucusu ile PHP-FPM sunucusunun bir kombinasyonunu kullanarak çalıştırırsınız. İlki, statik dosyalara hizmet eder ve belirli istekleri PHP-FPM'ye yönlendirirken, PHP-FPM'nin kendisi PHP kodunu yürütür. Apache ve mod_php'nin daha az popüler olan kombinasyonunu kullanıyor olabilirsiniz. Ama biraz farklı çalışsa da prensipler aynı.

PHP-FPM'nin uygulama kodunu nasıl yürüttüğüne bir göz atalım. Bir istek geldiğinde, PHP-FPM bir PHP alt sürecini başlatır ve isteğin ayrıntılarını durumunun bir parçası olarak iletir (_GET, _POST, _SERVER, vb.).

Bir PHP betiğinin yürütülmesi sırasında durum değişemez, bu nedenle yeni bir girdi verisi seti almanın tek yolu, işlem belleğini temizleyip yeniden başlatmaktır.

Bu yürütme modelinin birçok avantajı vardır. Bellek tüketimi konusunda çok fazla endişelenmenize gerek yok, tüm işlemler tamamen izole edilmiştir ve bunlardan biri "ölürse" otomatik olarak yeniden oluşturulur ve geri kalan işlemleri etkilemez. Ancak bu yaklaşımın, uygulamayı ölçeklendirmeye çalışırken ortaya çıkan dezavantajları da vardır.

Düzenli Bir PHP Ortamının Dezavantajları ve Verimsizlikleri

Profesyonel bir PHP geliştiricisiyseniz, çerçeve seçimi ile yeni bir projeye nereden başlayacağınızı bilirsiniz. Bağımlılık ekleme kitaplıklarından, ORM'lerden, çevirilerden ve şablonlardan oluşur. Ve elbette, tüm kullanıcı girdileri rahatlıkla tek bir nesneye (Symfony/HttpFoundation veya PSR-7) yerleştirilebilir. Çerçeveler harika!

Ama her şeyin bir bedeli vardır. Herhangi bir kurumsal düzeyde çerçevede, basit bir kullanıcı talebini veya bir veritabanına erişimi işlemek için en az düzinelerce dosya yüklemeniz, çok sayıda sınıf oluşturmanız ve birkaç yapılandırmayı ayrıştırmanız gerekir. Ancak en kötüsü, her görevi tamamladıktan sonra her şeyi sıfırlamanız ve baştan başlamanız gerekecek: az önce başlattığınız tüm kodlar işe yaramaz hale geliyor, onun yardımıyla artık başka bir isteği işlemeyeceksiniz. Bunu başka bir dilde yazan herhangi bir programcıya söyleyin, yüzünde şaşkınlık göreceksiniz.

PHP mühendisleri, akıllı tembel yükleme teknikleri, mikro çerçeveler, optimize edilmiş kitaplıklar, önbellek vb. kullanarak yıllardır bu sorunu çözmenin yollarını arıyorlar. (Çevirmenin notu: Bu sorun kısmen de olsa çözülecektir. preload PHP 7.4'te)

Go ile PHP birden fazla istekten kurtulabilir mi?

Birkaç dakikadan uzun (saatlere veya günlere kadar) yaşayan PHP betikleri yazmak mümkündür: örneğin, cron görevleri, CSV ayrıştırıcıları, sıra kesiciler. Hepsi aynı senaryoya göre çalışır: bir görevi alırlar, yürütürler ve bir sonrakini beklerler. Kod her zaman bellekte kalır ve çerçeveyi ve uygulamayı yüklemek için gereken birçok ek adım olduğundan değerli milisaniyelerden tasarruf sağlar.

Ancak uzun ömürlü komut dosyaları geliştirmek kolay değildir. Herhangi bir hata süreci tamamen öldürür, bellek sızıntılarını teşhis etmek çileden çıkarıcıdır ve F5 hata ayıklaması artık mümkün değildir.

PHP 7'nin piyasaya sürülmesiyle durum düzeldi: güvenilir bir çöp toplayıcı ortaya çıktı, hataları işlemek daha kolay hale geldi ve çekirdek uzantıları artık sızdırmaz. Doğru, mühendislerin hala bellek konusunda dikkatli olmaları ve koddaki durum sorunlarının farkında olmaları gerekiyor (bunları görmezden gelebilecek bir dil var mı?). Yine de, PHP 7'nin bizi bekleyen daha az sürprizi var.

Uzun ömürlü PHP betikleriyle çalışma modelini alıp HTTP isteklerini işlemek gibi daha önemsiz görevlere uyarlamak ve böylece her istekte her şeyi sıfırdan yükleme ihtiyacından kurtulmak mümkün mü?

Bu sorunu çözmek için, öncelikle HTTP isteklerini kabul edebilen ve her seferinde öldürmeden bunları PHP çalışanına birer birer yönlendirebilen bir sunucu uygulaması uygulamamız gerekiyordu.

Saf PHP (PHP-PM) veya bir C uzantısı (Swoole) kullanarak bir web sunucusu yazabileceğimizi biliyorduk. Ve her yöntemin kendine göre avantajları olmasına rağmen, her iki seçenek de bize uymadı - daha fazlasını istedik. Bir web sunucusundan daha fazlasına ihtiyacımız vardı - bizi PHP'deki "zor başlangıç" ile ilgili sorunlardan kurtarabilecek, aynı zamanda belirli uygulamalar için kolayca uyarlanıp genişletilebilecek bir çözüm bulmayı bekliyorduk. Yani bir uygulama sunucusuna ihtiyacımız vardı.

Go bu konuda yardımcı olabilir mi? Yapabileceğini biliyorduk çünkü dil uygulamaları tek ikili dosyalarda derliyor; çapraz platformdur; HTTP ile çalışmak için kendi çok zarif paralel işleme modelini (eşzamanlılık) ve bir kitaplığı kullanır; ve son olarak, binlerce açık kaynaklı kitaplık ve entegrasyon bize sunulacak.

İki Programlama Dilini Birleştirmenin Zorlukları

Öncelikle iki veya daha fazla uygulamanın birbiriyle nasıl haberleşeceğinin belirlenmesi gerekiyordu.

Örneğin, kullanarak mükemmel kütüphane Alex Palaestras, PHP ve Go işlemleri arasında bellek paylaşmak mümkündü (Apache'deki mod_php'ye benzer). Ancak bu kitaplık, sorunumuzu çözmek için kullanımını sınırlayan özelliklere sahiptir.

Farklı, daha yaygın bir yaklaşım kullanmaya karar verdik: soketler / işlem hatları aracılığıyla süreçler arasında etkileşim oluşturmak. Bu yaklaşımın güvenilir olduğu son yıllarda kanıtlanmıştır ve işletim sistemi düzeyinde iyi bir şekilde optimize edilmiştir.

Başlangıç ​​olarak, süreçler arasında veri alışverişi yapmak ve iletim hatalarını işlemek için basit bir ikili protokol oluşturduk. En basit haliyle, bu tür bir protokol şuna benzer: ağ dizisi с sabit boyutlu paket başlığı (bizim durumumuzda 17 bayt), paket türü, boyutu ve verilerin bütünlüğünü kontrol etmek için bir ikili maske hakkında bilgi içerir.

Kullandığımız PHP tarafında paket işlevi, ve Git tarafında kitaplık kodlama/ikili.

Bize bir protokolün yeterli olmadığı görüldü - ve arama yeteneğini ekledik net/rpc go hizmetleri doğrudan PHP'den. Daha sonra, Go kitaplıklarını PHP uygulamalarına kolayca entegre edebildiğimiz için bu, geliştirmede bize çok yardımcı oldu. Bu çalışmanın sonucu, örneğin diğer açık kaynaklı ürünümüzde görülebilir. Goridge.

Görevleri birden çok PHP çalışanına dağıtma

Etkileşim mekanizmasını hayata geçirdikten sonra, görevleri PHP süreçlerine aktarmanın en verimli yolunu düşünmeye başladık. Bir görev geldiğinde, uygulama sunucusu onu yürütmek için serbest bir çalışan seçmelidir. Bir işçi/işlem bir hata ile çıkarsa veya "ölürse" ondan kurtulur ve onun yerine yenisini yaratırız. İşçi/süreç başarılı bir şekilde tamamlandıysa, onu görevleri gerçekleştirmek için uygun çalışan havuzuna geri göndeririz.

RoadRunner: PHP ölmek veya Golang kurtarmak için yapılmadı

Aktif çalışan havuzunu depolamak için kullandık ara belleğe alınmış kanal, beklenmedik bir şekilde "ölü" çalışanları havuzdan çıkarmak için, çalışanların hatalarını ve durumlarını izlemek için bir mekanizma ekledik.

Sonuç olarak, ikili formda sunulan tüm istekleri işleyebilen çalışan bir PHP sunucumuz var.

Uygulamamızın bir web sunucusu olarak çalışmaya başlaması için, gelen HTTP isteklerini temsil edecek güvenilir bir PHP standardı seçmemiz gerekiyordu. Bizim durumumuzda, biz sadece dönüştürmek Biçime git'ten net/http isteği PSR-7Böylece bugün mevcut olan PHP çerçevelerinin çoğuyla uyumludur.

PSR-7 değişmez kabul edildiğinden (bazıları teknik olarak öyle olmadığını söyler), geliştiricilerin, isteği ilke olarak küresel bir varlık olarak ele almayan uygulamalar yazmaları gerekir. Bu, uzun ömürlü PHP süreçleri kavramına çok iyi uyuyor. Henüz isimlendirilmemiş olan son uygulamamız şuna benziyordu:

RoadRunner: PHP ölmek veya Golang kurtarmak için yapılmadı

RoadRunner Tanıtımı - yüksek performanslı PHP uygulama sunucusu

İlk test görevimiz, periyodik olarak öngörülemeyen isteklerde (normalden çok daha sık) patlayan bir API arka ucuydu. Çoğu durumda nginx yeterli olsa da, beklenen yük artışı için sistemi yeterince hızlı dengeleyemediğimiz için düzenli olarak 502 hatasıyla karşılaştık.

Bu çözümü değiştirmek için ilk PHP/Go uygulama sunucumuzu 2018'in başlarında konuşlandırdık. Ve hemen inanılmaz bir etki elde etti! 502 hatasından tamamen kurtulmakla kalmadık, aynı zamanda sunucu sayısını üçte iki oranında azaltabildik, mühendisler ve ürün yöneticileri için çok fazla para ve baş ağrısından tasarruf ettik.

Yıl ortasına kadar çözümümüzü geliştirdik, GitHub'da MIT lisansı altında yayınladık ve adını verdik. RoadRunner, böylece inanılmaz hızını ve verimliliğini vurguluyor.

RoadRunner geliştirme yığınınızı nasıl geliştirebilir?

Uygulama RoadRunner istek PHP'ye ulaşmadan önce JWT doğrulaması gerçekleştirmek için Git tarafında Middleware net/http kullanmamıza ve ayrıca Prometheus'ta WebSockets ve toplu durumu küresel olarak işlememize izin verdi.

Yerleşik RPC sayesinde, PHP için herhangi bir Go kitaplığının API'sini uzantı sarmalayıcıları yazmadan açabilirsiniz. Daha da önemlisi, RoadRunner ile HTTP olmayan yeni sunucular kurabilirsiniz. Örnekler, PHP'de çalışan işleyicileri içerir AWS Lambda, güvenilir sıra kesiciler oluşturma ve hatta ekleme gRPC uygulamalarımıza.

PHP ve Go topluluklarının yardımıyla çözümün kararlılığını iyileştirdik, bazı testlerde uygulama performansını 40 kata kadar artırdık, hata ayıklama araçlarını geliştirdik, Symfony çerçevesiyle entegrasyonu uyguladık ve HTTPS, HTTP/2, eklentiler ve PSR-17 için destek ekledik.

Sonuç

Bazı insanlar hala PHP'nin yalnızca WordPress için eklenti yazmak için iyi olan yavaş, hantal bir dil olduğu şeklindeki modası geçmiş kavramına yakalanmış durumda. Hatta bu kişiler PHP'nin böyle bir sınırlaması olduğunu bile söyleyebilirler: uygulama yeterince büyüdüğünde, daha "olgun" bir dil seçmeniz ve yıllar boyunca biriken kod tabanını yeniden yazmanız gerekir.

Bütün bunlara cevap vermek istiyorum: tekrar düşünün. PHP için kısıtlamaları yalnızca sizin belirlediğinize inanıyoruz. Tüm hayatınızı bir dilden diğerine geçiş yaparak, ihtiyaçlarınıza en uygun eşleşmeyi bulmaya çalışarak geçirebilir veya dilleri bir araç olarak düşünmeye başlayabilirsiniz. PHP gibi bir dilin sözde kusurları, aslında başarısının nedeni olabilir. Ve bunu Go gibi başka bir dille birleştirirseniz, herhangi bir dili kullanmakla sınırlı olduğunuz duruma göre çok daha güçlü ürünler yaratacaksınız.

Bir grup Go ve PHP ile çalıştıktan sonra onları sevdiğimizi söyleyebiliriz. Birini diğeri için feda etmeyi planlamıyoruz - aksine bu ikili yığından daha da fazla değer elde etmenin yollarını arayacağız.

UPD: RoadRunner'ın yaratıcısını ve orijinal makalenin ortak yazarını memnuniyetle karşılıyoruz - Lachesis

Kaynak: habr.com

Yorum ekle