Статьи

Веб-ошибки для планирования работы: взлом или решение?

Одной из странных реалий PHP является то, что многие проекты строят свой дизайн вокруг самого низкого общего демонинатора — общего веб-хостинга и по понятным причинам. Ограничения, накладываемые веб-хостами на использование их учетных записей, например, отсутствие доступа к оболочке, файловая система только для чтения или ограниченная возможность устанавливать разрешения файловой системы, странные конфигурации PHP и т. Д. Один пример, который приходит на ум; недавно взглянул на Drupal и наткнулся на этот фрагмент при кэшировании ;

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

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

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

Вы «включаете» псевдокрон на свой сайт, ссылаясь на него в теге HTML-изображения. Он отображает прозрачное изображение 1 × 1, затем переходит к выполнению сценариев PHP на основе файла расписания псевдокрон. Преимущество этого подхода заключается в том, что браузер запускает отдельный HTTP-запрос, соответствующий отдельному процессу Apache, поэтому любой код, выполняемый псевдокроном, запускается «вне диапазона» из основного сценария PHP — посетители вашего сайта не подвергаются длительная задержка, если происходит «задание cron». Ну, почти…

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

Однако в первом выпуске, содержащем индексатор, некоторые пользователи сообщали о проблемах, связанных с тем, что «загрузка страницы» стала очень медленной. Оказалось, что при работе с веб-ошибкой изображение 1 × 1 отображалось только после завершения индексации. Это означало, что, хотя браузер получил полный вики-контент, он зависал (с открытым HTTP-соединением) в ожидании прибытия изображения. Это привело к тому, что пульс продолжал пульсировать, создавая впечатление, что страница все еще загружалась. Был удивлен, увидев, что у псевдокрона есть подобные проблемы — хотя изображение отображается немедленно, оно не дает браузеру никаких указаний на то, что изображение завершено, поэтому может оставить соединение открытым.

Чтобы исправить это в Dokuwiki, изображение 1 × 1 было визуализировано немедленно в скрипте индексатора, и браузер отправил заголовок Content Length, который приказал бы ему разорвать соединение, когда у него будет полное изображение. Обычно, когда HTTP-соединение сбрасывается клиентом, соответствующий скрипт PHP будет уничтожен. Но это поведение может быть отменено с помощью ignore_user_abort () , поэтому стал запуск indexer.php;


<?php
/**
 * DokuWiki indexer
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Andreas Gohr
 */
 
/**
 * Just send a 1x1 pixel blank gif to the browser and exit
 */
function sendGIF(){
    $img = base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAIBTAA7');
    header('Content-Type: image/gif');
    header('Content-Length: '.strlen($img));
    header('Connection: Close');
    print $img;
    // Browser should drop connection after this
    // Thinks it's got the whole image
}
 
// Make sure image is sent to the browser immediately
ob_implicit_flush(TRUE);
 
// keep running after browser closes connection
@ignore_user_abort(true);
 
sendGIF();

// Browser is now gone...
 
// Switch off implicit flush again - we don't want to send any more output
ob_implicit_flush(FALSE);
 
// Catch any possible output (e.g. errors)
// - probably not needed but better safe...
ob_start();

// Start the real work now...

Все идет нормально. Но есть еще один негативный эффект от использования веб-ошибки. Для каждого запроса страницы, который делает пользователь, два процесса Apache связаны с использованием PHP-скриптов. Если веб-скрипт ошибок выполняется долго и требует много ресурсов, большое количество посетителей может привести к тому, что большое количество веб-ошибок будет работать параллельно, блокируя дочерние процессы Apache, потребляя ресурсы процессора и памяти влево, вправо и в центре. Как примечание стороны, противопоставьте это с тем, что Джордж советует здесь ;

Разгрузка статического контента. Если средняя страница в вашем веб-приложении содержит девять изображений, то только десять процентов запросов к вашему веб-серверу фактически использовали постоянные соединения, которые они им присвоили. Другими словами, девяносто процентов запросов тратят ценный (и дорогой, с точки зрения масштабируемости) дескриптор соединения Oracle [persistent]. Ваша цель должна заключаться в том, чтобы с вашего динамического веб-сервера обслуживались только те запросы, которые требуют подключения Oracle (или, по крайней мере, требуют динамического контента). Это увеличит объем работы, связанной с Oracle, выполняемой каждым процессом, что, в свою очередь, уменьшает количество дочерних элементов, необходимых для создания динамического контента … Самый простой способ добиться этого — выгрузить все ваши изображения на отдельный веб-сервер (или установить веб-серверов).

На самом деле это не проблема для Докувики, благодаря побочному эффекту осторожного подхода Энди к индексации.

Чтобы избежать состязаний с одновременной обработкой нескольких индексов и попыткой записи в одни и те же файлы, Энди применил простое правило, согласно которому в любой момент времени разрешено запускать только одному индексатору. Чтобы реализовать это, он добавил механизм блокировки, при котором индексатор создает каталог (всегда используя одно и то же имя / путь к каталогу), когда он начинает работать, а затем удаляет его по завершении. Создание каталога должно быть более эффективным (и требует меньше строк кода), чем создание файла, так как это всего лишь вопрос обновления базы данных inode . Если индексатор Dokuwiki не может получить блокировку (каталог блокировки существует), он немедленно завершается, что освобождает дочерний процесс Apache для дальнейшей работы.

В конечном итоге веб-ошибка, используемая Dokuwiki, вероятно, настолько эффективна и надежна, насколько это возможно. Хотя общая концепция может быть полезна для генерации электричества из колеса хомяка, учитывая эффективное решение, возникает вопрос: если у вас есть хост, который позволяет вам использовать cron , у вас все еще будет соблазн жить с веб-ошибкой Dokuwiki? Все еще хитрый взлом или верное решение?