Daha önce SOAP kullanan web hizmetleri hakkında şu yazıyı yazmıştım.
Aradan vakit geçip, Logipik‘i bir web hizmeti haline getirirken, bu kez REST kullanmayı düşündüm ve “hazır kütüphane olarak ne bulabilirim?” diye araştırırken Ersin Güvenç‘in yazdığı easyREST’e denk geldim.
EasyREST, oldukça düzgün kotarılmış ve gereken her şeyi PEAR kütüphanelerinden bir araya topladığından, “stand-alone” çalışabilen bir REST kütüphanesi. Benim indirdiğim sürüme dahil edilmiş olan PEAR paketleri biraz eski olduğundan, PHP 5.3.x üstünde “#” ile yorumlarda notice döndürüyor ama artık o kadar kusur kadı kızında da olur.
Elbette ben EasyREST’i kendi gereksinimlerim doğrultusunda bir miktar değiştirmek zorunda kaldım, ancak REST’e başlangıç noktası olarak çok doğru bir adres olduğunu söyleyebilir ve tüm PHP programcılarına da gönül rahatlığıyla öneririm.
Ersin Güvenç’e, bu kütüphane için çok teşekkür ederim.
Yazılım işleri
Gnu/Linux’la bir geçmişi olan herkesin kafasından “GNU/Linux harikulade, açık kaynak. Üstelik çoğunlukla da hem işletim sisteminin kendi, hem de uygulamaların ezici çoğunluğu bedava. Pekiyi neden yaygın değil?” sorusu mutlaka geçmiştir.
Herkesin bu soruya kendince bir yanıtı vardır. Bana göre de yanıt eğitim ve alışkanlıklarda yatıyor.
Geçtiğimiz hafta içinde bir gün annem (1980 Darbesi olduğunda üniversitede bir kaç yıllık hocaydı. Varın yaşını siz çıkarın) yanında, içinde dizüstü bilgisayarının olduğu bir çantayla çıkageldi. Derdi, kullandığı Windows’un “Lisansınız sahte. Sizi gidi üçkağıtçı sizi!” yollu bağırıyor olmasıydı. Oysa lisansı sahte falan değildi. Büyük olasılıkla yaşadığı Ankara yakınındaki kasabada, herkese “yolunacak kaz” gözüyle bakan bir “bilgisayarcının” gazabına uğrayarak lisanslı özgün Windows CD’sini kaptırmış, bunun yerine eline bir kopya Windows CD’si tutuşturulmuş ve yollanmıştı. Windows destek hattından da kısa zamanda bir sonuç çıkamayacağı anlaşılınca da bilgisayarını kaptığı gibi bize getirmişti. Biz durumu anlayıp, Micro~ Destek Hattı’nı arayıp tatlı tatlı(!) konuşmayı düşünürken, annemin gözü bizim makinelerimizde koşan Ubuntu’ya takıldı.
“Linux bu mu?” diye sordu.
“Budur!” dedik.
“Tamam”, dedi, “Bana da bundan kurun!”
“Aman anne!”, dedim, “sarsmasın Linux seni?”
“Ne olacak! Öğrenirim!” dedi.
Sonuç olarak, annemin bilgisayarına Ubuntu 10.04 kurduk ve masaüstünü de Ubuntu’ya çabuk adapte olacağı şekilde düzenledik. Yukarıda annemin Ubuntu’sunun masaüstü ekran görüntüsü var. (Büyük halini kaçırmayın!)
Eve gittikten sonra bir kere (evet yalnız bir kere) Türkcell Vınn ile İnternet’e bağlanmayı beceremediği için aradı; hepsi o kadar.
Benim annem artık Ubuntu kullanıyor ve çok memnun. Ya sizin anneniz?
Tor Projesi
Son birkaç yıldır ülkemizde hemen herkes domain bazlı engellemenin üstesinden gelmeyi öğrendi. Daha yakından ilgililer IP bazlı engellemenin nasıl geçilebileceğini de biliyorlar; anlatacaklarım ilk kez duyanlara:
Tor Projesi Nedir?
Web sitesinden alıntı:
“Tor kendinizi trafik analizine karşı koruyabilmenize yardımcı olan bir yazılım projesidir. Trafik analizi kişisel özgürlüğü ve gizliliği, gizli ticari eylemleri ve ilişkileri ve devlet güvenliğini tehdit eden bir çeşit ağ denetimidir. Tor iletişiminizi dünyanın her tarafından gönüllüler tarafından işletilen dağıtılmış bir ağ üzerinden sağlayarak sizi korur: birilerinin sizin Internet bağlantınızı izleyerek hangi siteleri gezdiğinizi öğrenmesini engeller, ayrıca girdiğiniz sitelerin sizin fiziksel yerinizi öğrenmesini de engeller. Tor, Internet tarayıcıları, anında mesajlaşma istemcileri, uzaktan erişim ve TCP protokolünü kullanan diğer uygulamalar dahil mevcut uygulamalarınızın bir çoğu ile çalışır. ”
Kendileri daha uygun biçimde söylemişlerdir ancak “trafik analizi”, “sizin internette yağtığınız şeylerle” o ya da bu saikle ilgilenmektir.
İnternette özgür olmak, engellenmemek, hepsinden daha basiti, çok beğendiğiniz bir sanatçının bir klibini izlemek istediğinizde can sıkıcı engellerle karşılaşmak istemiyorsanız Tor Projesi sizin içindir.
Aşaşığa Tor kullanarak Ubuntu üstünde Youtube’dan bir Enrico Macias-Ajda Pekkan klibini (herhangi biri ya da bir kurum, sizi teoride Youtube IP adresine ulaştırmak istemese dahi) nasıl izleyebileceğimizi görecdeğiz:
İlk olarak root haklarıyla gedit’i açıyoruz:
1 | sudo gedit |
ve akabinde Tor reposunu en alta ekliyoruz:
[code]/etc/apt/sources.list[/code]
Daha sonra sırasıyla gereken gpg anahtarını ekliyor ve apt veritabanını güncelliyoruz (herhangi bir komutta terslik yaparsa:
1 | sudo !! |
yazın, root şifresini girdikten sonra önceki komutu root haklarıyla çalıştıracaktır.
[code]
gpg --keyserver keys.gnupg.net --recv 886DDD89
[/code]
[code]
gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | sudo apt-key add -
[/code]
[code]
apt-get update
apt-get install tor tor-geoipdb polipo
[/code]
Artık Tor sisteminizde hazır. Bir ufak ayar kaldı:
Şu adresten Polipo ayar dosyasını alıyoruz ve yine biraz önce yaptığımız gibi root haklarıyla çalışan gedit’e
[code]/etc/polipo/config[/code] adıyla kaydedip Polipo’yu yeniden başlatıyoruz:
[code]
sudo /etc/init.d/polipo restart
[/code]
Tor artık hazır.
Son olarak Firefox’ta bizim için Tor’U istediğimiz zaman devreye alıp, istemediğimiz zaman devre dışı bırakacak olan “Tor Button” eklentisini kuruyoruz ve Firefox’u yeniden başlatıyoruz. Bu işlemden sonra sağ alt tarafta “Tor Devredışı” yazısını göreceksiniz. Bu yazının üstünü tıklatığınızda ise bu yazı “Tor etkin” şeklinde değişecek.
Ben Enrico Macias’ı seviyorum, siz istediğinizi dinleyebilirsiniz: Adres kutusuna http://youtube.com yazın ve hepsi bu kadar!
Son günlerdeki Youtube’a erişimin IP tabanlı olarak engellenmesine dair endişe verici gelişmeler beni, uzun süredir yazmak istediğim bir yazıyı iş-güç arasına sıkıştırmaya itti.
Bu yazı iki bölümden oluşuyor:
1- Günümüz Türkiye’sinde internet özelinde vatandaş-devlet dengesi ne durumdadır?
2- Vatandaş yasal olarak özgürlüğünü nasıl savunur?
Yalnız “ben Youtube’da klip izlemek istiyorum hoca; ben keyfime göre takılabildikten sonra çakmışım gerisine!” diyenler yazının birinci bölümünü atlayıp doğrudan ikinci bölüme geçebilirler. Birinci bölüm düşünsel, ikinci bölüm tekniktir ve Tor Project ile kişinin, kendi girmek istediği siteleri yine kendinin nasıl belirleyebileceğini anlatır.
Geri kalanlar için ilk bölüm:
Günümüz Türkiye’sinde internet özelinde vatandaş-devlet dengesi ne durumdadır?
Konuyla ilgili herkes biliyor ancak anımsatmakta yarar var: 5651 sayılı bir yasa var ve internet ülkemizde bu yasa uyarınca “düzenleniyor”. Bazı internet sitelerinin yasaklanması uygulaması, bu yasa hükümleri uyarınca vücut buluyor.
“İnternet özgür ortam”. Bu ifade artık klişe. Burada “özgürlük nedir?” sorusunu kabaca “başkasının özgürlükleriyle sınırlı şey” şeklinde genelgeçer yanıtlamak isterim. Gerçekleştirdiğiniz eylemin bana, öbürüne ya da diğerine herhangi bir zararı, diğerinin özgürlüğünü kısıtlayıcı bir yanı yoksa, o eylemde özgür olmalısınızdır. Eski Yunan’dan beri bu kavram “demokrasi” kavramı ile beraber anılır olmuşsa insanların bin yıllardır edindikleri deneyimlerinden çıkardıkları bir şeyler var demektir.
Hukukçu olmamakla beraber, hukukçuların yorumları ve güncel uygulamalardan çıkarabildiklerim ışığında:
- Bu yasa devletin bir kurumuna “kafasına göre” ve herhangi bir mahkeme kararı aranmasına gerek olmaksızın, “yasaklanma kriterlerine uygun gördüğü” sitelere sizin-benim erişimimizi engelleyebilme hakkı veriyor. Türkçesi, bu kurumda çalışan, maaşları sizin-benim verdiğimiz vergilerle ödenen “uzmanlar”, şu ya da bu sitenin “bizim için uygun olmadığına” karar verip, mezkur yasaya dayanarak yine bizim erişimimizi engelleyebiliyor. (Aşağıda yazacaklarım doğrudan herhangi bir kurum ya da kişiyi hedef almıyor; genel saptamalar niteliğinde okunmalı)
Bu durumun gerçeklenmesini bir sürü porno siteye erişimin engellenmesi şeklinde kendini gösteren uygulamalarda görebiliyoruz. Üstelik iş “porno” olunca kimse elini taşın altına koyup “kardeşim, sana ne!” demek istemiyor. Buyurunuz, işte ben diyorum: Otuzbeş yaşında adamım. İster porno izlerim, ister masturbasyon yaparım ve bunu size soracak değilim. Keyfimin bileceği iştir. Ben gelip size “gece hanımınla şöyle yapma, böyle yap!” diyor muyum? Demiyorum çünkü haddime düşmemiştir. Benim özel hayatımda yaptıklarımın sınırını belirlemek de, başkalarının sınırlarına herhangi bir tecavüz söz konusu olmadığı durumda, kimsenin haddine düşmez. Bu durum, işçinin patronuna “Şu filmleri izleyebilirsin, bunu izleyemezsin!” demesine benzer ve uygar bir sistemde kabul edilmesi mümkün değildir. Devletin, bireyin hizmetinde olduğunu söyleyen herhangi bir sistem için görünen budur.
Yasa koyucu bu durumu göz önüne aldığı için “erişmeye” cezai yaptırım uygulamıyor, ancak “erişim sağlayıcısına” yönelik yaptırımlar öngörüyor.
Bir parantez: (Öte taraftan “genel ahlaka mugayir” kavramı var ki, işte bu kavram içine girebilecek herhangi şeyi bulundurmak da bildiğim kadarıyla suç teşkil edebilir. Herhangi bir hukuk metninin içinde “genel ahlak” türünden öznel ifadeler gördüğüm vakit tüylerim diken diken oluyor. Neye göre yargılayacaksın? Kimin ahlakı “genel”? Bu ayrıca hukukçular ve düşünürler tarafından incelenmesi gereken bir kavram.)
- Diğer nokta “youtube” gibi popüler video paylaşım sitelerinin başına gelenler:
Bu noktada yasaya dayalı bir mahkeme kararı var. Mahkeme eldeki yasaya göre elbette en adil kararı verecektir. Buna göre, Atatürk’e hakaret eden site engellenmeli. Kanımca, bırakın bize hür bir memleket hediye etmiş Atatürk’ü, herhangi sıradan kişiye hakaret, yukarıdaki “başkalarının özgürlüğü” sınırlarını ihlal etmektir ve elbette yaptırımı olmalıdır. Ancak bu yaptırım mutlaka ve mutlaka “hakareti edene” uygulanmalıdır; dünyadaki geri kalan insanlara değil. Gücünüz yetiyorsa gidip bu tür içeriği web sitesinden kaldırtırsınız. Gücünüzün yetmediği durumlarda, hıncınızı gücünüzün yettiklerinden çıkarmak adil değildir.
Tüm dünya bu hakaretleri sınırsızca izleyebilirken, ülkemizdeki insanları, bir takım yabancıların yükledikleri birkaç görüntü yüzünden cezalandırmaya çalışmak, en masum benzetmeyle “katili serbest bırakıp, cinayeti görenlere ’siz bunu görmeyin, yasaktır!’” demektir.
- Son nokta yasanın uygulanabilirliği:
Yasalar gerçekçi olmalıdır. Youtube ülkemizde zaten iki yıldan uzun süredir yasaktı ancak bu yasak ancak İnternet servis sağlayıcılarının alan adı sunucularının yüklerinin hafiflemesine yaradı, başka da bir işe yaramadı. Yürürlükteki yasa sayesinde 12 yaşındaki genç arkadaşlarımız bile “nameserver nedir?” , “DNS protokolü nasıl çalışır?” sorularına yanıt veren alanlarda küçümsenmeyecek deneyim sahibi oldu.
Youtube’a IP bazlı erişim kısıtlaması içeren güncel durumdan sonra da gençlerin, yazının ikinci bölümünde anlatacağım uygulamalar hakkında uzmanlaşmaları da sağlanacağı aşikar olduğundan, yasama organı üyeleri, şapkalarını önlerine koyup aşağıdaki sorular hakkında tekrar düşünmelidir:
1- “Özgürlük” nedir?
2- “Yasak” nedir?
3- “Üstüne oy verdiğim konu hakkında ne derece bilgi sahibiyim?”
4- “Olumlu oy verdiğim bu yasa beni tarih önünde ne duruma sokacak? İnsanlar adımı kimle beraber anacak?”
Etiketler: Tor Project
İki günlük bir aradan sonra serinin yeni bölümüyle(!) karşınızdayım.
Bu yazının ana konusu pcntl destekli, daemon olarak çalışan Azra Web Server. Değişiklik olsun diye önce benchmarklara bakalım. Aşağıdaki tabloda açıkça görülebildiği gibi Azra ağır yük altında bile Apache’den çok iyi, Nginx’ten biraz daha iyi.
| X | Ortalama (ms) | real | user | sys |
|---|---|---|---|---|
| Apache | 0.00938119351864 | 0m2.247s | 0m0.068s | 0m0.028s |
| Nginx | 0,00461062550545 | 0m4.443s | 0m0.152s | 0m0.036s |
| Azra | 0,00429985105991 | 0m1.992s | 0m0.112s | 0m0.024s |
Benchmark yöntemi:
Apache, Nginx ve Azra kullanarak aşağıdaki kodu 400 kere çağırıyoruz.
test.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
ve test için kullandığımız python kodu (benchmark.py):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import urllib2 from threading import Thread class testSuite: def testConnection(self , url , thread = 0): page = urllib2.urlopen(url) data = page.read() page.close() print("Thread:%d" % thread) def simpleConnection(self , url , tourNum): for i in range(tourNum): self.testConnection(url) def threadedConnection(self , url , tourNum , threadNum): for i in range(tourNum): myThread = i % threadNum t = Thread(target=self.testConnection , args=(url , myThread)) t.start() myTestSuite = testSuite() myTestSuite.threadedConnection('http://localhost:8114/test.php' , 400 , 10) #azra #myTestSuite.threadedConnection('http://localhost/webserver/test.php' , 400 , 10) #apache #myTestSuite.threadedConnection('http://localhost/test.php' , 400 , 10) #nginx |
Gelişmeler:
Artık Azra, Ubuntu altında daemon olarak çalışabiliyor.
/etc/init.d/azra
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | #! /bin/sh ### BEGIN INIT INFO # Provides: azra # Required-Start: $all # Required-Stop: $all # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start and stop azra web server # Description: Start and stop azra web server ### END INIT INFO # Author: Can Ince <[EMAIL PROTECTED]> # Do NOT "set -e" PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="AZRA WEB SERVER" NAME=Azra DAEMON=/var/www/webserver/azra.php PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME PHP_CONFIG_FILE=/etc/php5/cgi/php.ini # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 # Read configuration variable file if it is present [ -r /etc/default/$NAME ] && . /etc/default/$NAME # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. . /lib/lsb/init-functions # If the daemon is not enabled, give the user a warning and then exit, # unless we are stopping the daemon if [ "$START" != "yes" -a "$1" != "stop" ]; then log_warning_msg "To enable $NAME, edit /etc/default/$NAME and set START=yes" exit 0 fi # Process configuration export PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS DAEMON_ARGS="-q -b $FCGI_HOST:$FCGI_PORT -c $PHP_CONFIG_FILE" do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \ --background --make-pidfile --chuid $EXEC_AS_USER www-data $DAEMON -- \ $DAEMON_ARGS \ || return 2 } do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred killall azra.php start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE > /dev/null # --name $DAEMON RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. # If the above conditions are not satisfied then add some other code # that waits for the process to drop all resources that could be # needed by services started subsequently. A last resort is to # sleep for some time. start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON [ "$?" = 2 ] && return 2 # Many daemons don't delete their pidfiles when they exit. rm -f $PIDFILE return "$RETVAL" } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; restart|force-reload) log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 exit 3 ;; esac |
… ve /etc/defaults/Azra:
1 2 | START=yes VERBOSE=yes |
Azra multi-process olarak çalışabiliyor.
Ana kod (azra.php)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | #! /usr/bin/php-cgi <?php /** * Azra Web Server * * Web Server coded in pure PHP * * @package Azra Web Server * @author Can Ince * @copyright Copyright (c) 2010, Logikit / Can Ince. * @version 0.0.2 * @license http://www.opensource.org/licenses/mit-license.php * @link http://can.logikit.net */ set_time_limit(0); interface server { public function setWebRoot($path); public function setPort($port); public function serve(); } final class webserver implements server { public $socket; private $_port , $_webroot , $_queryString , $_webRootWithoutSlash, $_input , $_file , $_verbose , $_remoteAddr , $_remotePort; public function __construct() { $this->_verbose = 0; $_SERVER = array(); $_COOKIE = array(); } public function setPort($port) { $this->_port = $port; } public function verbose() { $this->_verbose = 1; } public function setWebRoot($path) { $this->_webroot = $path . '/'; $this->_webRootWithoutSlash = $path; } private function _mimeHandler($input) { $fileinfo = pathinfo($input); switch ($fileinfo['extension']) { case 'png': $mime = "text/png"; break; case 'jpg': case 'jpeg': $mime = "text/jpeg"; break; case 'gif': $mime = "text/gif"; break; case 'css': $mime = "text/css"; break; case 'swf': $mime = "application/x-shockwave-flash"; break; default: $mime = "text/html; charset=UTF-8"; } return $mime; } private function _handleCode($contents) { //eval() edilecek kodun başında <?php ya da <? varsa çakar. halledelim. $contents = trim($contents); if(substr($contents , 0 , 5) == '<?php') { $contents = substr($contents , 5); } elseif(substr($contents , 0 , 2) == '<?') { $contents = substr($contents , 2); } return $contents; } private function _parseGet($str) { $_GET = array(); $array = explode('&' , $str); foreach($array as $requestItem) { $itemArray = explode('=' , $requestItem); if(!empty($itemArray[0])) $_GET[$itemArray[0]] = $itemArray[1]; } unset($_GET['hyperTextServer_php']); //bunu $_GET'e enjekte ediyor. neden, bilmiyorum... } private function _parsePost($str) { $_POST = array(); $array = explode('&' , $str); foreach($array as $requestItem) { $itemArray = explode('=' , $requestItem); if(!empty($itemArray[0])) $_POST[$itemArray[0]] = $itemArray[1]; } } private function _parseCookie($str) { $array = explode('; ' , $str); foreach($array as $requestItem) { $itemArray = explode('=' , $requestItem); if(!empty($itemArray[0])) $_COOKIE[$itemArray[0]] = $itemArray[1]; } } private function _parseRequest($str) { $lines = explode("\n" , $str); $rawData = explode(' ' , $lines[0]); $_SERVER['REQUEST_METHOD'] = $rawData[0]; if($_SERVER['REQUEST_METHOD'] == 'POST') $this->_parsePost(end($lines)); $_SERVER['PHP_SELF'] = $this->_webRootWithoutSlash . $rawData[1]; $_SERVER['HTTP_HOST'] = substr($lines[1] , 6); $_SERVER['HTTP_USER_AGENT'] = substr($lines[2] , 12); $_SERVER['HTTP_ACCEPT'] = substr($lines[3] , 8); $_SERVER['HTTP_ACCEPT_LANGUAGE'] = substr($lines[4] , 17); $_SERVER['HTTP_ACCEPT_ENCODING'] = substr($lines[5] , 17); $_SERVER['HTTP_ACCEPT_CHARSET'] = substr($lines[6] , 16); $_SERVER['HTTP_KEEP_ALIVE'] = substr($lines[7] , 12); $_SERVER['HTTP_CONNECTION'] = substr($lines[8] , 12); if(strstr($lines[9] , 'Referer')) { $_SERVER['HTTP_REFERER'] = substr($lines[9] , 9); $_SERVER['HTTP_COOKIE'] = substr($lines[10] , 8); } else $_SERVER['HTTP_COOKIE'] = substr($lines[9] , 8); if(!empty($_SERVER['HTTP_COOKIE'])) $this->_parseCookie($_SERVER['HTTP_COOKIE']); $_SERVER['SERVER_SIGNATURE'] = ' <address>Azra/0.0.1 Server at ' . $_SERVER['HTTP_HOST'] . ' Port ' . $this->_port . '</address>'; $_SERVER['SERVER_SOFTWARE'] = 'Azra/0.0.1'; $_SERVER['DOCUMENT_ROOT'] = $this->_webroot; $_SERVER['SCRIPT_FILENAME'] = $this->_webRootWithoutSlash . str_replace('./' , '/' , $this->_file); $_SERVER['SCRIPT_NAME'] = $this->_file; $_SERVER['REQUEST_TIME'] = time(); $_SERVER['REMOTE_ADDR'] = $this->_remoteAddr; $_SERVER['REMOTE_PORT'] = $this->_remotePort; if(!empty($this->_queryString)) $_SERVER['QUERY_STRING'] = $this->_queryString; } private function _handle404() { return "<title>UHU 404</title><h2>UHU 404</h2>Iın-ıın-ıın-ıın-cav-cav-cav-cav-oooooo - oooooo - zört - zört - zört -zört (araba alarmı efekti) Çok afedersin müdürüm ama yok öyle bir dosya."; } public function openSocket() { $this->socket = @socket_create_listen($this->_port); if (!$this->socket) { echo "Soket olamiyor!\n"; exit; } $this->_loadConfigHandler(); } private function _loadCOnfigHandler() { $directoryConfigCandidate = $this->_webroot . '/.ConfigHandler.php'; if ($directoryConfigCandidate && is_readable($directoryConfigCandidate)) { require ($directoryConfigCandidate); } } public function serve() { while (1) //daire-i ilanihaye! { $client = socket_accept($this->socket); socket_getpeername($client , $remoteAddr , $remotePort); $this->_remoteAddr = $remoteAddr; $this->_remotePort = $remotePort; $input = $this->_input = trim(socket_read ($client, 4096)); if($this->_verbose == 1) echo $input; $input = explode(" " , $input); $input = $input[1]; if(class_exists('ConfigHandler')) { $configHandler = new ConfigHandler($input , $this->_webroot); //konvansiyonumuza göre rewrite işi $configHandler'ın url özelliğinde bulunmalı if($configHandler->url != NULL) $input = realpath($configHandler->url); } $mime = $this->_mimeHandler($input); if ($input == "" || $input == '/') { $input = "/index.html"; } $input = ".$input"; $input = str_replace('//' , '/' , $input); $inputArray = explode('?' , $input); $this->_file = $inputArray[0]; $input = $this->_webroot . $this->_file; if (file_exists($input) && is_readable($input)) { if($this->_verbose == 1) echo "Dosya: $input\n"; $contents = file_get_contents($input); if(strstr($input , '.php')) { if(!empty($inputArray[1])) $this->_queryString = $inputArray[1]; $this->_parseGet($this->_queryString); $this->_parseRequest($this->_input); $_REQUEST = array_merge($_GET , $_POST , $_COOKIE); $contents = $this->_handleCode($contents); //PHP çalıştıktan sonra sonucu echo etmesini değil, $contents stringine atmasını istiyoruz. ob_start(); eval($contents); //gidip $contents'in üstüne yazmak doğru alışkanlık değil ama yeni değişken register etmek istemiyorum $contents = ob_get_contents(); ob_end_clean(); } $output = "HTTP/1.0 200 OK\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: $mime\r\n\r\n$contents"; } else { $contents = $this->_handle404(); $output = "HTTP/1.0 404 OBJECT NOT FOUND\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n$contents"; } socket_write($client, $output); socket_close ($client); } socket_close ($this->socket); } public function start() { $pids = array(); $this->openSocket(); for($i = 0; $i < 10; $i++) { $pids[$i] = pcntl_fork(); if(!$pids[$i]) { // child process $this->serve(); exit(); } } for($i = 0; $i < 10; $i++) { pcntl_waitpid($pids[$i], $status, WUNTRACED); } } } $server = new webserver(); $server->setWebRoot('/var/www/act'); $server->verbose(); //ayrıntıları stdout'a echo et. $server->setPort(8115); $server->start(); |
Ara-sonuçlar:
- Bir kaç sorun hala devam ediyor.
– Session’lar düzgün halledilemiyor.
– include’larda sorun yaşanıyor zira dirname(__FILE__) Azra’nın çalıştığı dizini döndürüyor.
- log işine çözüm bulmak lazım.
– header() kullanarak 302 yollanırken path düzgün alınamıyor.
Bu sorunlar da halledildikten sonra şaka olarak başlayan denememiz oldukça ilginç hale gelecek ve bu benchmarkları korumayı başarabilirsem 0.0.3 sürümüne Launchpad yolları görünüyor.
Etiketler: Azra Web Server, PHP, Web Server
Ubuntu üstünde PHP kullanıyoruz. Günün birince pcntl desteğine ihtiyacımız oluyor ve tüm PHP’yi yeniden derlemek istemiyoruz. Nasıl yapıyoruz? Şu şekilde:
- Önce bir dizin oluşturuyoruz ve o dizine geçiyoruz. Nerede oluşturduğumuz önemli değil
1 2 | mkdir php cd php |
Sistemimizde PHP-dev paketi yoksa kuruyoruz:
1 | sudo apt-get install php5-dev |
- Daha sonra php source paketini indiriyoruz:
1 | apt-get source php5 |
..ve pcntl dizinine geçiyoruz:
1 | cd php5-[php sürümü]/ext/pcntl |
Akabinde bildiğimiz PHP modül derleme işi:
1 2 3 | phpize ./configure make |
Son olarak derlediğimiz paylaşılmış nesneyi ilgili yere kopyalıyoruz
1 2 | cp modules/pcntl.so /usr/lib/php5/phpSürümTarihi/ echo "extension=pcntl.so" > /etc/php5/conf.d/pcntl.ini |
Sunucuyu yeniden başlatıp phpinfo() çektiğimizde pcntl’i görüp mutlu oluyoruz.
Tahmin edebileceğiniz gibi, gelecek bölüm: “Bir Web Sunucusu Kodlama Denemesi – 4 (Pcntl destekli Azra)
Heyecan devam ediyor:-)
Bu bölümde:
- Bir önceki sürümde eksik kalan $_SERVER['REMOTE_ADDR'] ve $_SERVER['REMOTE_PORT'] halloldu.
- .ConfigHandler.php isimli dosyayı kullanarak Apache’de .htaccess ile çözülen işler saf PHP ile çözülür hale geldi. Bir rewrite örneğini yazının devamında bulabilirsiniz.
- Benchmark sevenler: Bu bölümde istemediğiniz kadar benchmark var;-) Ayrıca, benchmark işleri küçük bir Python kodu yardımıyla otomatik hale getirildi.
Bir önceki yazımda bir web sunucusunda bulunması gereken temel özelliklerin çoğuna sahip, PHP çalıştıran bir web sunucusunu, yine PHP kullanarak hayata geçirebilmiş ve adını da Azra Erhat’ın anısına “Azra” koymuştum. Yukarıda gördüğünüz logo için ise, bir bakışta anlayabileceğiniz gibi hiç uğraşmadım, çünkü uğraşsam da daha iyisini yapamayacağımı biliyorum! Hayırsever bir arkadaş düzgün bir logo yapabilirse seve seve kullanırım. Şimdiki logo basitçe Göktürk alfabesi kullanarak “Azra” stringinden ibaret.
Bu sürümde yeni ne var?
- Artık supergloballer ($_POST, $_GET, $_REQUEST, $_COOKIE, $_SERVER) tam olarak destekleniyor.
- .ConfigHandler.php diye bir dosya icat ettim. Azra bu dosya var mı diye bakıyor, varsa içeri alıyor ve ConfigHandler sınıfını çağırıyor. Aşağıdaki dosyada da görebileceğiniz gibi, örneğin “url rewrite” işi çocuk oyuncağı.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php class ConfigHandler { public $url = NULL; public function __construct($input) { $this->azraRewrite($input); } public function azraRewrite($input) { $candidate = getcwd() . $input; if(!is_file($candidate) && !is_dir($candidate)) $this->url = '/router.php?' . $input; else $this->url = $input; } } |
- Web sunucumuzun yeni sürümü ise şu şekilde:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | <?php /** * Azra Web Server * * Web Server coded in pure PHP * * @package Azra Web Server * @author Can Ince * @copyright Copyright (c) 2010, Logikit / Can Ince. * @version 0.0.2 * @license http://www.opensource.org/licenses/mit-license.php * @link http://can.logikit.net */ set_time_limit(0); interface server { public function setWebRoot($path); public function setPort($port); public function serve(); } final class webserver implements server { private $_port , $_webroot , $_queryString , $_webRootWithoutSlash, $_input , $_file , $_verbose , $_remoteAddr , $_remotePort; public function __construct() { $this->_verbose = 0; } public function setPort($port) { $this->_port = $port; } public function verbose() { $this->_verbose = 1; } public function setWebRoot($path) { $this->_webroot = $path . '/'; $this->_webRootWithoutSlash = $path; } private function _mimeHandler($input) { $fileinfo = pathinfo($input); switch ($fileinfo['extension']) { case 'png': $mime = "text/png"; break; case 'jpg': case 'jpeg': $mime = "text/jpeg"; break; case 'gif': $mime = "text/gif"; break; case 'css': $mime = "text/css"; break; case 'swf': $mime = "application/x-shockwave-flash"; break; default: $mime = "text/html; charset=UTF-8"; } return $mime; } private function _handleCode($contents) { //eval() edilecek kodun başında <?php ya da <? varsa çakar. halledelim. $contents = trim($contents); if(substr($contents , 0 , 5) == '<?php') { $contents = substr($contents , 5); } elseif(substr($contents , 0 , 2) == '<?') { $contents = substr($contents , 2); } return $contents; } private function _parseGet($str) { $_GET = array(); $array = explode('&' , $str); foreach($array as $requestItem) { $itemArray = explode('=' , $requestItem); if(!empty($itemArray[0])) $_GET[$itemArray[0]] = $itemArray[1]; } unset($_GET['hyperTextServer_php']); //bunu $_GET'e enjekte ediyor. neden, bilmiyorum... } private function _parsePost($str) { $_POST = array(); $array = explode('&' , $str); foreach($array as $requestItem) { $itemArray = explode('=' , $requestItem); if(!empty($itemArray[0])) $_POST[$itemArray[0]] = $itemArray[1]; } } private function _parseCookie($str) { $_COOKIE = array(); $array = explode('; ' , $str); foreach($array as $requestItem) { $itemArray = explode('=' , $requestItem); if(!empty($itemArray[0])) $_COOKIE[$itemArray[0]] = $itemArray[1]; } } private function _parseRequest($str) { $_SERVER = array(); $lines = explode("\n" , $str); $rawData = explode(' ' , $lines[0]); $_SERVER['REQUEST_METHOD'] = $rawData[0]; if($_SERVER['REQUEST_METHOD'] == 'POST') $this->_parsePost(end($lines)); $_SERVER['PHP_SELF'] = $this->_webRootWithoutSlash . $rawData[1]; $_SERVER['HTTP_HOST'] = substr($lines[1] , 6); $_SERVER['HTTP_USER_AGENT'] = substr($lines[2] , 12); $_SERVER['HTTP_ACCEPT'] = substr($lines[3] , 8); $_SERVER['HTTP_ACCEPT_LANGUAGE'] = substr($lines[4] , 17); $_SERVER['HTTP_ACCEPT_ENCODING'] = substr($lines[5] , 17); $_SERVER['HTTP_ACCEPT_CHARSET'] = substr($lines[6] , 16); $_SERVER['HTTP_KEEP_ALIVE'] = substr($lines[7] , 12); $_SERVER['HTTP_CONNECTION'] = substr($lines[8] , 12); if(strstr($lines[9] , 'Referer')) { $_SERVER['HTTP_REFERER'] = substr($lines[9] , 9); $_SERVER['HTTP_COOKIE'] = substr($lines[10] , 8); } else $_SERVER['HTTP_COOKIE'] = substr($lines[9] , 8); if(!empty($_SERVER['HTTP_COOKIE'])) $this->_parseCookie($_SERVER['HTTP_COOKIE']); $_SERVER['SERVER_SIGNATURE'] = ' <address>Azra/0.0.1 Server at ' . $_SERVER['HTTP_HOST'] . ' Port ' . $this->_port . '</address>'; $_SERVER['SERVER_SOFTWARE'] = 'Azra/0.0.1'; $_SERVER['DOCUMENT_ROOT'] = $this->_webroot; $_SERVER['SCRIPT_FILENAME'] = $this->_webRootWithoutSlash . str_replace('./' , '/' , $this->_file); $_SERVER['REQUEST_TIME'] = time(); $_SERVER['REMOTE_ADDR'] = $this->_remoteAddr; $_SERVER['REMOTE_PORT'] = $this->_remotePort; if(!empty($this->_queryString)) $_SERVER['QUERY_STRING'] = $this->_queryString; } private function _handle404() { return "<title>UHU 404</title><h2>UHU 404</h2>Iın-ıın-ıın-ıın-cav-cav-cav-cav-oooooo - oooooo - zört - zört - zört -zört (araba alarmı efekti) Çok afedersin müdürüm ama yok öyle bir dosya."; } public function serve() { $socket = @socket_create_listen($this->_port); if (!$socket) { echo "Soket olamiyor!\n"; exit; } while (1) //daire-i ilanihaye! { $client = socket_accept($socket); socket_getpeername($client , $remoteAddr , $remotePort); $this->_remoteAddr = $remoteAddr; $this->_remotePort = $remotePort; $input = $this->_input = trim(socket_read ($client, 4096)); if($this->_verbose == 1) echo $input; $input = explode(" " , $input); $input = $input[1]; $dirName = dirname(__FILE__); $directoryConfigCandidate = $dirName . '/.ConfigHandler.php'; if ($directoryConfigCandidate && is_readable($directoryConfigCandidate)) { require_once ($directoryConfigCandidate); if(class_exists('ConfigHandler')) { $configHandler = new ConfigHandler($input); //konvansiyonumuza göre rewrite işi $configHandler'ın url özelliğinde bulunmalı if($configHandler->url != NULL) $input = $configHandler->url; } } $mime = $this->_mimeHandler($input); if ($input == "" || $input == '/') { $input = "/index.html"; } $input = ".$input"; $inputArray = explode('?' , $input); $this->_file = $inputArray[0]; $input = $this->_webroot . $this->_file; if (file_exists($input) && is_readable($input)) { if($this->_verbose == 1) echo "Dosya: $input\n"; $contents = file_get_contents($input); if(strstr($input , '.php')) { $this->_queryString = $inputArray[1]; $this->_parseGet($this->_queryString); $this->_parseRequest($this->_input); $_REQUEST = array_merge($_GET , $_POST , $_COOKIE); $contents = $this->_handleCode($contents); //PHP çalıştıktan sonra sonucu echo etmesini değil, $contents stringine atmasını istiyoruz. ob_start(); eval($contents); //gidip $contents'in üstüne yazmak doğru alışkanlık değil ama yeni değişken register etmek istemiyorum $contents = ob_get_contents(); ob_end_clean(); } $output = "HTTP/1.0 200 OK\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: $mime\r\n\r\n$contents"; } else { $contents = $this->_handle404(); $output = "HTTP/1.0 404 OBJECT NOT FOUND\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n$contents"; } socket_write($client, $output); socket_close ($client); } socket_close ($socket); } } $server = new webserver(); $server->setWebRoot('/var/www/webserver'); //$server->verbose(); //ayrıntıları stdout'a echo et. $server->setPort(8008); $server->serve(); |
konsoldan
1 | php-cgi azra.php |
şeklinde çağırmak suretiyle çalıştırıyoruz.
- Benchmarklar:
Benchmark işleri için aşağıdaki test.php dosyasını kullandım. Göreceğiniz gibi bir for döngüsüne girip 10001′e kadar saymaktan ve çalışma süresini bir metin dosyasına yeni satır olarak yazmaktan başka bir iş yapmıyor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Testleri otomatik hale getirebilmek için de, en kolayıma Python geldiği için kısa ve basit bir kod yazdım:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import urllib2 from threading import Thread class testSuite: def testConnection(self , url , thread = 0): page = urllib2.urlopen(url) data = page.read() page.close() print("Thread:%d" % thread) def simpleConnection(self , url , tourNum): for i in range(tourNum): self.testConnection(url) def threadedConnection(self , url , tourNum , threadNum): for i in range(tourNum): myThread = i % threadNum t = Thread(target=self.testConnection , args=(url , myThread)) t.start() myTestSuite = testSuite() #myTestSuite.simpleConnection('http://localhost:8008/test.php' , 1000) #myTestSuite.simpleConnection('http://localhost/webserver/test.php' , 1000) myTestSuite.threadedConnection('http://localhost:8008/test.php' , 1000 , 10) |
Bu kodu ‘benchmark.py’ adıyla kaydedip konsoldan
1 | time python benchmark.py |
ile çağırıyoruz ve böylece ne kadar sürede çalıştığını ayrıntılarıyla okuyabiliyoruz. Benim apache için webroot’um /var/www. Azra için webroot’um ise /var/www/webserver. localhost:8008′den Azra’ya, localhost:80 (varsayılan port) adresinden Apache’ye ulaşabiliyoruz.
Bunların yanında, bir tür “pseudo-multi-processing” elde edebilmek için aşağıdaki PHP kodunu kullandım:
1 2 3 4 5 6 7 8 9 10 | <?php set_time_limit(0); include ("thread.php"); $t2 = new Thread("azra.php"); while ($t2->isActive()) { echo $t2->listen(); } $t2->close(); |
Bu kodu da konsoldan
1 | php-cgi server.php |
ile çağırıyoruz. Göreceğiniz gibi server.php, yeni bir PID ile bildiğimiz, sevdiğimiz azra.php’yi çağırıyor.
Kullandığımız üçüncü parti PHP sınıfı (thread.php) ise aşağıda
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | <?php /** * original version can be found at http://www.alternateinterior.com/2007/05/multi-threading-strategies-in-php.html * This version is slightly modified to run with PHP 5 - Can Ince */ class Thread { var $pref ; // process reference var $pipes; // stdio var $buffer; // output buffer public function __construct($file) { $this->pref = 0; $this->buffer = ""; $this->pipes = (array)NULL; $descriptor = array (0 => array ("pipe", "r"), 1 => array ("pipe", "w"), 2 => array ("pipe", "w")); $this->pref = proc_open ("php $file ", $descriptor, $this->pipes); stream_set_blocking ($this->pipes[1], 0); } public function isActive() { $this->buffer .= $this->listen(); $f = stream_get_meta_data ($this->pipes[1]); return !$f["eof"]; } public function close() { $r = proc_close ($this->pref); $this->pref = NULL; return $r; } public function tell($thought) { fwrite ($this->pipes[0], $thought); } public function listen() { $buffer = $this->buffer; $this->buffer = ""; while ($r = fgets ($this->pipes[1], 1024)) { $buffer .= $r; } return $buffer; } public function getError() { $buffer = ""; while ($r = fgets ($this->pipes[2], 1024)) { $buffer .= $r; } return $buffer; } } |
Benim lokal sistemimde benchmarklar aşağıdaki gibi çıktı:
—-Azra——–
0.00114736318588 – 10 farklı thread – 100 tur
real 0m0.334s
user 0m0.056s
sys 0m0.020s
0.0180547189713 – 10 farklı thread – 1000 tur
real 0m2.982s
user 0m0.192s
sys 0m0.056s
0.00165226125717 – 100 farklı thread – 1000 tur
real 0m5.251s
user 0m0.196s
sys 0m0.056s
0.00191986322403 – 10 farklı thread – 10000 tur
real 0m38.442s
user 0m1.640s
sys 0m0.608s
0.00110848665237 – tek thread – 100 tur
real 0m0.354s
user 0m0.092s
sys 0m0.068s
0.0111782860756 – tek thread – 1000 tur
real 0m3.421s
user 0m0.744s
sys 0m0.424s
0.00116171479225 – tek thread – 1000 tur (pseudo-multiprocess destekli)
real 0m4.293s
user 0m0.792s
sys 0m0.384s
0.00114714858532 – tek thread – 10000 tur
real 0m37.138s
user 0m8.209s
sys 0m4.280s
—-Apache——-
0.00138350725174 – 10 farklı thread – 100 tur
real 0m0.360s
user 0m0.068s
sys 0m0.028s
0.0136778688431 – 10 farklı thread – 1000 tur
real 0m3.524s
user 0m0.176s
sys 0m0.060s
0.00142541861534 – 100 farklı thread – 1000 tur
real 0m4.030s
user 0m0.144s
sys 0m0.040s
0.00147548000813 – 10 farklı thread – 10000 tur
real 0m46.358s
user 0m1.476s
sys 0m0.496s
0.00153880357742 – tek thread – 100 tur
real 0m0.499s
user 0m0.140s
sys 0m0.068s
0.0135918140411 – tek thread – 1000 tur
real 0m3.824s
user 0m0.972s
sys 0m0.504s
0.0135918140411 – tek thread – 10000 tur
real 1m7.090s
user 0m9.149s
sys 0m4.984s
Benchmarklara hızlıca göz attığımızda fark edebileceğimiz gibi, Azra multi-threaded isteklerde yavaş kalıyor. Bu durumu yeni Ubuntu çıktıktan sonra, PHP’yi pcntl ile derleyerek aşabilmeyi umuyorum. Diğer benchmarkların hepsinde Azra, Apache’ye fark atıyor.
Yeni bölümde yeni maceralarla görüşmek üzere:)
Etiketler: Azra Web Server, PHP, Web Server
Önceki yazımda basit bir web sunucusu kodlama denemesini anlatmıştım.
Bugün biraz daha vakit bulabildim ve hem yapay bir tür multi-processing ile denemeler yaptım, hem de superglobal değişkenleri işledim.
Başarısız taraftan başlayayım: Şu adreste bulunan PHP sınıfını -biraz tozunu alarak- kullandım zira yeni Ubuntu’ya bir hafta kala pcntl desteği ile yeniden PHP derlemekle uğraşmak istemedim. Ubuntu’yu yükselttikten sonra bu konuya döneceğim. Yukarıda bahsi geçen sınıf ile thread oluşturmak ise sistem kaynakları açısından korkunç pahalı sonuçlar ortaya çıkardı.
Başarılı tarafa gelince: Tüm supergloballeri ($_GET, $_POST, $_COOKIE, $_SERVER, $_REQUEST) $_SERVER['REMOTE_ADDR'] dışında başarıyla ayrıştırdım. Böylece ufak-tefek bir-iki eksik dışında gayet başarıyla çalışan, PHP üstünde yazılmış ve native olarak PHP destekleyen bir web sunucumuz oldu.
Elbet artık bu sunucuya bir de isim bulmak gerekti. Altan Tanrıverdi Hypatia‘yı önerdi. Bir bilim kadınının adını vermek fikri hoşuma gitmekle birlikte daha “bizden” bir ismi tercih ettim. Tanrıverdi’nin “bilimkadını adı” önerisi artı “bizden isim” arayışı, beni Azra Erhat‘ın anısına götürdü. Kodun kulağına ezanını okudum, vaftiz ettim ve adını böylece “Azra” koydum.
Kodun yeni hali şöyle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | <?php /** * Azra Web Server * * Web Server coded in pure PHP * * @package Azra Web Server * @author Can Ince * @copyright Copyright (c) 2010, Logikit / Can Ince. * @license http://www.opensource.org/licenses/mit-license.php * @link http://can.logikit.net */ interface server { public function setWebRoot($path); public function setPort($port); public function serve(); } final class webserver implements server { private $_port , $_webroot , $_queryString , $_webRootWithoutSlash, $_input , $_file , $_verbose; public function __construct() { $this->_verbose = 0; } public function setPort($port) { $this->_port = $port; } public function verbose() { $this->_verbose = 1; } public function setWebRoot($path) { $this->_webroot = $path . '/'; $this->_webRootWithoutSlash = $path; } private function _mimeHandler($input) { $fileinfo = pathinfo($input); switch ($fileinfo['extension']) { case 'png': $mime = "text/png"; break; case 'jpg': case 'jpeg': $mime = "text/jpeg"; break; case 'gif': $mime = "text/gif"; break; case 'css': $mime = "text/css"; break; default: $mime = "text/html; charset=UTF-8"; } return $mime; } private function _handleCode($contents) { //eval() edilecek kodun başında <?php ya da <? varsa çakar. halledelim. $contents = trim($contents); if(substr($contents , 0 , 5) == '<?php') { $contents = substr($contents , 5); } elseif(substr($contents , 0 , 2) == '<?') { $contents = substr($contents , 2); } return $contents; } private function _parseGet($str) { $_GET = array(); $array = explode('&' , $str); foreach($array as $requestItem) { $itemArray = explode('=' , $requestItem); if(!empty($itemArray[0])) $_GET[$itemArray[0]] = $itemArray[1]; } unset($_GET['hyperTextServer_php']); //bunu $_GET'e enjekte ediyor. neden, bilmiyorum... } private function _parsePost($str) { $_POST = array(); $array = explode('&' , $str); foreach($array as $requestItem) { $itemArray = explode('=' , $requestItem); if(!empty($itemArray[0])) $_POST[$itemArray[0]] = $itemArray[1]; } } private function _parseCookie($str) { $_COOKIE = array(); $array = explode('; ' , $str); foreach($array as $requestItem) { $itemArray = explode('=' , $requestItem); if(!empty($itemArray[0])) $_COOKIE[$itemArray[0]] = $itemArray[1]; } } private function _parseRequest($str) { $_SERVER = array(); $lines = explode("\n" , $str); $rawData = explode(' ' , $lines[0]); $_SERVER['REQUEST_METHOD'] = $rawData[0]; if($_SERVER['REQUEST_METHOD'] == 'POST') $this->_parsePost(end($lines)); $_SERVER['PHP_SELF'] = $this->_webRootWithoutSlash . $rawData[1]; $_SERVER['HTTP_HOST'] = substr($lines[1] , 6); $_SERVER['HTTP_USER_AGENT'] = substr($lines[2] , 12); $_SERVER['HTTP_ACCEPT'] = substr($lines[3] , 8); $_SERVER['HTTP_ACCEPT_LANGUAGE'] = substr($lines[4] , 17); $_SERVER['HTTP_ACCEPT_ENCODING'] = substr($lines[5] , 17); $_SERVER['HTTP_ACCEPT_CHARSET'] = substr($lines[6] , 16); $_SERVER['HTTP_KEEP_ALIVE'] = substr($lines[7] , 12); $_SERVER['HTTP_CONNECTION'] = substr($lines[8] , 12); if(strstr($lines[9] , 'Referer')) { $_SERVER['HTTP_REFERER'] = substr($lines[9] , 9); $_SERVER['HTTP_COOKIE'] = substr($lines[10] , 8); } else $_SERVER['HTTP_COOKIE'] = substr($lines[9] , 8); if(!empty($_SERVER['HTTP_COOKIE'])) $this->_parseCookie($_SERVER['HTTP_COOKIE']); $_SERVER['SERVER_SIGNATURE'] = ' <address>Azra/0.0.1 Server at ' . $_SERVER['HTTP_HOST'] . ' Port ' . $this->_port . '</address>'; $_SERVER['SERVER_SOFTWARE'] = 'Azra/0.0.1'; $_SERVER['DOCUMENT_ROOT'] = $this->_webroot; $_SERVER['SCRIPT_FILENAME'] = $this->_webRootWithoutSlash . str_replace('./' , '/' , $this->_file); $_SERVER['REQUEST_TIME'] = time(); if(!empty($this->_queryString)) $_SERVER['QUERY_STRING'] = $this->_queryString; } public function serve() { $socket = @socket_create_listen($this->_port); if (!$socket) { echo "Soket olamiyor!\n"; exit; } while (1) //daire-i ilanihaye! { $client = socket_accept($socket); $input = $this->_input = trim(socket_read ($client, 4096)); if($this->_verbose == 1) echo $input; $input = explode(" " , $input); $input = $input[1]; $mime = $this->_mimeHandler($input); if ($input == "" || $input == '/') { $input = "/index.html"; } $input = ".$input"; $inputArray = explode('?' , $input); $this->_file = $inputArray[0]; $input = $this->_webroot . $this->_file; if (file_exists($input) && is_readable($input)) { if($this->_verbose == 1) echo "Dosya: $input\n"; $contents = file_get_contents($input); if(strstr($input , '.php')) { $this->_queryString = $inputArray[1]; $this->_parseGet($this->_queryString); $this->_parseRequest($this->_input); $_REQUEST = array_merge($_GET , $_POST , $_COOKIE); $contents = $this->_handleCode($contents); //PHP çalıştıktan sonra sonucu echo etmesini değil, $contents stringine atmasını istiyoruz. ob_start(); eval($contents); //gidip $contents'in üstüne yazmak doğru alışkanlık değil ama yeni değişken register etmek istemiyorum $contents = ob_get_contents(); ob_end_clean(); } $output = "HTTP/1.0 200 OK\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: $mime\r\n\r\n$contents"; } else { $contents = "<title>UHU 404</title><h2>UHU 404</h2>Iın-ıın-ıın-ıın-cav-cav-cav-cav-oooooo - oooooo - zört - zört - zört -zört (araba alarmı efekti) Çok afedersin müdürüm ama yok öyle bir dosya."; $output = "HTTP/1.0 404 OBJECT NOT FOUND\r\nServer: HyperTextServer\r\nConnection: close\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n$contents"; } socket_write($client, $output); socket_close ($client); } socket_close ($socket); } } $server = new webserver(); $server->setWebRoot('/var/www/webserver'); //$server->verbose(); //ayrıntıları stdout'a echo et. $server->setPort(8008); $server->serve(); |
…ve benchmarklar:
Önceki yazıdaki benchmarkla aynı şartlarda ve aynı php dosyasını kullanarak 100 çalıştırma ortalaması
apache: 0.012167468071
azra: 0.00674695968628
Temiz şekilde iki kat hızlı.
socket_getpeername() de düzgün çalışabilir ve peer adresini alabilirsem $_SERVER['REMOTE_ADDR'] de dolacak ve hiç sorun kalmayacak.
Etiketler: PHP, Web Server
Bu yazı bir süredir kafamın içinde bir yerlerde yazılmayı bekliyordu; bugünü bekliyormuş.
Öncelikle yazılarını -genel olarak yaklaşım açısını- fevkalade bulduğum, beğendiğim Altan Tanrıverdi’den bir alıntı yapmak isterim:
‘Doğrusu da budur. Diğer taraftan “senin yerinde olsaydım” ibaresi ile başlayan cevaplar da yanlış sayılmaz ki birazdan ben de öyle yapacağım.’
Yazının tamamı da oldukça ilginç ve şuradan okunabilir.
Tanrıverdi yazısında, “deneyimli bir programcıdan çaylağa nasihatler” kabilinden -doğru yorumlayabildiğim ölçüde- diğer unsurların yanında, satır aralarında önyargılardan kaçınmak gerektiğinden de dem vuruyor.
Ben de fikirlerimi bu çerçeveye oturtmak bu buradan yola çıkarak düşüncelerimi geliştirmek isterim:
“Çok bilen çok önyargı sahibi olandır.” genel olarak doğru bir önerme olarak kabul görebilir, ta ki “düzgün fikir sahibi olmak” ile “önyargı sahibi olmak” ayrıştırılabilsin.
Son günlerde heyecanlı gençleri sıkça görüyorum: “Hoca, iyi diyorsun da, falancayı filanca şekilde yapsak daha iyi sonuç alamaz mıydık?”
…ve sıkça yanıtlıyorum: “Ben benchmark severim. Görelim!”
Doğal olarak işbu genç arkadaşlarımızın bir çoğu “Görürüz!” diyor da arkasını -adı üstünde “gençlik” hezeyanının sonucu- bir türlü getiremiyor.
Oysa ben ne söylüyorum? “Disiplinli düşünebilme yetisi en büyük yetenektir.”
“Disiplinli düşünme” ya da “düşünceyi disiplin altına alma” derken bir yöntemler bütününden söz ediyorum; önyargılardan değil.
Devlet dairesinde memur olacak kişiler yazının bundan sonrasını okumayabilirler, ancak gelecek için kendinden bir şeyler bekleyenler okumaya devam etmeli.
- Eğitim sistemimiz ne yazık ki analist yetiştirmek üstüne değil, “vasat adam” yetiştirmek üstüne kurulu. Bu sistemden “ukala papağanlar yetiştirmek” durumundan ötesini -ne yazıktır- elde edemiyoruz. Çocuk pırıl pırıl, harika bir sistem çözümleyicisi olacak, biz ona “hayır!” diyoruz, “önce üniversite bilmemne hadisesini kazan!” ve beynini böylece -o vakte kadar elimizden kurtarabilmişse bile- düdüklüyoruz, dimağının ırzına geçiyoruz. Ondan sonra da oradan mezun olunca adama (bakın artık “adam” oldu) “haydi, sistemlerimiz, kıçımız başımız sana emanet!” diyoruz. (Tabii işin böyle olduğunu bildiğimiz için demiyoruz ama sistem buna yönelik)
- İşbu (artık) “adam” mektebi bitirdikten sonra elimize düşüyor ve dünyanın gerçekte kaç bucak olduğunu biraz olsun görmeye başlıyor: Zaten düdüklenmiş beyniyle (özellikle yazılım sektöründen söz ediyorsak) okulda hoca olmaktan gayrısını başaramamış hocaların elinde, beyni çift kat düdüklenmiş adamdan söz ediyoruz. Söz ediyoruz ve bu adamların sektörde “insan kaynağı” diye ortalıkta dolaştığını da böylece söylüyoruz.
- “Kendini geliştirmek istiyor musun? Unut!” diyoruz, “okulda ne öğrendiysen unut, çünkü hepsi ya önyargılı, ya yanlış, ya eksik ya da düpedüz palavra! Yeni doğmuşsun gibi yap. Aç bak, dokümantasyon orada. En başından oku, hepsini oku. Sonra gel bize anlat ‘ruby fastcgi ile şöyle çalışırmış’, ‘python aslında ne kadar şahaneymiş, php bilmemneymiş’. ama önce oku! Edindiğin bilgiyi sınıflandırmak için, sana uygun gelebilecek bir yöntem geliştir. Bunları yapmıyorsan, ancak devlet dairesinde memur olursun. Çocukların da devlet dairesinde memur olur.”
Bunları söylerken devlet memuru olma durumunu aşağılıyor muyuz? Hayır. Haddimize düşmez. Öte taraftan, bir yazılımcının devlet memuru ya da okulda hoca olmak için hakikaten çok rasyonel gerekçeleri olmalı. Sizin kişisel olarak verebileceklerinizin çok çok sınırlı olduğu bir ekosistemde isterseniz allame-i cihan olun, hiç ama hiç bir şeyi değiştiremezsiniz. Oysa, bir yazılımcı “bir şeyleri daha doğru yapmak” için vardır; öyle yazılımcıdır.
Önceki gün yazdığı kod hakkında (istediği kadar iyi çalışıyor olsun) şüpheye düşmeyen adama dahi artık yazılımcı değil “kod ağası” denir.
Öğrendiklerinizin, uzun süredir eski öğrendiklerinizin yerini almaması durumunu gözlemliyorsanız (yaşınız kaç olursa olsun) kod yazmaktan elinizi-ayağınızı-eteğinizi çekmenin vakti gelmiş demektir.
Yok, aksi durumda diretiyor ve üstelik ukalalık yapmaya kalkıyorsanız, yeri burası değildir!
Etiketler: Akıl-fikir, Yazılım



