PHP gyorstárazás APC alapokon
PHP programozók életében is előbb utóbb bekövetkezik a pillanat, amikor szükségét érzik gyorsítótárazás alkamazásának. Ennek oka lehet puszta szakmai perverzió, vagy megnövekedett terhelés kicsiny szerverünkön. A rendelkezésünkre álló sok-sok megoldás közül én most az APC-t szeretném röviden bemutatni némi kiegészítéssel.
APC telepítéséhez a PHP könyv idevágó fejezetében találunk segítséget. Ez Ubuntun a sudo apt-get install php-apc
parancs végrehajtását jelenti.
Mit is tud az APC? PHP fájljainkat előre lefordítva eltárolja a memóriában, ezzel gyorsítva a kimenet generálását. Lehetővé teszi, hogy különböző értékeket tároljunk memóriában és képes arra is, hogy oldalunkon PHP alapú progress bar-t alakítsunk ki.
Nézzük meg most, hogy hogyan támogatja változók tárolását ez a megoldás. Működő rendszer kialakításához elég négy függvényt megismernünk.
apc_add()
– Eltárol egy változót a cache-ben. Azonos név esetén nincs felülírás.apc_store()
– Eltárol egy változót a cache-ben. Azonos név esetén felülírja a létező értéket.apc_fetch()
– Visszaad egy értéket a cache-ből.apc_delete()
– Töröl egy értéket a cache-ből.
Mindegyik függvényről kielégítő leírást találhatunk az őket bemutató oldalakon. Néhány probléma azonban felmerülhet használatukkor. A kulcs (ami szerint tároljuk az értéket) az egész webszerverre érvényes. Biztosítanunk kell, hogy alkalmazásaink ne zavarják egymást, valamint a tárolt értékek csak egyszerű változók, vagy egydimenziós tömbök lehetnek.
Az első problémára egy előtag alkalmazása nyújthat megoldást, a másodikra pedig a serialize()
-unserialize()
függvénypáros.
Nézzünk hát egy osztályt ami kényelmes felületet biztosít az APC fölé.
<?php
class Cache {
const prefix='prefix_';
protected $success;
protected $doCache;
function __construct($bool=true){
$this->setCache($bool);
}
function setCache($bool){
$this->doCache=(bool)$bool;
$this->success=(bool)$bool;
}
function __get($name){
return $this->$name;
}
# ⋮
Lesz egy konstansunk, amit prefixként alkalmazhatunk, a $success
változóban tárolhatjuk az apc_fetch()
függvény sikerességét, valamint ha ki szeretnénk kapcsolni a gyorstárazást alkalmazásunkban, azt egy setCache(false)
hívással könnyen megtehetjük.
# ⋮
function store($name,&$var,$ttl=600){
if(!$this->doCache) return false;
return apc_store(self::prefix.$name,serialize($var),$ttl);
}
function add($name,&$var,$ttl=600){
if(!$this->doCache) return false;
return apc_add(self::prefix.$name,serialize($var),$ttl);
}
function fetch($name){
if(!$this->doCache) return false;
return unserialize(apc_fetch(self::prefix.$name,$this->success));
}
# ⋮
A store()
, add()
, fetch()
függvényekkel az APC alap funkcióit egészítettük ki oly módon, hogy bonyolult struktúrák tárolására is alkalmassá tettük őket.
# ⋮
function fetchSimple($name){
if(!$this->doCache) return false;
return apc_fetch(self::prefix.$name,$this->success);
}
function addSimple($name,&$var,$ttl=600){
if(!$this->doCache) return false;
return apc_add(self::prefix.$name,$var,$ttl);
}
function storeSimple($name,&$var,$ttl=600){
if(!$this->doCache) return false;
return apc_store(self::prefix.$name,$var,$ttl);
}
# ⋮
Ez a hármas az eredeti add()
, store()
, fetch()
hármas fájó hiányát szünteti meg.
# ⋮
function delete($what,$regexp=false){
if($regexp){
$info=apc_cache_info('user');
foreach($info['cache_list'] as $cache){
if(strpos($cache['info'],self::prefix)===0){
$cachename=substr($cache['info'],strlen(self::prefix));
if(preg_match($what,$cachename)){
apc_delete($cache['info']);
}
}
}
}else{
apc_delete(self::prefix.$what);
}
}
function getInfo(){
return apc_cache_info('user');
}
}
?<
Valamint van egy függvényünk törlésre (egyszerű, reguláris kifejezésen alapuló) és némi gyorstár információ visszaadásra.
Végül hadd mutassam be az osztályt működés közben.
<?php
$CACHE=new Cache();
$eroforrasigenyesInformaciok=$_CACHE->fetch('durvaInfok');
if(!$CACHE->success){
$eroforrasigenyesInformaciok=durvainfoEloallitoFuggveny();
$CACHE->store('durvaInfok',$eroforrasigenyesInformaciok);
}
#itt már dolgozhatunk az $eroforrasigenyesInformaciok-kal
?>
Néhány plusz sor beszúrásával komoly feladatokat vehetünk le webszerverünk válláról (jut eszembe még egy hátrány: parancssorból nem működik az APC), ha mindez némi figyelmetlenséggel is társul, akkor könnyen helyezhetünk el új feature-öket is programunkban. Én jó szívvel ajánlom bárkinek az APC-t, mind a használata, mind a telepítése igen egyszerű.
■
Tetszik
normal_folyamat, cache_folyamat
(errata: APC linknél egy -p- betű hiányzik)
(errata: APC linknél egy -p-
Köszi. csak az a baj, fogalmam sincs hogy tudom javítani :)
Javítottam
Jó!
Ami az APC-t illeti, leginkább egy gépes setupokban érdemes használni, több gépes konstrukciónál ugyanis fura jelenségeket fog okozni a különböző cache tartalom.
Bizony
bar a cikk nem tert ki ra, de
Tyrael
Valóban
Ha opcode cache, akkor
ki tudnad fejteni? xcache-hez
xcache-hez is van webadmin ha ilyesmit ertesz alatta.
mas kerdes, hogy a debiannal basznak belecsomagolni a csomagba. :)
Tyrael
APC több gépes környezetben, észrevételek
Adott esetben az hogy hostonként más kerül a cache-be, az még kívánatos is lehet, ha mondjuk az külön fontos, hogy oldalanként mindig más tartalom jelenjen meg, így APC-be cache-elve megtehető, hogy hosszabb időre cache-elünk, mert a különböző hostokon jó eséllyel más lesz, a kérések pedig jó eséllyel más gépre esnek be.
Az is lehet use case, hogy APC-be teszünk egy Memcache-ből lekért adatot azért, hogy onnan gyorsabban ki tudjuk szedni (cache chain).
Arra érdemes figyelni, hogy Memcache-sel ellentétben itt nincs LRU, ezért fontos, hogy az APC által használható memória jól legyen méretezve.
Az számára adott memóriát érdemes időről időre checkolni, mert pl. egy hirtelen becommitolt fórum tudhat meglepetést okozni. Ehhez segítséget nyújt az APC-hez mellékelt apc.php, ami hasznos infókat tud megjeleníteni.
A külön serializálást nem érzem jó ötletnek, az APC ezt magától is megteszi, duplán dolgoztatjuk (és ugye pont olyan esetről beszélünk, ahol fontos a teljesítmény).
Az még érdekes lehet, hogy ha a legjobb teljesítményre van szükségünk, akkor úgy érdemes beállítani, hogy nem figyelje a fájlokat, hogy változtak-e. Ilyenkor magunknak kell megoldani, hogy kód változtatás esetén az APC-t ürítsük.
A cache osztály kapcsán pedig amire érdemes figyelni, hogy gyakran az is fontos, hogy tudjunk empty értéket és tárolni (pl. egy DB elkérdezés null-t ad vissza, ami lehet normális, de ha azt nézzük, hogy a cache null-t ad-e vissza, akkor szét stresszeljük a DB-t). Ezért érdemes a wrapperünket úgy megcsinálni, hogy ha üres értéket akarunk lerakni, akkor cserélje le egy valamilyen normális esetben nem előforduló karakter sorozatra, és kiolvasáskora is tegyük meg a konverziót, valamint a cache osztály nyújtson egy isEmpty jellegű metódust, ami meg tudja mondani, hogy üres értéket olvastunk-e.
egy ismerosom ezen
erre lehet latni tobbfele megoldast:
- tedd a cache-t localba (pl. redis-szel kapcsolatban lattam olyat, hogy irni mindenki a tavoli kozos redis-be ir, de minden www node-on fut egy redis master, ami localban halozati roundtrip nelkul elerheto)
- a leheto legkevesebbet nyulj ki a halozatra (jellemzo erre pl. a multi-get jellegu keres, tehat nem egyesevel kerdezed le a cache-bol az adatokat, hanem egyszerre az osszeset, ami kell az adott lapletoltesre)
- az elso megoldast kicsit megvarialva: kombinald a kulonbozo technologiakat, tehat pl. central memcache-t hasznalsz, de az eredmenyeket local apc-ben/xcache-ben is cacheled, itt valoszinuleg nincs annyi felesleges memoriad, viszont a leheto legkevesebb idobe tellik a lookup(nincs semmilyen context switch, vagy inter-processz kommunikacio)
minden attol fugg, hogy mi az, amit megenged az alkalmazasod, es a rendszered.
ps: tudom, hogy ezekkel tisztaban vagy, nem neked szol, csak a cache chain-rol.
Tyrael
APC