Disassembler

Artificial intelligence is no match for natural stupidity.
02srpen2012

Apache a jeho Multi-Processing Moduly


V jednom z minulých článků jsem nahlas rozjímal o výhodách a nevýhodách různých webových serverů a přemítal, který z nich si vyberu pro svůj nový virtualhost. Se svou averzí k Apache jsem se nakonec rozhodl pro nginx. Ovšem pod tíhou nových skutečností (a hlavně pod tíhou několika tisíc .htaccess souborů v transferovaných webech) jsem si řekl, že Vinnetouovi nakonec šanci dám. A moc dobře jsem udělal.

Zakopaná sekera


Netušil jsem totiž, že Apache za dobu, co ho nemám rád, vyspěl do takové míry, že se už dá dokonce i používat. Měl jsem ho totiž zafixovaný jako rozežraný moloch se starým zkostnatělým prefork modelem, schopný sežrat půl paměti při nečinnosti a celou paměť, včetně swapu všech okolo stojících počítačů, při zátěži. Jaké bylo moje překvapení, když jsem zjistil, že worker MPM s pamětí hospodaří nejen rozumně, ale navíc je i velmi dobře škálovatelný, takže se dá efektivně použít jak pro malé webíky s pár desítkami přístupů měsíčně, tak i pro vhosta s několika stovkami domén a myriádami nášlapných min s příponou .php. A i hrozba kolaterálních škod způsobené touhle třípísmenkovou hrůzou se dá celkem snadno eliminovat užitím vhodných nástrojů, jako třeba PHP-FPM. A právě u výše jmenovaných, tedy worker MPM a PHP-FPM jsem zůstal i já.

Vidličky a nitě


Když jsem zjistil kolik procesních modelů a modulů dnes Apache nabízí, byl jsem příjemně překvapen. Důvody toho, proč nemám rád klasický prefork jsem naznačil v článku „Závody webserverů“, ale pro úplnost je připomenu i teď. Prefork nepoužívá vlákna. Místo nich používá prachsprosté podprocesy, takže je vhodný pro webové aplikace používající non-thread-safe (čti: „prasácky napsané“) knihovny. Třeba PHP. Vtip je totiž v tom, že všechna rozšíření, všechny knihovny a eventuálně i všechny binárky se kterými se Apache spřáhne, běží v každém z podprocesů a zpracování požadavku se odehrává pouze a výhradně v paměťovém prostoru daného Apache podprocesu. Co se týče bezpečnosti, je to super, protože když si dá cokoliv uvnitř na tlamu, vezme s sebou jen ten jeden jediný podproces a nikdo jiný si toho ani nevšimne. Co už ale není super, je fakt, že paměťová náročnost takového nastavení roste s každým novým rozšířením geometrickou řadou.

Pak tu máme worker model, který staví na klasickém modelu s vlákny, který se v praxi velmi osvědčil u „lehkých“ serverů jako lighttpd nebo nginx. I když v podání Apache se jedná o hybrida, protože se nespawnují přímo jednotlivá vlákna, ale podprocesy s kýblem vláken naráz. V nečinném stavu se v rohu paměťového banku krčí pouze několik podprocesů, které netrpělivě očekávají vaše požadavky a při zátěži se Apache rozparádí tak, že se vlákna střídají rychleji než na tkalcovském stavu. A když špička utichne, využití paměti opět vypadá, jako by se nikdy nic nestalo.

Poslední z trojice modelů pro *nix-based systémy je event model, který funguje podobně jako worker. Dokumentace sice tvrdí, že pro Apache 2.2 je model stále v experimentální fázi, ale i přesto funguje dostatečně spolehlivě na to, aby jej bylo možno bez obav použít i na produkčních systémech. Na rozdíl od workeru eventová vlákna nenakukují do fronty (polling), ale poslušně čekají a reagují na události vyslané systémem. Event model se výborně hodí (resp. je primárně určen) pro servery s vysokým počtem keep-alive spojení, protože má vlastní dedikované vlákno, které dělá v již otevřených spojích pořádek.

Mimo tři výše uvedené sice pro *nixy ještě existuje ITK model, ale ve skutečnosti jde jen o klasický prefork s tím, že každý vhost může běžet pod jiným systémovým uživatelem. Dále existují MPM specificky navržené pro další operační systémy. Jmenovitě - beos, mpm_netware, mpmt_os2 a bohužel i mpm_winnt, který, i když za to tak úplně nemůže, je ostudou všech webserverů. Kdykoliv vidím trio Apache + PHP + MySQL na Windowsovském serveru, přemýšlím, do které kategorie mentálně retardovaných musel architekt takového prostředí patřit. A zvlášť dnes, kdy je deployment webserveru na linuxu záležitostí jednoho yum nebo apt-get příkazu.

Konfigurace modelů


U preforku je konfigurace vcelku snadná.

<IfModule mpm_prefork_module>
    StartServers          5
    MinSpareServers       5
    MaxSpareServers      10
    MaxClients          150
    MaxRequestsPerChild   0
</IfModule>

U workeru je situace poněkud složitější, ale trocha experimentování a zdravého selského rozumu vás jistě rychle ke vhodným hodnotám navede.

<IfModule mpm_worker_module>
    StartServers          2
    MinSpareThreads      25
    MaxSpareThreads      75
    ThreadLimit          64
    ThreadsPerChild      25
    MaxClients          150
    MaxRequestsPerChild   0
</IfModule>

Noticka k worker MPM


Zpočátku se všechny ty údaje a možnosti nastavení worker multi-processing modulu mohou zdát nepřehledné a matoucí, ale zkusím v tom udělat trochu jasno. Jak už jsem se zmínil výše, je třeba si nejprve uvědomit, že worker MPM nespawnuje jednotlivá vlákna, ale rovnou celý nový podproces s pevně daným počtem vláken. Po spuštění Apache tedy bude naspawnováno StartServers procesů z nich každý bude mít ThreadsPerChild vláken. V konfiguraci uvedené výše nás tedy po startu budou netrpělivě očekávat dva podprocesy po pětadvaceti vláknech, celkem tedy 50 vláken připravených plnit rozkazy. Je-li na serveru nával, Apache vytvoří další podproces s ThreadsPerChild vlákny. V našem případě tedy budeme mít 3 podprocesy a 75 vláken, pak 4 podprocesy s celkem 100 vlákny až dokud Apache nedosáhne ServerLimit podprocesů. Výchozí ServerLimit, který v konfiguraci výše není vidět, je 16, takže Apache může při návalu může naflákat teoreticky až 400 pracovních vláken, ale protože máme nastaven MaxClients na 150, počet vláken se bude plazit někde okolo tohoto čísla. A když špička opadne, začne podprocesy zabíjet tak, aby byl celkový počet vláken menší nebo roven MaxSpareThreads.