Sunucu Yükü ve Bant Genişliği Optimizasyonu
Bant genişliği için para ödüyoruz ki çoğundan kurtulabilir para ve hız kazanabiliriz. Aynısı sunucu için de geçerli. İşlemci yükünü çok kolay bir biçimde rahatlatarak sürekli sunucu güncellemekten veya daha güçlü sunuculara binlerce lira ödemekten sıyrılabiliriz.
Peki nasıl olacak bu? Çok basit; milyonlarca yıldır işleyen evrimi taklit ederek. Parazitlerden kurtularak… Sunucumuza patır patır gelen robot web-spider isteklerine “erkekseniz teker teker gelin” diyeceğiz, hepsi o.
Sunucu kayıtlarını inceleyen herkesin bildiği gibi kimi anlarda çok yoğun bot etkinliği görülür. Üstelik bunların sayısı giderek arttığı için artık aynı anda istekte bulunup çakışmalara ve sunucu yükünde ani aşırı artışlara yol açıyorlar.
Bu küçümsememiz gereken bir durum çünkü bize para ziyaretçilere de zaman kaybettiriyor. Artık çoğu web sitesi / web uygulaması yoğun SQL sorguları içeriyor. Gerçek bir yarar getirmeyecek arama-motoru / istatistik / telif / spam / antivirüs / reklam robot ziyaretleri ise hızlı, ardışık ve üst üste geldiklerinde ziyaretçileri engelleyebilecek düzeyde bir sunucu yoğunluğuna sebep oluyor. Oysa basit bir trafik lambası ile bunlara kırmızı ışık yakmamız mümkün.
Aşağıda kolaylıkla kullanabileceğiniz iki işlev (function) bulabilirsiniz. Hemen hemen her türlü PHP sayfasının başında kullanabilirsiniz. Sonrasında ise en aşağıda ufak bir robots.txt ipucu var. Bunların bileşimiyle ben pek çok sunucuda epeyce rahatlama sağlayabildim. Sizin de sunucu yükünüzü ve bant-genişliğinizi epeyce hafifletecektir.
Kullanacağımız Yoklama, Hızlandırma ve Engelleme İşlevleri
Önce sistem yükünü öğrenelim. İşte loadavg() işlevimiz:
<?php function loadavg() { $buffer = "0 0 0"; $f = fopen("/proc/loadavg","rb"); if (!feof($f)) { $buffer = fgets($f, 1024); } fclose($f); $load = explode(" ",$buffer); return max((float)$load[0], (float)$load[1], (float)$load[2]); } ?>
Bunu loadavg() diye çağırdığınızda linux sistemlerde /proc altındaki loadaverage’ı okuyup en büyük değeri döndürüyor. İlkini, yani o sıradaki yükü değil de uzun dönem ortalama bile olsa en büyüğünü istememiz sunucuyu ardışık ani yüklenmelerden korumak için. İlkini kullansaydık sistem yükünün bizim eşik değerimiz etrafında bir tür osilasyona girdiğini görebilirdik. Bizse robotlara dur-kalk yaptırmak değil ani yüklenmelerde sunucu yükünü dengeli bir azami değerde tutmak istiyoruz.
Gelelim hızlandırma görevi yapan ikinci işlevimize. Bu işlev PHP içinde normalde kullanılamayan GET_IF_MODIFIED_SINCE türü HTTP çağrılarını etkin bir biçimde kullanabilmek için. Web tarayıcıları (Firefox, Opera, Chrome, Internet Explorer, vs.) ile proxy’ler önbelleklerinde bir sayfa varsa bunun tazeliğini GET_IF_MODIFIED_SINCE çağrıları ile yoklarlar. Önbelleklerindeki sayfanın daha önce sunucu tarafından verilmiş son_değiştirilme (last_modified) tarihini bu çağrı ile birlikte sunucuya sorarak “eğer bu tarihten daha yeni ise elindekini gönder bakalım ey sunucu” derler. Sunucu ise eğer bir değişiklik yoksa “304 Not Modified” yanıtını döndürür. Web tarayıcı veya proxy bu yanıtı aldığında ise doğrudan ön bellekteki sayfayı kullanır ve sayfayı indirmekle zaman kaybetmez.
Ancak PHP scriptlerinde ve benzeri diğer CGI dinamik sayfalarda sunucular last_modified bilgisi göndermez, gönderemez veya ayarlandıysa sadace o anki zamanı gönderir. Oysa veri tabanında bir yenilik yoksa o PHP scripti her seferinde aynı sayfayı üretiyor olabilir. Boşu boşuna her seferinde yeniden çalışacak, onca SQL sorgusu yapacak, disk kafasını haldır haldır oradan oraya koşturup, işlemcinin önbelleğinin, işletim sisteminin de context-switch’inin başını ağrıtacaktır. Gereksiz…
Aşağıdaki script işlevi ile PHP scriptlerimizin başında last_modified ve expires bilgileri üretip, GET_IF_MODIFIED_SINCE isteklerini uygun bir dille yanıtlayabiliriz.
Bu işlev kendisine verilen parametre kadar bir zaman boşluğunca PHP scriptinin değişmediğini bildiriyor. Parametre olarak 2-30 arası değerler dakika cinsinden içeriğin aynı olacağının bildirildiği süre. 1 değeri ise özel durum ve 1 gün anlamında. Bu dinamik bir script için uzun bir süre. Yalnızca 1 gün içinde sayfanın değişmeyeceğinin emin olduğunuz ya da 1 günlük ön belleklemenin sorun yaratmayacağı durumlarda kullanmak için.
<?php function hizlan($miktar) { $agent=$_SERVER['HTTP_USER_AGENT']; if ( $miktar == 1 || preg_match("/bot/i",$agent) || preg_match("/Slurp/i",$agent) || preg_match("/Twiceler/i",$agent) ) { $fake_last_modified = gmdate('D, d M Y ', (time())) . "00:00:00 GMT"; $fake_expires = gmdate('D, d M Y ', (time())) . "23:59:59 GMT"; } else { if ( $miktar < 2 ) $miktar = 2; if ( $miktar > 30 ) $miktar = 30; $saat=floor(gmdate('i',(time() - 00))/$miktar)*$miktar; if ($saat == "0" ) $saat="00"; $sonsaat=floor(gmdate('i',(time() - 00))/$miktar)*$miktar+$miktar; if ($sonsaat >= 60 ) $sonsaat="59"; $fake_last_modified = gmdate('D, d M Y H:', (time())); $fake_last_modified .= $saat.":00 GMT"; $fake_expires = gmdate('D, d M Y H:', (time())); $fake_expires .= $sonsaat.":00 GMT"; } if ( getenv("HTTP_IF_MODIFIED_SINCE") == $fake_last_modified ) { header("HTTP/1.1 304 Not Modified"); exit; } header('Last-Modified: '.$fake_last_modified); header('Expires: '.$fake_expires); } ?>
İşlev verilen dakika miktarına bakarak yalancı bir son değiştirilme tarihi üretip, o anki çağrı normal bir çağrı ise Last-Modified HTTP başlığı ile bunu bildiriyor. Ayrıca bir de aynı süre kadar Expires başlığı üretiyor. Bu arada bu başlıkları üretip gönderdiği için bu işlevler bir web sayfası PHP scriptinin en başında çağrılmalı. Eğer çağrı bir GET-IMS çağrısı ise çağrı tarafından verilen HTTP_IF_MODIFIED_SINCE değerinin kendisinin üretmiş olduğu last_modified ile uyuşup uyuşmadığına bakıyor. Aynıysa HTTP/1.1 304 Not Modified başlığını göndererek scriptten çıkıyor. Evet sayfayı bir daha ürettirmiyor.
Yalancı son_değiştirme tarihi ise sizin verdiğiniz sürenin en yakınındaki bir önceki tam katı döndürülüyor. Yani örneğin o an saat 12:21 ise ve siz dakika miktarı olarak 5 verdi iseniz, last_modified olarak 12:20, expires olarak da 12:25 döndürüyor. Bu arada dikkat ettiyseniz ortalama tazelik süresi aslında sizin verdiğinin dakika miktarının yarısı kadar. Eğer bir çağrı 12:24’te geldiyse 1 dakika sonra süre doluyor. 12:21’de ise 4 dakika sonra. Yani 5 dakika diyerek ortalamada ziyaretçilere son 2.5 dakikalık içeriği sunuyoruz.
Bir forum siteniz varsa, veya bir sosyal imleme veya benzeri bir toplulukça oluşturulan içeriğe sahip bir siteniz varsa, bu hızlandırma tekniği ziyaretçilerin sayfadan sayfaya dolaşmalarını epeyce hızlandıracaktır. Özellikle böyle sitelerde kullanıcılar oradan oraya tıkladıklarından hızlanma inanılmaz. Hatta sunucu yüküne göre bakarak bu süreyi uzatırsanız çok daha hızlı yanıt veren bir siteniz olacaktır.
Bu arada fonksiyona eğer miktar olarak 1 değeri verildiyse, sayfanın gece yarısı 00:00’da güncellendiğini ve gün sonu 23:59’a kadar da güncel olacağını söyleyecektir. Kimi durumlarda bu oldukça yararlıdır. Bu işlev bu süreleri siz 1 değeri vermeseniz dahi user-agent bilgisinden saptadığı robotlara söylüyor ayrıca. Bu yolla kimi çılgın robotlarla sayfa zararlı mı diye her seferinde sayfayı indirip yoklamaya çalışan anti-virüs programlarına destur çekmiş oluyoruz.
PHP Hızlandırma İşlevinin Kullanım Örneği
İlk verdiğimiz sistem yükü işlevi ile birlikte kullanırsak ve işlevleri include klasörü altına örnekteki dosya adlarıyla kaydettiğimizi varsayarsak:
<php // Sayfanın en başı include("include/function.loadavg.php"); include("include/function.hizlan.php"); $sistemyuku=loadavg(); if ( $sistemyuku > 2 ) { hizlan(30); } else if ( $sistemyuku > 1 ) { hizlan(10); } else { hizlan(5); } //... //... sayfanın geri kalan PHP scripti de bundan sonraya
Bu örnekte eğer sistem yükü 2’den çoksa ortalama 15dk, 1-2 arasındaysa ortalama 5 dakika, 0-1 arasındaysa da ortalama 2.5dk boyunca sayfa değişmiyor bilgisini verdik. Bu örnek çoğu dinamik site için kullanılabilir. Tabii bir borsa veya benzeri bir site, hızlı güncellenen bir haber ajansı sitesi için 5-6 dakikayı geçmenizi önermek olası değil.
Ancak kendi sitenizin durumuna / özelliğine göre bu değerleri arttırarak verimliliğini arttırmanız mümkün; “varsın insanlar son 5-10 dakikalık içeriği biraz geç görsün” diyebileceğiniz durumlar için…
Uzun bekleme süreleri içinse yüke bakmanıza pek gerek yok. Sadece bir scriptin başında:
<php include("include/function.loadavg.php"); include("include/function.hizlan.php"); hizlan(30); // veya 30dk değil tüm gün değişmeyecekse hizlan(1)
dememiz yeterli olacaktır.
Aşırı Yükte Robot Örümcek Durdurma
Şimdi de sıra sitemize yüklenen web-spider’ları geçici olarak durdurmakta. Önce aşağıdaki scripti include/robotdurdur.php gibi bir adla kaydedelim:
<?php // Zamana bağlı olsun mu? $zamanli=false; // preg_match işlevini güçlendirip array desteği verelim: function preg_search($ary, $subj) { $matched = false; if (is_array($ary)) { foreach ($ary as $v) { if (preg_match($v, $subj)) $matched = true; } } else { if (preg_match($ary, $subj) ) $matched = true; } return $matched; } // IP bulalım: if ( $_SERVER['HTTP_X_FORWARDED_FOR'] ) { $ip=$_SERVER['HTTP_X_FORWARDED_FOR']; } else { $ip=$_SERVER['REMOTE_ADDR']; } $ipp=explode(" ",$ip); if ( $ipp[count($ipp)-1] != "" ) $ip=str_replace(",","",$ipp[0]); $agent=$_SERVER['HTTP_USER_AGENT']; // Belli başlı web-spider'lar (günceli klavyemonitor.com'da): $needle_sr=array( "/Spider/i","/Yandex/i","/Crawler/i","/Nutch/i", "/ScoutJet/i","/Charlotte/i","/Twiceler/i","/bot/i", "/spider/i","/fastsearch/i","/ia_archiver/i","/Lsearch/i", "/searchme/i","/Slurp/","/larbin/i","/Pagebull/i", "/page_verifier/i","/SBIder/i","/GetHTML/i","/StackRambler/i", "/MMCrawler/i","/sphider/i","/Yeti/i","/touche/i" ); // Belli başlı web-spider adresleri: $needle_srip=array( "/38\.99\.44\.103/","/66\.249\.66\./","/66.249.72.170/", "/131\.107\.151\./","/66.249.66.161/","/88.131.153.91/", "/65\.55\.212\./","/65\.54\.188\./","/64\.1\.215\./", "/217.155.75.171/","/78\.165\.228\./" ); // Daha ayrıntılı ve güncel liste klavyemonitor . com'da var. // Eğer $zamanli=true ise bu işlemi yalnızca yoğun saatlerde yapalım: if ( $zamanli ) { $zamansaat=date('G'); if ( $zamansaat > 14 && $zamansaat < 19 ) { if ( preg_search($needle_sr,$agent) || preg_search($needle_srip,$ip) ) { header(' ',true,403); exit; } } } else { if ( preg_search($needle_sr,$agent) || preg_search($needle_srip,$ip) ) { header(' ',true,403); exit; } } ?>
Şimdi burada $zamanli diye bir değişkenimiz var. Bunu doğrudan scriptin içinde tanımlıyoruz. Sitenizin kendine özgü durumuna göre bunun değerine siz karar veriyorsunuz. Eğer sisteme aşırı yük bindiği her durumda robotları durdurmak istiyorsanız $zamanlı=false; olmalı. Eğer sistem yüklü bile olsa sadece yoğun site kullanımı olan 15-18 saatleri arasında durdurulsun istiyorsanız web-spider’lar $zamanli=true; olarak değiştirebilirsiniz.
include/robotdurdur.php adıyla kaydedeceğimiz bu script user_agent bilgisine ve IP’sine bakarak robot çağrısı olan istekleri “403 Not Allowed” yanıtı ile durdurur. Bu yanıt genellikle o gün için o robotu o URL için durdurur. Ertesi gün reddedildiği sayfayı taramayı yeniden deneyecektir. Yani bu scripti yalnızca sistem çok yoğun olduğu anlarda çağırırsak web sitemizi tarayıp dizinlerine almaya çalışan arama motorlarını küstürmüş olmayız. Hem sitemiz gene arama motorlarındaki sırasını korur hem de çok yoğun robot taramasına maruz kalmaz.
Kullanmak için tek yapmamız gereken yukarıdaki loadavg(); işlevini yükledikten ve sistem yükünü ölçtükten sonra belli bir yükte bu işlevi çağırmak. Önceki örneğin devamına ekleyelim:
<php // Sayfanın en başı include("include/function.loadavg.php"); include("include/function.hizlan.php"); $sistemyuku=loadavg(); if ( $sistemyuku > 2 ) { hizlan(30); } else if ( $sistemyuku > 1 ) { hizlan(10); } else { hizlan(5); } // Aşırı yükte robot durduralım: if ( $sistemyuku > 2.5 ) include("include/robotdurdur.php"); //... //... sayfanın geri kalan PHP scripti de bundan sonraya
Bu örneği PHP scriptlerimizin en başına koyarak sistem yüküne göre önbellekleme sürelerini ve robot isteklerini düzene sokmuş oluyoruz. Sistem yükü eşik değerleri ile oynayarak ortalama sistem yükünü belli bir düzeyde tutmanız mümkün. Çoklu sitenin barındığı ortak sunucularda ise önce sistem yükünü gözleyerek genel ortalama ve uç değerleri saptayıp ona göre eşik değeri belirleyebilirsiniz. Örneğin tamamıyla boş ve sadece sizin tek bir sitenizi barındıran bir sunucuda eşik değer olarak 0.20’yi seçebilirsiniz. Veya çok yoğun ortak bir sunucuda yük ortalaması 1’in altına hiç düşmüyor olabilir. Eşik değerini buna göre yukarılara çekebilirsiniz. Bu ince ayarları yaptıktan sonra sunucu kayıtlarınızı incelerseniz gerçekten de unique IP azalmamasına rağmen bant-genişliği kullanımının kayda değer ölçüde düştüğünü görebileceksiniz…
Evet. Son olarak kimi arama motorlarının kullandığı “Crawl-delay” komutunu robots.txt dosyalarımıza ekleyelim:
User-agent: Slurp Crawl-delay: 120 User-agent: msnbot Crawl-delay: 120 User-agent: twiceler Crawl-delay: 120 User-agent: discobot Crawl-delay: 120 User-agent: OOZBOT Crawl-delay: 120
Bu Crawl-delay’e uyan başka robotlara denk gelirseniz onları da ekleyebilirsiniz. “User-agent: *” kullanarak hepsi için geçerli derdik ancak GoogleBot dahil Crawl-delay desteklemeyen kimileri bundan şimdilik şikayet ediyor. Bu arada ben örnekte 120 saniye değerini verdim. Kendim aslında 360 kullanıyorum. “Search Engine Optimization” (SEO = Arama Motoru Optimizasyonu) yapıyor ve “ama sürekli indekslenmek istiyorum” diyebilirsiniz gerçi. Ancak uzun dönemde bu denli sık yoklanmaya gerek duymazsınız. Hele sitenizde on binlerce yüz binlerce link varsa indekslenmeniz hiç bitmeyebilir… Böyle durumlarda arama motorlarının işlerini uzun süreye yaymak sunucu yükünüzü ve bant genişliğinizi hafifletecektir. Varsın her saniyede değil de birkaç dakikada bir gelip indekslesin. Ne de olsa tarar taramaz veritabanlarını güncellemiyorlar…
Evet hepsi bu kadar. İşinize yaraması dileğiyle.
kaynak: http://www.klavyemonitor.com/sunucu_yuku_ve_bant_genisligi_optimizasyonu.html