Friday, May 15, 2009

Я тебя породил, я тебя и убью!

Я тебя породил, я тебя и убью!
Н. Гоголь "Тарас Бульба".
Старые ошибки или почему иногда, большое удобство плохо.

Если человеку дают очень удобное и красивое средство он теряет нюх и начинает ошибаться, конечно, потом он находит свои ошибки, и не считает, что все что блестит золото , но все таки. Как пример можно привести мелкие ошибки в решении которых я когдато участвовал (примеры относяться к .Net).

Как известно эта платформа обладает очень хорошим свойством как сборщик мусора, вот понимание его работы и вызвало ошибку: когда теряется ссылка на объект через некоторое время этот объект будет удален, но бывают некоторые особенности. Иногда остаются скрытые ссылки на этот объект, и как следствие он остается на вечно и скрытно жрет ресурсы: например окошко с созданным в нем таймером по которому в этот окошке , что то изменяется, вызывает такую особенность: система обрабатывает это окошко по особенному. В системе есть команда: 'выполнять по таймеру событие' и вся эта система, когда вы думаете уже мертва, начинает жить по собственным правилам, и не давать сборщику мусора удалить себя, так как есть круговая зависимость окошко->таймер->система и через некоторое время все ресурсы съедены и система уже не может создавать новые компоненты. Но и умереть она не может, так как не понятно, что происходит и нужно сообщить разработчикам или умереть, одно выполнить не можем, а другое не красиво, а решение простое: когда вам что то уже не нужно разрывайте и останавливаете связи сами, а не ждите, пока это сделает система, так хоть немного дольше проживете(в смысле программы).

Я тебя породил я тебя и убью... всегда сохраняйте взаимосвязь внутри приложения родитель -> потомок. Так как поток созданный в программе обладающей выше указанным багом некогда не умрет при исключении в основном потоке и программа постепенно 'захватит мир или моск'. В данном случае поток выполняющийся в программе был создан, как отдельный процесс не связанный с основным, в деструкторе основного потока он не уничтожался и потихоньку держал ресурсы всей программы. Решение было простое создавать не новый поток, а вызывать асинхронно делегат, который заменил этот поток.

И будьте внимательны к мелочам, лучше самому все уничтожить в деструкторе и не делать асинхронно, то что можно сделать проще и синхронно, чем надеяться на ум среды разработки - она только помощь, а не решение.

Wednesday, May 6, 2009

Маленькие выводы по кэшированию

Или временное завершение этой темы...
  • Проверить качество кэширования можно используя эту ссылку, на этом сайте также даются рекомендации по сжатию контента и приводятся данные относительно времени загрузки через различные каналы;
  • статья относительно указания параметров кэширования на примере использования Apache, даются достаточно универсальные методы указания параметров кэширования;
  • эталонное описание как должно работать кэширование rfc2616;
  • идеи относительно времени кэширования, построения гибкой версии указания этого времени и перенаправления во время обработки для увеличения степени валидности кэша;
  • и как заключение пример архитектуры.

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 атаку он не был рассчитан, он предназначен только для выравнивания и снижения единовремменой пиковой нагрузки.

Friday, March 27, 2009

Nothing is always absolutely so

Сменил заголовок своего блога на утверждение высказанное писателем-фантастом Theodore_Sturgeon, думаю оно максимально выразит идею, что все записаное в блоге является только моим субъективным мнением и на истину в полной мере не претендует.