mylmz10 yazdıÖncelikle merhabalar.
PHP Framework'ler hakkında araştırma yaparken laravel konusu üzerinde biraz durayım istedim. Sonucunda kendimi bu sayfada buldum ve bir teşekkür edebilmek için hemen foruma kayıt oldum.
@pellempus ve @Aristona rumuzlu arkadaşlara teşekkürlerimi iletiyorum. Ufak bir atışma havasında geçen sohbette çok güzel bilgiler verdiklerinden kaynaklı ağızlarına sağlık.
İyi çalışmalar diliyorum hepinize :)
Teşekkürler, keşke bu forum daha aktif olsa, birçok konuyu detaylıca tartışabilsek.
Bunu şimdi gördüm. Yanlış bir algıya sebebiyet vermemek için ufak bir yorum yapmak istiyorum.
Varnish, konfigüre edildiğinde, bir sayfanın cevabını (response) hafızasında tutup, diğer kullanıcılar istek yaptığında direkt olarak hafızadaki veriyi döndüren bir araç. Sayfaya giren ilk kullanıcı, cache yoksa haberleri PHP/Laravel ile çekiyor. Ondan sonra sayfaya girenler o hafızadaki veriyi görüyorlar. PHP'ye istek gelmiyor. İstek gelmediği için de CPU/MySQL vb. bottleneck olmadan çıkıyor. Her istekte sadece RAM'de bulunan bir veriyi okuyup, onu döndürmek inanılmaz derecede hızlı bir olay. Örneğin, bir Laravel uygulaması saniyede 10 hit desteklerken, önüne Varnish koyup herşeyi önbellek üzerinden sunarsanız, muhtemelen Laravel bottleneck olmaktan çıkar. Oraya gelene kadar sunucunun network kapasitesi/işletim sisteminin limitasyonlarına takılmanız çok olası.
Burada dikkat edilmesi gereken nokta şu. Her sayfayı cacheye alamazsınız. Örneğin, giriş yapmış kullanıcıların görebileceği özel sayfalar herkese sunulamaz. Uygulama ne kadar komplex olursa, global olarak cacheye alabileceğin sayfalar azalır. Mesela, bu haber sitesi, gelen kullanıcının lokasyonunu alıp, ona o şehirdeki haberleri gösterebilir. Bu durumda Kastamonu'dan siteye giren ben, cacheli İstanbul haberlerini görmemeliyim. Kastamonu'dan gelene, Kastamonu cachesi gösterilmeli. Aynı şekilde, bu site giriş yapmış kullanıcılara "Takip ettiği haber kaynaklarından" reklam gösterebilir. Bu durum daha özel. O adama global bir cache gösteremezsiniz. İstek bir defa Laravel'e gelir, orada takip ettiği haber kaynaklarına göre haberler çekilir. Her sayfa yenilemede aynı şeyleri göstermemek adına, belli bir süreliğine (örn 5 dk) veya cachenin yenilenmesini gerektiren olaylar olana kadar (örn takip ettiği bir kanala yeni bir haber geldi) o sayfayı o kullanıcıya özel cachelersiniz.
Burada Laravel'in nasıl scale olacağından çok, uygulamanın ne kadar komplike olduğunu ve ne tür cacheler oluşturabileceğimizi düşünmeliyiz.
Elimizde ne var? Bir haber sitesi. Bir haber sitesine genellikle giriş yapmamış (guest) kullanıcılar girer. Anasayfada haberleri görürler, birkaç tanesine tıklarlar ve çıkarlar. Girenlerin %90'u guest desek ve lokasyon bazlı targetleme olmasa; o zaman yaklaşık olarak şöyle bir çıkarım yapabilirim: "Bu sayfanın aldığı 10M sayfa gösteriminin, %90'ı sitenin anasayfasına ve haber detaylarına gidiyor." Bu sayfaları bir şekilde önbellekte tutup, Varnish üzerinden sunabilirsem; o zaman Laravel'e 10M istek yerine, sadece 1M + 1 (anasayfa) + N (haber detay sayısı) kadar istek gelir. Buradaki N fazla olmayacağı için (sitenizde yüzbinlerce haber yoksa), o zaman Laravel'e yaklaşık 1M hit gelir. Giriş yapmış kullanıcılara, yorumlara, yönetici panellerine falan hiç cache/optimizasyon farzedelim.
1.000.000 günlük pageview = saniyede 11.5 pageview'e denk geliyor ortalama. Saniyede 11.5 istek çok düşük bir rakam. Optimize "edilmemiş" NGINX + PHP-FPM (opcache) bile, uyduruk bir laptopta bile Laravel bu kadar hiti kaldırır. Ama uygulamanız farklı olabilir.
1. O sayfalarda kaç tane MySQL sorgusu var? Sorgular ağır mı? MySQL'in cacheleyebileceği şeyler mi? Sonuçları Redis'te cacheliyormusunuz?
2. Uygulamanızın complexitysi ne kadar? Her defasında 1.000.000 değeri olan array ile işlem yapıyormusunuz? İşlemci kullanımı uçuyor mu?
3. Sistemi profile edip yavaş noktaları optimize edebilir misiniz? Komponent bazlı cache yapabiliyormusunuz?
Uygulamanızın yük altında nerede patlayacağını bilemem. Büyük arrayler ile iş yaparsanız, işlemci bottleneck olur. Her defasında optimize edilmemiş onlarca MySQL sorgusu yaparsanız, MySQL patlar. CDN kullanmıyor ve büyük verileri her defasında client tarafında cache ettirmiyorsanız, network IO coşar. Disk üzerinde çok iş yapıyorsanız, disk IO patlar. Her uygulama farklı olduğu için, bottleneck yapacak yerler hep değişiklik gösterir.
Biraz toparlayayım. Haber7'nin 10M pageview desteklemesi, Laravel'in 10M hit kaldıracağı anlamına gelmez. Haber sitelerini cachelemek çok kolay oluyor, sebeplerini yazmıştım. Şuan 9M Varnish, 1M Laravel desek; "anasayfaya" DDoS saldırısı yapılsa ve günlük PV 100M olsa, o zaman 99M Varnish, 1M Laravel olacak ve birşey değişmeyecek. Aynı şekilde, Haber7'nin 1M Laravel hit desteklemesi, senin uygulamanın da destekleyeceği anlamına gelmez. Belki senin 10.000 PV bile desteklemeyecek. Belki Laravel işlemcinin %1'ini anca kullanabiliyor ama senin uygulaman çok ağır, işlemcinin 99%unu yiyor her zaman? Bu durumda Laravel'e birşey diyemeyiz.
Genel olarak şu görüş yaygın. "Dünyanın en yavaş uygulamasını bile yazsan, onu scale edebilirsin."
Çok işlemci gerektiren işlemler mi var? Yüksek işlemci kapasitesine sahip sunucu al, işlemleri kuyruk üzerinden o sunucuda çalıştır.
NGINX ve PHP artık o kadar hiti kaldıramıyor mu? Önüne Varnish koy.
Varnish koydun ama kalan hiler bile NGINX ve PHP'yi öldürüyor mu? Reverse proxy kullan ve yükü birden fazla NGINX/PHP sunucusuna dağıt.
Sorgular seni öldürüyor mu? Redis'te cache tut.
Kodların optimize mi? Blackfire ile profilini çıkar ve yavaş noktaları gör. (%99 sorun bu oluyor)
AB/Blitz ile stres testi yaptığında ne oluyor? İlk ne patlıyor? Onu bul ve optimize et.
Kullandığın bir paket çok mu yavaş? Daha hızlısını bul, veya yazabilirsen daha low level şekilde (raw php) kendin yaz.
Network IO çok mu fazla? CDN'e geçebiliyorsan geç.
Kısacası yapılabilecek tonla iş var. Şuana kadar PHP ile veya Laravel ile yazıldığı için scale olamamış bir uygulama görmedim. Farklı diller ve frameworkler de dahil. 10M 100M hit desteklemek zor değil. Sadece resmi bütün olarak görüp ona göre scale edeceksiniz sisteminizi.
Şunu da unutmayalım. Projende gerçekten Laravel'e ihtiyacın var mı? 1 + 1 'i hesaplatacaksan o zaman Laravel'i yüklemeye gerek yok. Mesela benim şuan üzerinde çalıştığım uygulama Laravel ile yazıldı ama public API kısmını Laravel'den kopardım ve küçük bir microframework ile yazdım. Niye? Çünkü devasa bir istek gelecekti (milyarları konuşuyoruz), gelen istekler her kullanıcıya daima farklı olacaktı ve tek yapacağı iş Redis'e veri yazıp okumak olacaktı. Yani ne Laravel Eloquent, ne Blade, ne Service Providerlar, ne kuyruk sistemi, hiçbiri işime yaramayacaktı.
İlk başta, 160 $'lık sunucuda dakikada 1.000 istek gördüğünde patlıyordu. (Evet, microframework + Redis + ufacık bir uygulama bile patlayabiliyor optimize edilmezse) Konfigürasyonları güncelledim 1.500 destekledi. Algoritmayı düzenleyip her istekte 50 olan Redis komutunu 10'a düşürdüm. Pipe olabilecek işleri pipeye attım. 2500 oldu. Blackfire ile profilerini çıkarttım ve yavaş çalışan noktaları düzelttim. (URL manipülasyon için kullandığım bir paket vardı, verilen URL'den domain buluyordu. Çok büyük regexler döndüğü için, işlemci kullanımı yük altında tavan yapıyordu. Onun yerine parse_url kullandım ve CPU yükü bir anda gitti) Stress testi yaptım, 4000'de Redis hata vermeye başladı. Baktım port limiti var. Onu kaldırdım 12.000 gördü local bilgisayarımda. Belki 50.000 desteklerken tamamen farklı bir sorun çıkacak. Belki port yetmeyecek. Zamanla göreceğiz neresi takılıyor
Şuan microframeworküde bırakmayı düşünüyorum çünkü baktım, sadece pretty URL'ler için kullanmışım. index.php'ye saniyede 10.000 istek gelebilirken, microframeworkün "Hello world" routesine sadece 2500 hit geliyor. Çünkü framework'ün boot aşaması var. Sırf URL'leri pretty göstereceğim diye (ki benim işim için önemsiz) 3 katı hiti bir kenara bırakmalı mıyım? Bence bırakmalıyım.
Şuan sunucuma dakikada 6000 istek geliyor ve işlemci kullanımı 15%, 1 hafta önce 1000 istek ile 100% işlemci kullanımı vardı. Saniyedeki Redis komutu yaklaşık 1500'den 450'ye düştü. Ve bu tek makina. Bunun önüne Load Balancer koyup arkasına 10 tane $ 160 makina alabilirim. Redis tıkanırsa, Redis'i cluster haline döndürüp kullanabilirim. Her defasında farklı bir response dönme zorunluluğu olmasa, şimdiye Varnish'i önüne koyup işlemci kullanımını minimuma indirmiştim. O yüzden haber siteleri gibi siteler için Varnish hayat kurtarıcı.
Bu kadar şey yazdım, ama asıl uygulamam hala Laravel. Neden? Çünkü ana uygulamada Laravel'in neredeyse her özelliğini kullanıyorum. Laravel kullanmasam belki 10 dakikada biten Auth işlemleri için 1 hafta Auth driver yazacaktım. Eventlere Command'ları koyuyorum, Command'lar kuyruğa gidiyor, Business Intelligence denen olay için inanılmaz bir nimet. "Anıl bey, birisi şifresini değiştirirse ona email atalım." user.password-update eventini hookla, oradan kuyruğa EmailUserPasswordChangeService sınıfını kullanıcı bilgilerini pass ederek gönder. Kuyruk çalışsın ve kullanıcıya email gitsin.
Laravel kullanmasam, yine composer.json içine en azından 1 tane Auth sistemi, bir ORM, bir Event sistemi, Predis, DI Container vb. koyacaktım ve sonuç olarak custom bir full stack framework elde edecektim. Bunu yaptım diyelim, Laravel bunları birleştirirken birçok optimizasyondan geçiriyor, bazı şeyler lazy çalışıyor vb. Oluşturduğum frameworkün performansını hep benchmarktan geçirmek zorunda kalacaktım. Bunu yapmak vakit. Testlerini yazmak vakit. Gelecek yazılımcılara iyi bir dökümantasyon yazmak vakit. Onları eğitmek vakit. Çalıştığım şirket bana maaş veriyor. Şirkete katma değer sağlamadığım her saniye birilerinin cebinden çıkıyor benim maaşım. "Ben şu Auth driverini biraz optimize edeceğim." diyemem. Rakip firmalar müşterilere cazip gelen onlarca özellik geliştirirken bunlarla vakit kaybedemeyiz. Şuan projeyi tek başıma geliştiriyorum ama aslımda yanımda binlerce insan var. Taylor Otwell ve ekibi Laravel'imi geliştiriyor. NRK ve ekibi Predis'i geliştiriyor. Antirez ve ekibi Redis'i geliştiriyor. PHP League kullandığım paketleri geliştiriyor. Çinli hackerlar Laravel'de açık buluyor, birisi bunu çözüyor ve composer update ile bunların hepsine sahip oluyorum. Ben sadece "yemeği pişiren" oluyorum. Taylor Otwell tarlada patates çıkartıyor, Antirez bunları sepete koyup eve getiriyor. PHP League bunları temizleyip kesiyor. Ben sıcak yağın içine bırakıyorum ve müşterilerimize patates kızartması satıyoruz
Açık kaynağın güzelliği bu.
Laravel'e gerçekten ihtiyacın yoksa (benim API sunucum gibi) o zaman yazma. Sonuçta gereksinimlerini en iyi sen biliyorsun. Laravel'in kaldırmadığı bir duruma geleni görmedim, ama kendi hatasından dolayı Laravel'e ve frameworklere laf atanı çok gördüm. Her sayfada 100 tane sorgu var, opcachesi bile açık değil, ama yavaş olan Laravel oluyor nedense. Genelde tecrübesizlerin/yeni başlayanların düştükleri çukurlardan birisi bu malesef.