Скажи кэшированию… иногда. Часть 2: Memcache
by Larin
В своей первой статье о кэшировании, я рассказывал о кэшировании информации в файлы. Данный тип кэширования можно использовать на всех хостингах, для него не требуется установки какого-либо специфического ПО (разумеется хостинг должен поддерживать PHP).
Сегодня я расскажу о кешировании при помощи MemCache (и еще ссылочка).
В предыдущей статье мы создали 2 класса: Cache & File. В этой статье нам снова пригодится класс Cache, только на этот раз мы заменим его на интерфейс (FX Poster, спасибо за поправку) и будет называться CACHE_ICache (почему, думаю, поймете позже).
Таким образом интерфейс CACHE_ICache будет выглядеть следующим образом:
<?php
interface CACHE_ICache
{
public function save($value, $valueID);
public function load($valueID, $timeLife);
public function delete($valueID);
}
?>
Установка memcache под Windows
- Скачиваем сервер memcached (memcached-1.2.1-win32.zip)
- Распаковываем полученный архив на диск, например, в c:\memcache\
- Далее запускаем из командной строки: c:\memcache\memcached.exe -d install, тем самым устанавливая memcached как сервис.
- Теперь, осталось только запустить этот сервис: c:\memcache\memcached.exe -d start
- Все, memcache установнен на вашей машине и уже работает. Список всех доступных команд получаем так: c:\memcache\memcached.exe -h
Установка модуля для PHP
Здесь тоже все предельно просто.
- Скачиваем расширение для установленной у вас версии PHP
- Копируем полученный файл php_memcache.dll в директорию расширений для PHP. Например, у меня это C:\server\usr\local\php5\ext\
- Добавляем в файл php.ini строку extension = php_memcache.dll, в раздел Dynamic Extensions.
- Перезапускаем сервер. Все, memcache установлен! Данный факт можно проверить при помощи функции phpinfo();
Установка под Linux
А линуксоиды и сами справятся с установкой. Один совет, устанавливайте из репозитария – меньше гемороя будет. :)
Например, для Debian: apt-get install memcached libmemcache0 php5-memcache и будет вам счастье.
Любителям гемороя могу посоветовать How to install memcache on Debian Etch.
Создание класса
Подготовительный этап окончен, теперь приступаем к программированию. У нас уже есть интерфейс CACHE_ICache описывающий необходимый интерфейс классов кэширования. Теперь создадим класс кэширования для memcached и назовем его CACHE_MemCache.
<?php
class CACHE_MemCache implements CACHE_ICache
{
private $memcache;
private $timeLife;
private $compress;
/**
*
* @param string $host - хост сервера memcached
* @param int $port - порт сервера memcached
* @param int $compress - [0,1], сжимать или нет данные перед
* помещением в память
*/
public function __construct($host, $port = 11211, $compress = 0)
{
$this->memcache = memcache_connect($host, $port);
$this->compress = ($compress) ? MEMCACHE_COMPRESSED : 0;
}
public function load($valueID, $timeLife)
{
$this->timeLife = $timeLife;
return memcache_get($this->memcache, $valueID);
}
public function save($value, $valueID)
{
return memcache_set($this->memcache, $valueID, $value, $this->compress, $this->timeLife);
}
public function delete($valueID)
{
memcache_delete($this->memcache, $valueID);
}
public function __destruct()
{
memcache_close($this->memcache);
}
}
?>
Как видите, код намного проще чем код кэширования в файлы.
Теперь у нас есть возможность кэшировать при помощи файлов и при помощи memcache. Но что же делать если мы вначале будем использовать файловое кэширование, а потом захотим быстро перейти на memcache? Не перелапачивать же все файлы в которых используется кэширование?!
Выход есть! Нам помогут 2 паттерна проектирования: Strategy и Registry.
Паттерны спешат на помощь!
Создадим класс CACHE_Manager он поможет нам управлять всем многообразием (мы же не остановимся на 2-х классах) классов для кэширования.
<?php
class CACHE_Manager
{
private $_cache;
public function __construct(CACHE_ICache $cache)
{
$this->_cache = $cache;
}
public function load($valueID, $timeLife)
{
return $this->_cache->load($valueID, $timeLife);
}
public function save($value, $valueID)
{
$this->_cache->save($value, $valueID);
}
public function delete($valueID)
{
$this->_cache->delete($valueID);
}
}
?>
Код очень прост и я думаю не требует объяснений. Если же объяснения все же нужны, вам поможет Google, по запросу: паттерн Strategy. (Я бы с радостью пояснил, но боюсь, что статья получится очень большой и ее просто никто не станет читать :) )
Пример использования данного класса:
<?php
//Кэширование на файлах
$cache = new CACHE_Manager(new CACHE_File('cache'));
if (!$var = $cache->load('key', 60)) { //60 секунд
$var = mega_function();
$cache->save($var, 'key');
}
//Кеширование memcache
$cache = new CACHE_Manager(new CACHE_MemCache('127.0.0.1', 11211));
if (!$var = $cache->load('key', 60)) { //60 секунд
$var = mega_function();
$cache->save($var, 'key');
}
?>
Как видно из примера, что бы сменить тип кэширования нам достаточно сменить аргумент в вызове конструктора класса CACHE_Manager.
Теперь сделаем, чтобы вызов класса кэширования выполнялся централизованно, в одном месте. Таким образом, для перевода всего проекта на новый тип кэширования, нам понадобится заменить всего одну строку. В этом нам поможет паттерн Registry.
<?php
class BASE_Registry
{
private static $_vars = array();
public function __construct() {}
public static function set($key, $var)
{
if (isset(self::$_vars[$key]) == true) {
throw new Exception('Данная переменная [' . $key . '] уже существует!');
}
self::$_vars[$key] = $var;
return true;
}
public static function get($key)
{
if (isset(self::$_vars[$key]) == false) { return null; }
return self::$_vars[$key];
}
public static function remove($var)
{
unset(self::$_vars[$key]);
}
}
?>
А теперь пример использования всего этого добра :).
<?php
/* ... */
//Добавляем в реестр экземпляр кэширования в файлы
BASE_Registry::set('cache', new CACHE_Manager(new CACHE_File('cache')));
//Получаем экземпляр класса кеширования из реестра
$cache = BASE_Registry::get('cache');
//И работаем с ним как и раньше
if (!$var = $cache->load('key', 60)) { //60 секунд
$var = mega_function();
$cache->save($var, 'key');
}
/* ... */
?>
Теперь для того чтоб сменить тип кэширования нам нужно будет заменить строку:
BASE_Registry::set(‘cache’, new CACHE_Manager(new CACHE_File(‘cache’)));
на
BASE_Registry::set(‘cache’, new CACHE_Manager(new CACHE_MemCache(’127.0.0.1′, 11211)));
И все. Конечно же это все будет верно, при условии, что ваша система построена по MVC принципу и все вызовы идут через один файл, например, index.php.
Странные имена
В конце, я расскажу почему использовал такие странные имена классов. Опять же, все просто. Имя класса состоит из названия модуля и названия класса, разделенных символом нижнего подчеркивания. Это позволяет структурировать классы, что облегчает их повторное использование. А так же позволяет создать простой autoload:
<?php
define ('DIRSEP', DIRECTORY_SEPARATOR);
define ('site_path', dirname(dirname(__FILE__)) . DIRSEP);
function __autoload($class_name)
{
$fname = str_replace('_', DIRSEP, $class_name) . '.php';
if(!file_exists(site_path . 'include' . DIRSEP . $fname)) {
throw new Exception('Файла [' . site_path . 'include' . DIRSEP . $fname . '] не существует!');
}
require_once(site_path . 'include' . DIRSEP . $fname);
}
?>
Спасибо за внимание!
Надеюсь, что мои статьи помогут вам лучше понять кэширование и некоторые принципы ООП.