Опубликован: 16.06.2010 | Уровень: специалист | Доступ: платный
Лекция 5:

Уменьшение количества запросов

< Лекция 4 || Лекция 5: 12345 || Лекция 6 >

4.5.4. Кэширование на серверном уровне

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


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

На виртуальных хостингах (когда ресурсы одного сервера могут делить десятки или сотни различных сайтов) для относительно простого сайта (который не предполагает значительного взаимодействия с пользователем) стоит рассмотреть возможность создания кэша готовых HTML-страниц. Что это такое?

Отдаем закэшированный документ

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

Для более детального примера давайте рассмотрим следующий код, отвечающий за простейшее серверное кэширование, из Web Optimizer:

/* проверяем, можем ли отдать закэшированный документ */
if (!empty($this->cache_me)) {
/* переводим адрес документа в реальное имя файла */
$this->uri = $this->convert_request_uri();
$file = $this->options['page']['cachedir'] . '/' . $this->uri;
/* проверяем, существует ли файл и достаточно ли он актуальный */
if (is_file($file) &&
$_SERVER['REQUEST_TIME'] - filemtime($file) <
$this->options['page']['cache_timeout']) {
$content = @file_get_contents($file);
/* проверяем, сжат ли файл */
if (!empty($this->options['page']['gzip']) &&
substr($content, 0, 8) ==
"\x1f\x8b\x08\x00\x00\x00\x00\x00") {
/* если сжат, то выставляем соответствующие заголовки */
$this->set_gzip_header();
}
/* отдает закэшированное содержимое */
echo $content;
/* и закрываем PHP-процес */
die();
}
}
Листинг 4.22.

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

Создаем закэшированный документ

Но мы рассмотрели процесс выдачи закэшированного документа, а каким образом он появляется на жестком диске? Процедура сохранения файла немного проще и может быть записана следующим образом (код из Web Optimizer):

/* определяем, нужно ли нам сохранять закэшированную версию
документа */
if (!empty($this->cache_me)) {
/* формируем имя файла */
$file = $options['cachedir'] . '/' . $this->uri;
/* проверяем, есть ли такой файл и не устарел ли он */
if (!is_file($file) ||
$_SERVER['REQUEST_TIME'] - filemtime($file) >
$options['cache_timeout']) {
/* записываем новое содержимое в файл */
$fp = @fopen($file, "a");
if ($fp) {
/* блокируем файл от конкурентных попыток записи */
@flock($fp, LOCK_EX);
/* удаляем содержимое и перемещаемся на начало файла */
@ftruncate($fp, 0);
@fseek($fp, 0);
@fwrite($fp, $this->content);
@fclose($fp);
}
}
}
Листинг 4.23.

Правильно настроенное кэширование на серверном уровне способно сэкономить время ваших посетителей (и тем самым поднять конверсию сайта) и сэкономить серверные ресурсы (при использовании каких-либо распределенных мощностей).

4.5.5. Кэширование XHR-запросов

Данный раздел написан после прочтения заметки Steve Souders "F5 and XHR deep dive", посвященной вопросам кэширования XHR-ресурсов.

Оказывается, что любые данные, полученные при помощи AJAX, никогда не будут обновлены в IE прежде истечения срока действия кэша, даже если вы форсируете обновление ( Ctrl+F5 ). Единственный путь обновить эти данные — это вручную удалить их из кэша.

Если вы нажимаете Перезагрузку (F5), IE перезапросит все ресурсы (даже с неистекшим сроком действия кэша), за исключением XHR. Это может вызвать большое недоумение среди разработчиков при тестировании, но меня заинтересовало, какие еще проблемы существуют в этом направлении. Будет ли поведение аналогичным во всех остальных основных браузерах? Что произойдет, если срок давности кэша будет в прошлом или заголовок Expires вообще не будет выставлен? Будет ли какой-либо эффект от добавления Cache-Control max-age (который переписывает заголовок Expires)?

Проводим тестирование

Для ответа на все заявленные вопросы была создана тестовая страница. На ней располагалась картинка, внешний скрипт и XMLHttpRequest. Ниже приведены протестированные модификации этой страницы.

  • Вариант с Expires в прошлом добавляет в заголовки ответа Expires, который содержит дату на 30 дней ранее текущей, и Cache-Control с max-age=0.
  • Вариант без Expires вообще не выставляет никаких заголовков Expires или Cache-Control.
  • Вариант с Expires в будущем добавляет в заголовки ответа Expires, который содержит дату на 30 позже текущей, и Cache-Control с max-age=2592000.

Ниже в таблице приведены результаты тестирования этой страницы в основных браузерах. Также там записано, перезапрашивался ли XHR-ре-сурс или был прочитан из кэша, а если перезапрашивался, то с каким кодом HTTP-статуса.

Ниже приведены соображения на тему того, что происходит при нажатии F5.

  • Все браузеры перезапрашивают и картинку, и внешний скрипт (это имеет смысл).
  • Все браузеры перезапрашивают XHR-ресурс, если срок действия кэша находится в прошлом (это тоже имеет смысл: браузер знает, что закэшированный XHR-ресурс устарел).
Таблица 4.1. Если кэшируется XHR, что происходит при нажатии F5?
Expires в прошлом Без Expires Expires в будущем
Chrome 2 304 304 304
Firefox 3.5 304 304 304
IE 7 304 кэш кэш
IE 8 304 кэш кэш
Opera 10 304 кэш 304
Safari 4 200 200 200
  • Единственное различие в поведении замечено в тот момент, когда для XHR-ресурса нет заголовка Expires, или же Expires выставлен в будущее. IE 7&8 не перезапрашивают XHR-ресурс, если нет Expires или Expires выставлен в будущее, даже при нажатии Ctrl+F5. Opera 10 не перезапрашивает XHR-ресурс, если нет Expires (эквивалента для Ctrl+F5 в Opera найти не удалось).
  • И Opera 10, и Safari 4 перезапрашивают favicon.ico во всех случаях (это весьма расточительно).
  • Safari 4 не посылает заголовок If-Modified-Since во всех случаях. В результате ответ всегда приходит со статус-кодом 200 и включает запрашиваемый ресурс полностью. Это верно как для XHR-ре-сурса, так и для картинок и внешних скриптов (это выглядит неоптимально и отличается от поведения других браузеров).
Выводы

Ниже резюмированы рекомендации для веб-разработчиков при работе с XHR-ресурсами.

  1. Разработчики должны выставлять срок действия кэша для XHR-ресурсов или в прошлом, или в будущем, чтобы предотвратить расхождения в поведении браузеров, когда Expires вообще не выставлен.
  2. Если XHR-ресурсы вообще не должны быть закэшированы, разработчикам стоит выставлять дату изменения ресурса в прошлое. Это давняя проблема с различным поведением браузеров при наличии закэшированных копий определенных ресурсов, и касается она не только XHR-запросов. Например, не всегда пользователи будут перезагружать страницу — они могут на нее попасть, просто переходя по ссылкам. В этом случае браузер выдаст им закэ-шированные версии XHR. Для форсирования сброса кэша мы можем выставлять, например, дополнительный GET-параметр, и это будет работать для всех браузеров и всех прокси-серверов. Более подробно вопросы сброса кэша описаны ранее в этом разделе.
  3. Если XHR-запросы желательно кэшировать, то разработчики должны назначить срок истечения кэша в будущем. При тестировании в IE 7&8 разработчикам придется не забывать очищать кэш, чтобы проверить действие Перезагрузки (F5).
< Лекция 4 || Лекция 5: 12345 || Лекция 6 >
Ольга Артёмова
Ольга Артёмова

Доброго времени суток!

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

Сертификация: оптимизация и продвижение web-сайтов.

Ярославй Грива
Ярославй Грива
Россия, г. Санкт-Петербург
Ёдгор Латипов
Ёдгор Латипов
Таджикистан, Кургантепа