Testovací prostředí
Testovacím
serverem mě byl můj netbook Compaq s procesorem Intel Atom N270 1.60
GHz, 2GB RAM, vypnutý swap. Operační systém je Linux Debian Squeeze
updatovaný k datu 30. 7. 2010. Testy byly spuštěny z jiného stroje přes
zabezpečenou (WEP 40) WLAN v Ad-Hoc režimu. Dotazy byly prováděny na
host name, nicméně záznam byl uložen v tabulce /etc/hosts. Úzkým hrdlem
testování byla evidentně síť, která vše dost brzdila.
Každý test byl spuštěn přibližně 60
vteřin. Občas jsem si všiml velmi zvláštního jevu u serveru Apache, kdy
docházelo k nevyřízení požadavků. Tzn., server začal vracet status 500
a v logu se objevovali nesmyslné chyby typu: NameError: name ‚dispatch_table‘ is not defined
.
Do testu je pak započítán pouze čas správně vyřízených požadavků. Oba
servery byly spuštěny v „produkčním” módu, tedy s vypnutým python
debugem a zapnutou optimalizací. Server Apache dostal v průběhu testů
nějaký ten čas na rozjezd (občas mu trvalo, než začal odpovídat
relevantně rychle. Test byl tedy po „rozjetí” ukončen a znovu spuštěn.
Konfigurace serverů
httpd.conf:
# keepalive is off by default
Timeout 300
KeepAlive Off
MaxKeepAliveRequests 100
KeepAliveTimeout 15
# use client-supplied SERVER_NAME
UseCanonicalName Off
# do not lookup hostnames
HostnameLookups Off
<ifmodule prefork.c>
StartServers 10
MinSpareServers 10
MaxSpareServers 10
MaxClients 220
MaxRequestsPerChild 2000
</ifmodule>
PythonOptimize On
# document root directory
<directory>
SetHandler mod_python
PythonHandler /srv/poorpublisher/poorpublisher/poorpublisher.py
PythonDebug Off
PythonAutoReload Off
PythonPath "['/srv/poorpublisher/poorpublisher','/srv/test/app'] + sys.path"
Order allow,deny
Allow from all
<Files ~= "\.(gif|html|jpg|png|css|js|txt)$">
SetHandler default-handler
</files>
</directory>
lighttpd.conf:
server.modules += ( "mod_proxy" )
$HTTP["host"] == "test.dev" {
proxy.server = ( "" => ( ( "host" => "127.0.1.1",
"port" => "8081") ) )
# ( "host" => "127.0.1.1",
# "port" => "8181") ) )
}
poorhttp.ini:
# server type could be: single, forking or threading
# default = Single
type = forking
# exception traceback to html pages
# default False
# debug = True
# auto reloading modules when they are changed
# default False
# autoreload = True
Sériový test
První
série testů prováděla sériové dotazování. V jednom procesu byly
cyklicky generovány dotazy na server, po obsloužení jednoho požadavku
serveru byl odeslán další. Tomuto testu tedy říkám sériový test. Měřeny
byly zejména časy odpovědí (ans t) a časy kompletních stránek (res t).
V tabulce jsou dále uvedeny ans/s a res/s, což odpovídá teoretickému
počtu odpovědí/stránek za vteřinu. Skutečný průměr počítaný z celkového
počtu odpovědí a času testu je real/s. Ten by měl být vždy menší, neboť
ans t a res t jsou měřeny jako čas od spojení socketu do přijmutí 15ti
znaků, resp. do stažení celé stránky. Režie zpracování výsledků je
započítána až do real/s.
| ans/s | res/s | real/s | ans t | res t |
Single | 183,02 | 110,85 | 80,49 | 0,0054 | 0,0090 |
Forking | 59,88 | 49,16 | 40,26 | 0,0166 | 0,0203 |
Threading | 154,93 | 103,35 | 76,14 | 0,0064 | 0,0096 |
Apache prefork | 184,60 | 137,57 | 81,01 | 0,0054 | 0,0072 |
Lighttpd + Single | 79,41 | 77,52 | 57,91 | 0,0125 | 0,0128 |
Lighttpd + 2 x Single | 77,03 | 74,30 | 56,21 | 0,0129 | 0,0134 |
Sloupečky v grafu mají stejné pořadí jako v tabulce (ans/s, res/s, real/s - serial test a ans t, res t - serial time).
^ větší znamená lepší
v menší znamená lepší
Paralelní test
Druhá série
testů byla prováděna stejnou aplikací, i měření bylo totožné, jen
požadavky nebyly odesílány postupně, ale najednou 100 dotazů každou
sekundu. Dotazy byly odeslány paralelně pomocí threadů, každý dotaz byl
pak zpracován zvlášť. Všechny hodnoty jsou měřené stejně jako v případě
sériového testu, hodnota real/s je tedy počet všech stažených stránek
za dobu testu. Test ve skutečnosti netrval vždy 60 sekund, protože
některé konfigurace způsobovali zahlcení testovací aplikace. hranice
pro ukončení serveru bylo 600 nezpracovaných threadů. Tuto hranici
dosáhly konfigurace Single, Threading a Lighttpd +
Single.
| ans/s | req/s | preq/s | ans t | req t |
Single | 9,14 | 8,80 | 82,95 | 0,1093 | 0,1135 |
Forking | 5,12 | 4,84 | 60,76 | 0,1950 | 0,2065 |
Threading | 6,21 | 5,89 | 58,80 | 0,1608 | 0,1696 |
Apache prefork | 4,96 | 4,85 | 70,79 | 0,2014 | 0,2060 |
Lighttpd + Single | 0,64 | 0,64 | 79,50 | 1,5510 | 1,5514 |
Lighttpd + 2 x Single | 2,88 | 2,87 | 92,04 | 0,3471 | 0,3475 |
Sloupečky v grafu mají stejné pořadí jako v tabulce (ans/s, res/s, real/s - serial test a ans t, res t - serial time).
^ větší znamená lepší
v menší znamená lepší
Paměťové nároky
Měření
paměťových nároků je velmi obtížný proces. V první řadě se mi
nepodařilo rozumě odchytit množství pracujících procesů. Forkování
procesů se často děje metodou write-on-change a tato skutečnost není do
tabulek nijak promítnuta. Nakonec je tabulka počítána pro 4 procesy
Apache a Poor Http v režimu Forking, kdy právě 4 procesy Apache žijí v
systému. Dále je třeba brát v úvahu, že Apache procesy (alespoň
některé) běží v systému vždy, i když je klid. Všechny konfigurace, byť
jsou v tabulce uvedeny 4 procesy, byly nejdříve zatíženy 10ti a
následně 100kou paralelních dotazů. Růst paměti nebyl u žádného ze
sledovaných serverů lineární, spíše se zvedla náročnost o max. pár
stovek bytů.
Paměť byla
měřena příkazem: ps axo ppid,size,vsize,cmd | grep SLUZBA. V případě
serveru Apache jsem z grafu schválně vyjmul hodnotu 4 x size, hodnota
je příliš vysoká a ztrácí se pak porovnání ostatních konfigurací.
| size | vsize | 4 x size |
Lighttpd | 952 | 6 196 | 952 |
Poor Http Single | 4 160 | 10 440 | 4 160 |
Lighttpd + 4 x Single | 5 112 | 16 636 | 17 592 |
Poor Http Forking | 4 196 | 10 476 | 41 960 |
Poor Http Threading | 38 008 | 44 288 | 38 008 |
Apache | 228 832 | 237 156 | *917 344 |
Sloupečky v grafu mají stejné pořadí jako v tabulce (size, vsize, 4 x size).
* Číslo je vypočítáno jako 4*size + parent process size
v menší znamená lepší
Závěrem
Každé opakování testu
vede k malinko jiným výsledkům, proto jsem test prováděl po sobě, tak,
aby nálada testovacího systému ovlivnila měření co nejméně. Co se
vývoje aplikací týče, Poor Http má daleko lepší autoreloading modulů,
resp. skutečně reloaduje každý modul, pokud je tak nastaven. Je ale
důležité, že Single mód je tzv. blokující, a v případě zdržení jednoho
requestu se server zablokuje (nastane deadlock). Tento nešvar je možné
částečně odstranit použitím nějaké proxy před serverem. Lighttpd navíc
disponuje tzv. load-balancing konfigurací, kdy je možno zátěž rozložit
mezi několik serverů, což je případ právě Lighttpd + 2 x Single. Tak či
tak, Poor Http si proti Apache nestojí vůbec špatně, i když je znatelně
méně výkonnější, pro menší projekty, vývoj a pro místa s menší RAM je
rozhodně vhodný, neztratí se ale i v jiném náročnějším prostředí.