Отправляет email-рассылки с помощью сервиса Sendsay

Построй свой сайт на PHP!

  Все выпуски  

Построй свой сайт на PHP!


Информационный Канал Subscribe.Ru

Построй свой сайт на PHP!

искать в
Здравствуйте, уважаемые читатели.

Как некоторые из вас, возможно, уже заметили, рассылка не выходила достаточно продолжительный период времени. Стоит ли винить ее автора, то есть меня, за, признаюсь, вынужденное молчание? Несомненно, стоит. Не в полную силу, конечно, но все-таки стоит. Упущение это нужно как-то исправлять. А так как дорогу осилит только лишь идущий или, что гораздо эффективнее, едущий по ней, то сегодня я решил сделать маленький шажок в сторону возобновления выхода рассылки. Так что сегодня речь у нас пойдет о кэшировании в PHP. Эта тема довольно объемна, поэтому в одном выпуске ее не разместишь, так что в ближайшее время (надеюсь, в этом году ;-)) я надеюсь осчастливить вас продолжением.

Данная статья также доступна на моем блоге: http://blog.never-invited.com/

С уважением, Олег Шимчик aka The Wanderer. wanderer@php.net

Кэширование в PHP - Часть 1

Где те далекие времена, когда сайты состояли из десятка статических HTML-страниц и нескольких рисунков? Когда веб-браузеры сами беспокоились о кэшировании, а работа сервера была простой: знай себе отдавай нужные документы или сообщай, что, дескать, ничего не изменилось и нечего было вообще беспокоить по пустякам и спокойно отдыхай дальше. Страницы тогда загружались практически мгновенно (если вы не были счастливым обладателем самого медленного модема), а мощности тогдашних серверов нельзя поставить и в сравнение с сегодняшними. К сожалению, или, возможно, к счастью, те времена безвозвратно ушли.

Кэширование сегодня - важная часть любого достаточно крупного веб-сайта. Почему? Во-первых, из-за очень большого числа запросов на обслуживание каждого тратится все больше и больше процессорного времени. Большая его часть тратится на повторяющиеся операции, которые достаточно бы было выполнить только один раз. Во-вторых, в связи с тем же большим количеством запросов увеличивается время непосредственно генерации страницы, то есть промежутка между запросом и отправкой сервером страницы. В некоторых случаях этот промежуток может достичь нескольких десятков секунд. В этом случае, скорее всего, многие посетители просто уйдут, не дождавшись результата, и больше не вернутся на ваш сайт.

Вместе с приходом динамических страниц появилась и другая проблема, связанная с кэшированием. Дело в том, что в случае статических страниц, сервер почти всегда мог точно определить дату последнего изменения документа и либо отдать его, либо сообщить веб-браузеру о возможности использовать уже когда-то загруженный вариант страницы, экономя свой и клиентский трафик. Проблема в том, что сам по себе код скрипта меняется достаточно редко, и эти изменения мало связаны с изменениями генерируемых скриптом страниц. По этой причине приходится или принудительно отключать кэширование клиентским браузером или брать эту часть работы на себя. (Впрочем, нужно отметить, что пользователи хоть чуть-чуть современного ПО не так часто встречаются с такого рода проблемами, но учесть некоторые моменты определенно стоит) Позже в этой статье мы поговорим о данной проблеме, а сейчас позвольте мне рассказать о методах решения проблемы кэширования на стороне сервера.

Оптимизация и кэширование скомпилированных PHP-скриптов

Существует несколько решений для оптимизации и кэширования уже скомпилированных PHP-скриптов. Эти программы как правило преобразуют php-код в собственный формат, уже нечитабельный для человека, и включаются в стандартный процесс компиляции, значительно уменьшая время исполнения скрипта. Существует несколько программных продуктов для этих целей. Самым популярным является Zend Accelerator, написанный создателями PHP и распространяющийся на коммерческой основе. Также довольно распространены iconCube PHP Accelerator и Turk MMCache. Последний ныне не развивается.

В этой статье мы не будем касаться работы данных пограммных продуктов, так как это достаточно объемная тема, касающаяся, к тому же, в большей степени администраторов, нежели обычных PHP-программистов.

PEAR Cache_Lite

PEAR Cache_Lite (лицензия - LGPL) является ответвлением PEAR-пакета Cache. Cache_Lite изначально ориентирован на скорость и простоту. Автором были протестированы все типы контейнеров PEAR Cache, и самым быстрым оказался метод хранения кэша в файлах. Именно этот метод хранения является основополагающим и единственным в Cache_Lite. Кроме того, пакет практически не зависит от PEAR: pear.php используется только для сообщения об ошибках и подключается, соответсвенно, только в случае возникновения оных. Поэтому, если вы по каким-то причинам не хотите использовать PEAR вовсе, проблем с переписыванием Cache_Lite под свою систему возникнуть не должно.

Так как Cache_Lite работает с файловой системой, перед пакетом встает такая проблема, как возможное повреждение кэша (в том числе, в случае совместного доступа). Естественно, будет неприятно, если пользователю будет возвращен поврежденный кэш и, соответственно, библиотека должна беспокоится о том, чтобы не допустить этого. Для решения этой проблемы Cache_Lite предлагает на выбор три механизма:

  • Блокировку файла (file locking) - самый известный и в большинстве случаев самый оптимальный способ. К несчастью, он не работает на распределенных файловых системах (таких, как NFS).
  • Контроль записи - кэш записывается и сразу же прочитывается и сравнивается с оригиналом.
  • Контроль чтения - вместе с кэшем хранится его контрольная сумма (crc32, md5 или просто длина строки, содержащей кэш). Целостность кэша проверяется сразу же после чтения.
Примеры использования

Начнем с самого просто примера:

<?php

// Включаем код Cache_Lite в программу
require_once('Cache_Lite/Lite.php');

// Устанавливаем идентификатор кэша
$id '123';

// Небольшая настройка
$options = array(
    
'cacheDir' => '/tmp/',
    
'lifeTime' => 3600
);

// Инициализируем объект Cache_Lite
$Cache_Lite = new Cache_Lite($options);

// Проверяем, существует ли кэш с таким идентификатором
if ($data $Cache_Lite->get($id)) {

// Выводим данные из кэша
echo $data;

} else { 
// Кэш не найден или поврежден

    //Устанавливаем и сохраняем данные
    
$data 'Эта строка взята из кэша';
    
$Cache_Lite->save($data);

}

?>

При первом запуске данный пример сохраняет кэш в директории /tmp/ (если она, конечно, доступна) и показывает пустую страницу. Зато при повторной загрузке на странице появляется 'Эта строка взята из кэша'. Для того, чтобы убедится, что строка действительно закэширована, вы можете просто заглянуть в папку /tmp/ и убедится, что там находится файл с замысловатым именем 'cache_c21f969b5f03d33d43e04f8f136e7682_202cb962ac59075b964b07152d234b70'. В файле содержится хэш для проверки целостности записанных данных и собственно строка с данными кэша.

Записанный нами кэш будет считаться актуальным в течение одного часа (3600 секунд), если мы принудительно не сбросим его раньше.

В нашем примере мы использовали только две опции настройки: cacheDir и lifeTime. Cache_Lite поддерживает гораздо большее их число. Вот полный список опций настройки:

  • 'cacheDir' - директория, в которой будет хранится кэш;
  • 'caching' - включить/отключить кэширование;
  • 'lifeTime' - время жизни кэша в секундах;
  • 'fileLocking' - включить/отключить блокировку файлов;
  • 'writeControl' - включить/отключить контроль записи;
  • 'readControl' - включить/отключить контроль чтения;
  • 'readControlType' - тип контроля чтения: 'crc32', 'md5', 'strlen';
  • 'pearErrorMode' - режим ошибки pear (при вызове raiseError) (см. документацию PEAR);
  • 'memoryCaching' - включить/отключить кэширование в памяти;
  • 'onlyMemoryCaching' - включить/отключить только кэширование в памяти;
  • 'memoryCachingLimit' - максимальное число записей, хранимых в памяти;
  • 'fileNameProtection' - включить/отключить автоматическую защиту имен файлов;
  • 'automaticSerialization' - включить/отключить автоматическую сериализацию;
  • 'automaticCleaningFactor' - отключить/настроить процесс автоматической чистки;
  • 'hashedDirectoryLevel' - количество дополнительных "уровней" внутри директории с кэшем, автоматически создаваемых системой.

С PEAR Cache_Lite вы можете не только полностью кэшировать страницу, но также кэшировать только отдельные ее блоки. К примеру, вы можете создать кэш для меню навигации, которое, как правило, меняется крайне редко, а, к примеру, пользовательское меню генерировать каждый раз.

<?php

require_once('Cache_Lite/Lite.php');

$options = array(
    
'cacheDir' => '/tmp/',
    
'lifeTime' => 3600
);

// Создаем объект Cache_Lite
$Cache_Lite = new Cache_Lite($options);

if (
$data $Cache_Lite->get('block1')) {
    echo(
$data);
} else { 
    
$data 'Вот тут будут данные блока номер раз';
    
$Cache_Lite->save($data);
}

echo(
'<br><br>А вот эта строка высказалась против кэширования !<br><br>');

if (
$data $Cache_Lite->get('block2')) {
    echo(
$data);
} else { 
    
$data 'А вот тут - блока номер два';
    
$Cache_Lite->save($data);
}
?>

При первой загрузке вам будет показана только незакэшированная строка - два других блока тихо и мирно сохранятся в директории с кэшем. При нажатии на "Обновить" выведутся данные из всех трех блоков. Вы можете попробовать поизменять данные блоков: изменения сразу же будут видны только для некэшированной строки.

Еще одна возможность PEAR Cache_Lite, о которой бы хотелось рассказать, это встроенные функции для перехвата содержимого, поступающего в браузер, и сохранения его в кэше. Это очень удобно на реальных страницах, где такая работа с кэшем, как в предыдущих примерах, слишком усложняет код и попросту неудобна.

Для того, чтобы сохранить содержимое страницы (или отдельного ее блока), перед выводом данных нужно создать объект Cache_Lite_Output, предварительно включив файл 'Cache_Lite/Lite/Output.php' и вызвать метод start('Имя блока'), в конце же блока прость вызовите метод $cache->end() и данные будут сохранены в кэше.

Вот предыдущий пример, переписанный с использованием Cache_Lite_Output:

<?php

require_once('Cache/Lite/Output.php');

$options = array(
    
'cacheDir' => '/tmp/',
    
'lifeTime' => 10
);

$cache = new Cache_Lite_Output($options);

if (!(
$cache->start('block1'))) {
    
// Не закешировано...
    
echo('Вот тут будут данные блока номер раз<br>');
    
$cache->end();
}

echo(
'<br><br>А вот эта строка высказалась против кэширования !<br><br>');

if (!(
$cache->start('block2'))) {
    
// Не закешировано...
    
echo('А вот тут - блока номер два<br>');
    
$cache->end();
}

Как нетрудно заметить, поведение нового примера принципиально отличается от предыдущего тем, что выводит все данные сразу, вне зависимости от того, закешированы они или нет. Настроек, позволяющих изменять такое поведение, у класса нет, что, в принципе, неудивительно: текущее поведение вполне удобно и логично с практической точки зрения. Если же по какой-то причине оно вас не устраивает, то изменить класс для своих нужд или написать свой не составит труда: он состоит всего лишь из двух функций, использованных в предыдущем примере, да конструктора. Сами функции не содержат никакого мудренного кода и представляют собой обычную обертку над стандартными функциями PHP по работе с буфером вывода.

Еще одна интересная функция Cache_Lite - возможность кеширования результата выполнения той или иной функции. В данный момент поддерживается работа с обычными функциями, методами и статическими методами класса. Давайте посмотрим на пример:

<?php

require_once('Cache/Lite/Function.php');

$options = array(
    
'cacheDir' => '/tmp/',
    
'lifeTime' => 10
);

$cache = new Cache_Lite_Function($options);

$cache->call('function_to_bench'1245);

function 
function_to_bench($arg1$arg2
{
    echo 
"Вывод функции function_to_bench($arg1, $arg2)<br>";
    return 
"Результат функции function_to_bench($arg1, $arg2)<br>";
}

?>

Как вы можете заметить, ничего сложного в использовании Cache_Lite_Function нет: достаточно просто вызвать метод call(), которому в качестве первого параметра передать имя функции, результат работы которой нужно закешировать, а все остальные параметры будут использованы как аргументы вызываемой функции. Кроме того, работают конструкции типа 'object->method' и 'class::method()'. Единственный подводный камень здесь, который обязательно нужно учесть, это то, что 'this->method' методу call() передавать нельзя по одной простой причине: так как $this указывает на экземпляр того класса, в контексте которого выполняется код, то Cache_Lite_Funсtion будет пытаться вызвать метод с именем method() у себя самого, что вряд ли даст тот результат, который вы ожидали.

Итог

PEAR Cache_Lite является, пожалуй, идеальным решением для большинства сайтов, на которых планируется внедрить кеширование: его главными ориентирами является скорость и безопасность кеширования. Так что, если вы не планируете хранить кеш в базе данных или каких-то экзотических местах, то это как раз то, что вам нужно. Как уже было сказано выше, Cache_Lite минимально зависит от PEAR, так что, если вдруг на вашем хосте оного не окажется, то можно не мучаться, загружая все необходимые файлы самостоятельно, или ругаться с админам на предмет установки PEAR (нужно заметить, что если его нет в системе, то это случай довольно клинический), а просто полазить немного по коду ручками (желательно, конечно, прямыми) и устранить такого рода зависимость хирургическим путем.

 
Copyright © 2004-2005 Построй свой сайт на PHP!
Перепечатка возможна только с сохранением авторства.
Выпуск #11: 2006-01-10

Subscribe.Ru
Поддержка подписчиков
Другие рассылки этой тематики
Другие рассылки этого автора
Подписан адрес:
Код этой рассылки: comp.soft.prog.phpdev
Архив рассылки
Отписаться Вебом Почтой
Вспомнить пароль

В избранное