A cache probléma végleges megoldása

img

Közel 3 hete foglalkoztam a blogon utoljára a kérdéssel, mi legyen a cache-sel. Akkor csak körvonalakban volt meg, hogy mit szeretnék, közben teljesen rendet raktam a káoszban. Első fő kérdés, hogyan tároljam az értékeket. Két lehetőségem volt, serialize vagy json_encode. Ezután végeztem egy mérést, hogy melyik a gyorsabb. A tesztnél fontos volt, hogy a letárolás és visszaolvasás együttes sebessége legyen gyorsabb. Így végül a serialize - unserialize páros nyerte a versenyt.

Ezután felvetettem a kérdést, hogy letároljam-e a teljes HTML kimenetet. Ökörség lett volna ezt is szerializálni, úgyhogy ez a lépés kimaradt, helyette kapott a cache egy bővítést, így képes letárolni a teljes HTMLt is. Ezen még majd módosítani kell kicsit, de jelenleg teljesen tökéletesen végzi a dolgát.

Miután kész lett a cache és elfogadható állapotba került, gondoltam egyet, és újraírtam a blog alapját. Eddig egy TextPress motor hajtotta az oldalt, ami a Slim keretrendszerre épült. Ennek egyetlen hátránya volt, hogy nehezen tudtam bővíteni, mert folyamatosan a Slim kódjában kellett matatni. Nehezebb volt hibát keresni benne, nehezebben tudtam új szolgáltatásokat beépíteni. Túl bonyolult volt a rendszer a tudásához képest. A mostani rendszerbe áthoztam pár apróságot a TextPressből, például a bejegyzések formátumát, ami annyit jelent, hogy az md file elején van egy JSON fejléc, amiben benne vannak a főbb adatai a bejegyzésnek. Mivel rengeteget olvasgattam mostanában külföldi blogokat, rátaláltam egy másik MarkDown feldolgozóra, amit ParseDown névre kereszteltek. A tesztek szerint jóval gyorsabb, mint a "hivatalos" Markdown parser. Például 1-1 blogbejegyzésem tesztelésekor 6-7× gyorsabban és ráadásul pontosabban jelenítette meg az eredményt.

Tehát a végső cache megoldás megszületett. Jelenleg teljesen file alapú, így a szerverek 95%-án futtatható. Készül egy Memcached változata is, de azt már csak a saját szerveren fogom a kész programban beüzemelni.

Egy kis technikai leírás, azaz, mit és hogyan

A teljes Cache osztály 150 sornyi kódból áll (formázva). Tehát nem túl nagy dolog.

Szóval metódusok:

  • public get
  • public set
  • public clear_cache
  • private get_file_path
  • private auto_cleaner

get(keyword, get_all=false)

$path = $this -> get_file_path($keyword);
if (!file_exists($path)) {
    return null;
}
$object = unserialize(file_get_contents($path));
if (!empty($object['expired_time']) && time() >= $object['expired_time']) {
    @unlink($path);
    $this -> auto_cleaner();
    return null;
}

if ($get_all) {
    return $object;
} else {
    return $object['value'];
}

set(keyword, content='', time=0, rewrite=0)

if ((int) $time <= 0) {
    $time = 3600 * 24 * 7;
}
$object = array(
    "value" => $content,
    "write_time" => time(),
    "expired_in" => $time,
    "expired_time" => time() + (int) $time
);

$value = serialize($object);
$path = $this -> getFilePath($keyword);

$write = 1;
if (file_exists($path) && !$rewrite) {
    $old = unserialize(file_get_contents($path));
    $write = 0;
    if (!empty($old['expired_time']) && time() >= $old['expired_time']) {
        $write = 1;
    }
}

if ($write) {
    file_put_contents($path, $value);
}

clear_cache(check=0)

$iter = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(CACHE_FOLDER, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD);
foreach ($iter as $path => $dir) {
    if ($dir -> isDir()) {
        $path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
        foreach (glob($path . '*.Cache') as $file) {
            if ($check){
                $object = unserialize(file_get_contents($file));
                if (!empty($object['expired_time']) && time() >= $object['expired_time']) {
                    @unlink($file);
                }
            } else {
                @unlink($file);
            }
        }
    }
}
clearstatcache();

private get_file_path(keyword)

$filename = strtolower(trim(preg_replace("/[^a-zA-Z0-9]+/", "_", $keyword), "_"));
$folder = CACHE_FOLDER . substr($filename, 0, 3) . DIRECTORY_SEPARATOR;
if (!file_exists($folder) || !is_dir($folder)) {
    mkdir($folder, 0777, 1);
}
return $folder . $filename . '.Cache';

private auto_cleaner()

$autoclean = $this -> get('run_auto_cleaner');
if ($autoclean == null) {
    $this -> set('run_auto_cleaner', 'auto_clean', 3600);
    $this -> clear_expired(1);
}

Ha szeretnél vele játszani kicsit, akkor letölthetővé tettem, magyar kommentekkel: Cache class letöltése

Fontos megjegyzések

  • Az osztály használatához >= PHP 5.3 verzió fog kelleni, ha jól emlékszem.
  • A fájlműveleteknek engedélyezve kell lenni a szerveren.
  • A CACHE_FOLDER konstans értékének a megfelelő cache mappára kell mutatni.