Webes oldalak, PHP alkalmazások sebességének javítása

Default book

A webes alkalmazások sebességének növelése örök téma a webfejlesztők körében. Ez rendkívül összetett probléma, hiszen a sebesség növelése több összetevőtől függ. Hogy a gyorsítás lehetőségeit megérthessük, át kell tekintenünk, hogy mi történik a böngészőből elküldött kérés és a visszaérkező válasz között.

  1. A böngésző parancssorába beírunk egy URL-t, vagy egy űrlapon megnyomjuk a küldés gombot. Ekkor a böngésző egy TCP/IP kapcsolatot nyit a webszerverrel és ezen a kapcsolaton át elküld egy kérést a webszervernek, amelyben a kért oldal címe található, az esetlegesen elküldött adatok, a küldés módja (GET, vagy POST), néhány adat magáról a böngészőről. A kapcsolatot nyitva tartja, és ezen keresztül várja a szerver válaszát.

  2. A Webszerver a kapott kérést fogadja és a kérésben szereplő URL-ből megállapítja, hogy a kért fájl kép, css, javascript, applet, stb... vagy php fájl-e. Ha nem PHP fájlról van szó, akkor a fájlt megkeresi a fájlrendszerben beolvassa és a tartalmát visszaküldi a böngészőnek. A böngészőnek elküldi a fájl mime típusát is, ami a böngészőnek leírja, hogy mit kezdjen a fájllal, hiszen egy képet másképpen kell kezelnie, mint egy szöveget. Ha PHP fájlról van szó, akkor a kérés paramétereit átadja a szerveren futó PHP motornak.

  3. A PHP motor az átvett paraméterek alapján beolvassa a fájlt a fájlrendszerből. A programot értelmezi, egy köztes kódot, úgynevezett op-code-ot hoz létre, majd az op-code-ot végrehajtja (az értelmezésről később). Ha a php kód tartalmaz include fájlokat, akkor azokat menet közben beolvassa és értelmezi.

  4. Futás közben a PHP motorhoz kapcsolódó különböző moduljain keresztül igénybe vehet egyéb erőforrásokat is, mint például adatbázis-kapcsolat kiépítése, adatok lekérdezése illetve feltöltése adatbázisba, illetve egyéb lehetőségek. Az adatbázis kapcsolat kiépítése talán az egyik leglassabb művelet, ugyanis közben megoldástól függően a PHP modul TCP/IP kapcsolatot épít fel az adatbázis szerverrel (vagy csak egy memória közös területen kommunikál vele), ami időbe telik.

  5. A mai website-ok gyakran az adatbázis tartalom alapján állítják elő a kimeneti tartalmát. A PHP motor által előállított kimenet általában HTML tartalmat jelent. Ehhez a tartalomhoz a PHP motor automatikusan hozzáfűz úgynevezett HTML fejléceket (header).

  6. Ezt a tartalmat elküldi a webszervernek.

  7. A Webszerver a tartalmat visszaküldi a böngészőnek, és többnyire bontja a TCP/IP kapcsolatot és új kérésre vár.

Gyorsítási lehetőségek

  • A Böngésző és a szerver közötti kapcsolat sebessége adottság. Elsősorban a kliensnél található hálózati sávszélesség határozza meg a maximális lehetőségeket. A beérkező kérések memóriát foglalnak le. A nyitott TCP/IP kapcsolatok száma arányos a beérkező kérések számával. Ha egy időben több százezer kapcsolat van nyitott állapotban és a kéréseket a szerver nem tudja időben kiszolgálni, akkor időtúllépésre fut a rendszer és a böngészők nem kapnak idejében választ. Megoldás: bővítsük a hardvert.

  • A nagy terhelésű rendszereknél gyakori, hogy külön egy http cache-t tesznek a rendszerbe, amely a lekért oldalak tartalmát bizonyos ideig megőrzi kiküldés után és azonos kérésre ugyanazt állítja elő. Ilyenkor nem kell a PHP és az adatbázis-kezelő folyamatait igénybe venni a kiszolgáláshoz. A http cache lehet egy szoftver, ami a szerveren fut és lehet egy "előtét" számítógép, amely az igazi webszerver és a kliens között található. Olyan esetben hatékony a használata, amikor ugyanazt a tartalmat gyakran kérik le közel azonos időben, mint pl. egy újság esetén. Ilyenkor lehet úgy is konfigurálni a webszervert, hogy a PHP és nem PHP kéréseket más és más szerver szolgálja ki és lehetséges, hogy ugyanazon a gépen másik fajta web szerver szoftver. A gyors háttértár elengedhetetlen. A szerver hardverének bővítésével lehet gyorsítani a rendszert.

  • A PHP motor által előállított op-code újrafordítását megelőzendő az op-code cache-elésével lehet gyorsítani. A PHP 5.X.x sorozatban akár a Zend Optimizer, akár más cache megoldás is szóbajöhet.

  • A megfelelő algoritmusok használata.

  • Adatbázis műveletek gyorsítását az adatbázis-szerver és a PHP közötti kapcsolat gyorsításával lehet megoldani, illetve az adatbázishoz küldött kérések optimalizálásával a rendszer gyorsulhat.

  • A forráskód oldalon kevesebb letöltendő fájl létrehozásával. Ez alatt értem azt, hogy ne sok apró css vagy javascript fájlt töltsön le a böngésző, hanem egy-egy nagyobbat. Ehhez az kell, hogy a szerver oldalon az említett fájltípusokat összegyűjtse egy algoritmus és elvégezze az összefűzést, illetve a tömörítést.

  • A szerver által kiküldött adatok optimalizálása során a tartalmat áttekintve megszabadíthatjuk bizonyos felesleges whitespace-ektől így csökkentve a kimenő adatok méretét, illetve a tartalmat a szerver oldalon tömöríthetjük (gzip, zip) is. További lehetőség a kiküldendő adatokat cache memóriába tétele.

  • A kliens oldali feldolgozás sebességének javításával - A szerver oldalnak erre nincsen hatása hacsak az nem, hogy olyan weboldalakat készítünk, amelyek kevés kérést küldenek ki a szervernek.

A hardverfeltételek bővítésének lehetőségei

  • Ha már nem elég egy szerver, akkor el lehet gondolkodni azon, hogy több szerverből álló szerverparkot hozunk létre, és olyan megoldásokat alkalmazunk, amikor az önálló hardverek elosztják a terhelést egymás között.

  • A gyorsabb háttértár jelentheti azt, hogy olyan RAID megoldást alkalmazok, amely gyorsítja az adatelérést (A RAID megoldásokról itt találsz vizuálisan is értelmezhető megoldásokat ).

  • A memória mérete kritikus, ugyanis a memóriaelérés sebessége nagyságrendileg 1-2 GB/s, míg a háttértárak adatelérése kb. 10 szer lassabb. Mivel a memóriában lévő adatokat lehet a leggyorsabban kiszolgálni, ezért minden olyan megoldás, amely a kiszolgálandó adatokat memóriában tartja a lehető leggyorsabb lesz.

  • A processzor sebessége a programok futási sebességénél számít.

A fenti lehetőségek abban az esetben, ha nem mi kezeljük a hardvert, akkor nem jöhetnek szóba.

A szerveren futó alkalmazások hatékonyabb konfigurálása

Nézzünk egy tipikus önálló, egyszerű standalone webszervert. Van elég gyors háttértár, gyors processzor és elegendő memória. Szeretnénk gyorsítani a webkiszolgálást kizárólag konfigurációval. Ez külön tudomány, ezért egy későbbi cikk témája lesz.

Default konfiguráció

Most legyen elegendő annyi, hogy nem a default választás lesz a leggyorsabb. Választhatunk az operációs rendszerek között. Windows, Linux disztribúciók, BSD, stb. Nem ugyanolyan sebességgel futtatják ugyanazokat az alkalmazásokat.

Más webszerver

Választhatunk más webszervert az Linuxokon az Apache vagy a Windows rendszereken az IIS helyett. Érdemes utánanézni az nginx, a Lighttpd webszervereknek. Linux disztribúciókon és Windows rendszereken is lehet őket futtatni.

Más adatbázis-motor

Választhatunk más adatbázis motorokat, nem feltétlenül MySQL adatbázis szerverben kell gondolkodni. Az elterjedt PostgreSQL szintén jól használható teljes kör? adatbázis-kezelő rendszer. Egyes esetekben érdemes a "Non SQL" területre is merészkedni. Ezek az adatbázis-kezelők az rekordokat asszociatív indexeken keresztül érik el. Ha szöveges információt tárolunk egy adatbázisban, akkor gyakran ezek az adatbázis-kezelők gyorsabbak, mint a hagyományos RDBS-ek.

A szerveren futó kód hatékonyabbá tétele

Az alábbiakban egy kis elméleti eszmefuttatást végzek a szerver oldali programok lelkivilágáról.

Fordító, fordítás (Compiler, compiling ) Egy program írása során sima text állományokat szerkesztünk. A program csak akkor fut a számítógépen, ha az adott processzor és operációs rendszernek megfelelő bináris kódot készítünk belőle. Ezt hívják fordításnak  (compiling). A fordító program (compiler) a program forrásszövegét lefordítja egy köztes állapotba (object kód), majd egy szerkesztő (linker) az operációs rendszerhez illeszkedő kóddal összeszerkesztve az adott környezetben futtatható kódot hoz létre. Ennek előnye, hogy a futtatott kód a processzoron elérhető legnagyobb sebességgel fog futni. További előnye, hogy a lefordított programból nehéz, bár nem lehetetlen visszafordítani az eredeti forráskódot (általában nem éri meg). Hátránya, hogy a program módosításánál újra kell fordítani a programot, az elkészült verziót telepíteni, a terjesztéséről gondoskodni, ami macerás. A fordítás alkalmanként rengeteg időbe és energiába kerül.

Értelmező (interpeter) - A másik véglet, amióta programozók léteznek az, hogy olyan rendszert használunk, amely a megírt programszöveget magát futtatja. Általában utasításonként (régebben soronként) értelmezi, azaz megvizsgálja, hogy szintaktikailag helyes-e a programkód, majd végrehajtja. Ezt a szoftvert értelmezőnek hívják (interpreter) és a fentieken kívül gondoskodhat a hibakezelésről és még egy sor egyéb dologról is. előnye ennek a rendszernek az, hogy a fejlesztés során egy hiba megjelenése és kijavítása után azonnal futtathatjuk a programot, hiszen nem kell lefordítani azt. Gyorsabbá válik a fejlesztés, de ennek az ára a lassabb futásban jelentkezik. Az értelmező az utasítások végrehajtása során mindig értelmezi a kódot, még akkor is ha századszor fut ugyanazon a részen. Ha egy másik forráskódban van egy futtatandó programrészlet, akkor annak a meghívása a másik szöveg megnyitását vonja maga után. Az így megírt program a binárisra lefordított programhoz képest akár 10-20x is lassabban futhat.

Bájtkód - A legjobb megoldás már régóta létezik és a két fenti módot ötvözi össze. Az interpreter a programszöveg megnyitásakor a programszöveget egy úgynevezett bájtkódra fordítja. A fordítás automatikus és ameddig a kód fut, addig a memóriában is marad a bájtkód. Ennek az előnye triviális. A lefordított kódot már nem kell szintaktikailag ellenőrizni, mert biztosan hibátlan. A kód mérete kisebb, mint a text verzió, tehát a memóriában kevesebb helyet foglal. A futtatott program közel a binárisra fordított programok sebességével fut. Az így lefordított program szerkesztése is gyors, mert a forrásszöveget módosítva azonnal futtathatom a kérdéses programot. Ezt a megoldást a PC-s világ először a DBASE => CLipper programoknál alkalmazta (tudtommal), de a JAVA forrásállományok fordítása során létrejövő class állományok is bájtkódra fordított kódok. Persze, hiszen nincsen új a Nap alatt!

A PHP.EXE egy interpreter

Sajnos ez sem elég gyors sokszor, ezért a Facebook 2010-ben készített egy fordítót (HipHop), amely a PHP forrásból C++ forrást hoz létre és ebből egy bináris állományt készít.

A fenti gondolatot gondolta tovább néhány cég, és fejlesztő, akik úgy gondolták, hogyha a lefordított opcode a memóriában van, akkor az oldal következő hívásakor is a memóriából kellene kiszolgálni, amivel meg lehet takarítani a háttértárról való olvasást és az opcode-ra való fordítást is. Amikor egy oldalt meghívnak a szerveren beolvassa a PHP kódot a memóriába, opcode-ot fordít belőle, majd egy cache memóriába helyezi az opcode-ot. Ha később szüksége lesz rá, akkor a forráskód időpontját összehasonlítja a memóriában lévő opcode fordításának időpontjával és ha nem változott a forráskód, akkor a cache-ben lévővel szolgálja ki a kérést, ha módosult, akkor az új verziót tölti be. A cache elévülési idejét is meg lehet adni és kevés memória esetén akár a háttértár is lehet az opcode tárolásának helye (ez utóbbi ugye fából vaskarika).

Ilyen program a Zend által hivatalosan támogatott Zend Optimizer+ vagy az open source világból az APC, az eAccelerator, Turc MemCache, XCache, a Windows világban a WinCache illetve a fizetősek közül az Ioncube PHP Accelerator.

A fent felsorol programok mindegyike kicsit más körülmények között nyújtja a maximumot és más a felhasználási lehetősége is.