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");

Thursday, April 9, 2009

Be Linux

Наиболее понравившийся мне видеоролик к конкурсу We're Linux



И ролик BBC использовавшийся при создании этого ролика:

Sunday, April 5, 2009

Оптимизация времени кэширования

  1. Оптимизация времени кэширования под количество запросов, обычно пользователей загрузивших страницу можно разделить на 2 группы: зашедших на страницу с другой станицы или непосредственно на страницу и другую группу обновляющих станицу в ожидании изменений. Первой группе не столь важно сколько кэшируеться страница им более важна скорость получения страницы и можно использовать максимальное допустимое время кэширования. Для второй же группы нужно кэшировать минимальное время - их можно определить по referer и тому что у них будет стоять заголовок "if not modifined since" - используя эти особенности можно при приходе первого типа запроса использовать удвоенное время кэширования, а для вторых одинарное - в результате вторая группа получает всегда обновленной контент, а первая скорость(и при совпадении запросов обоих групп еще и более быстрое обновлении, но они этого не замечают так видят страницу первый раз).
  2. При появлении запроса с заголовками не изменилось ли что то ("if not modifined since"), если еще существует кэш этого запроса можно сравнивать указанный в запросе хеш с возвращенным после обработки и стараться возвратить не изменилось так как это позволит быстрее освободить ресурсы.
  3. При варианте, когда контент уже существует на диске и его расположение высчитывается скриптом, более разумно использовать не хеш, а дату изменения и стараться, если это возможно не выдавать, а перенаправлять пользователя на это контент, в результате мы получаем минимальное потребление памяти и скорость отработки близкую к отображению статики. Перенаправлять желательно даже, если результат находиться на другом ресурсе, меньше посредников быстрее ресурс освободиться.
  4. Очень желательно кэшировать результаты запросов не на самодельных скриптах кэширования, а использовать более распостраннённые системы кэширования написаные на компилируемых языках - так больше вероятность, что выдача будет максимально быстро отдаваться и не нужно тратить время на создание велосипеда и его тестирование. Как следствие лучше перенаправить на кэш или файловую систему, чем генерировать контент скриптом - так больше вероятность, что будет использована максимально эффективная стратегия отдачи контента.

Friday, April 3, 2009

Недостаток в коде модуля балансировки

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