Friday, April 17, 2009

Пример архитектуры кэширования(Caching Architecture)

Общую структуру кэширования и получения контента можно представить в таком виде:

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

Контент любого сервиса можно разделить на 3 составляющие: статический контент, медиа контент и динамический контент.

Статический контент


Статический контент - изменяется редко, и создается разработчиками ресурса и изменяется только при смене версии ресурса. И как следствие его желательно отдавать легковесным сервером с максимальной производительностью и поддержкой системных вызовов(writev, sendfile). Для запросов на эти файлы вебсервер должен выдавать последнюю дату изменения, при последующих запросах сервер выдает Not Changed.

Медиафайлы


Медиафайлы - политика кэширования подобна, с некоторыми уточнениями: файл может изменяться только по запросу пользователя, на данный момент используется только процедуры добавления и удаления файлов. И политика заключается в том чтобы снизить количество запросов единичного пользователя для получения файлов.

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

Методы кэширования: при приходе запроса на получение файла без заголовков(HTTP_IF_MODIFIED_SINCE) - выдать файл с указанием времени создания(Last-Modified) для локального файла или указания даты формирования ответа для файла полученного с другого зеркала, и указания для дополнительных заголовков (Cache-Control: max-age=28800). При приходе заголовка (HTTP_IF_MODIFIED_SINCE), если файл с того времени не обновился выдается что контент не изменился(HTTP/1.x 304 Not Modified). Это позволяет дать возможность закешировать файл на месяц и более - причем первый месяц браузер не будет делать никаких запросов к серверу. Желательно иметь одно запасное синхронизируемое зеркало, чтобы при проблемах с основным на него можно было перевести запросы.

Динамические страницы


Запрос поступает на frontend сервер, для всех запросов которые могут попасть под кэшируемость, который запрашивает этот файл с Memcached. Если файл там присутствует, то он возвращается с указанием, что его можно в течении 15 секунд не запрашивать - это блокирует повторных запрос от браузера на это контент на 15 секунд (Cache-Control: max-age=15). Если контент в кэше отсутствует, то выполняется запрос к backend, который повторно запрашивает контент от кеша, так как исходный url может не совпадать с результирующим url идущим на обработку. При отсутствии данных в кеше или не кэшируемости запроса запрос перенаправляется в SlaveDb. Возвращенный от туда запрос при возможности кэширования сохраняется в кэше с указанием времени кэширования определяемого для этого url, для примера: 30 секунд главная страница, 90 - страницы второго уровня. В ответе для кэшируемых станиц указывается в значении max-age тоже время, что указывалось для времени хранения. Как следствие мы получаем, что все станицы кэшироватся в браузере пользователя первым подавшим запрос на полное время, все последующие до окончания времени кэширования на 15 секунд.

Вторая ветвь(зеленая) нужна для снижения нагрузки на slave, так как на всех slave должен быть одинаковый контент, то при разнесении запросов на несколько сервером мы должны получить ~2 увеличение скорости обработки. Memcached - один, так как при использовании 2 экземпляров для каждого slave приведет к дублированию контента на каждом.

Пример кода


function exitIfNotModifiedSince($last_modified) {
if(@array_key_exists("HTTP_IF_MODIFIED_SINCE",$_SERVER)) {
$if_modified_since=@strtotime(@preg_replace('/;.*$/','',$_SERVER["HTTP_IF_MODIFIED_SINCE"]));
if($if_modified_since >= $last_modified) {
header("HTTP/1.x 304 Not Modified");
header("Last-Modified: ".date("D, j M Y G:i:s T", $last_modified));
exit();
}
}
}


function dump_foto_content($photo) {
exitIfNotModifiedSince(strtotime("now")- 100000);
// send the right headers
header("Content-Type: ".$photo["mime"]);
header("Content-Length: ".strlen($photo["photo_data"]));
header("Last-Modified: ".date("D, j M Y G:i:s T"));
header("Cache-Control: max-age=28800");
echo $photo["photo_data"];
exit;
}


P.S.: Для разрешения кэширования на промежуточных серверах нужно добавить параметр:
header("Cache-control: public");

No comments: