Асинхронность с помощью fastcgi_finish_request()

При оптимизации приложения важно не забывать о том, что оптимизируем мы прежде всего для клиента. Сайт, который работает медленно, это всегда неудобно и плохо.
Главный критерий оптимизации для клиента - это скорость ответа (т.е. время, за которое Web сервер отвечает на запрос). Если не брать во внимание клиентскую оптимизацию, есть ряд практик, позволяющих быстрее генерировать ответ клиенту (по сути без оптимизации внутренностей).
Одна из распространенных практик - это переводить необязательную для ответа часть логики в асинхронную обработку. Для этой задачи применяются очереди сообщений.
Если Вы счастливый обладатель связки PHP + PHP-FPM, то существует более простой (хотя и несколько ограниченный) подход для реализации асинхронности. После сборки патча php-fpm, Вам станет доступна функция:
fastcgi_finish_request();
После ее вызова из скрипта, php-fpm получает сигнал о завершении запроса (т.е. отправляет ответ Web серверу), но сам скрипт не завершается. Т.е. вся логика, которая находится после вызова этой функции будет выполнена на фоне.
Пример
Для примера рассмотрим задачу отправки письма (зачастую, весьма медленный процесс):
$to = $_POST['to'];
$body = $_POST['body'];
$subject = $_POST['subject'];
if ( $to && $body && $subject )
{
echo 'Ваше письмо успешно отправлено';
# После этой инструкции посетителю уйдет ответ
fastcgi_finish_request();
# Сама отправка будет выполнена на фоне
mail($to, $subject, $body);
}
else
{
echo 'Вы не ввели все необходимые данные';
}
Это самый простой пример, но, как видно, затраты на внедрение этого ощутимого улучшения минимальны. Другие примеры: загрузка фото с последующим ресайзингом, загрузка видео с последующей конвертацией, отправка сообщения всем друзьям (либо просто нескольким людям) и т.п.
Вам приходилось пользоваться fastcgi_finish_request(), и если да, то в каких случаях?


echo ‘before’;
fastcgi_finish_request();
echo ‘after’;
попадет ли after в ответ? Если нет, то логику приложения придется изрядно менять, чтобы весь layout ушел пользователю, а только потом выполнились долгие вызовы. Если так, то очередь сообщений более универсальный способ.
@<fb:name linked=”false” useyou=”false” uid=”501712046″>Stanislav V. Starcha</fb:name>
Стас,
1. ‘after’ не попадет в ответ, только ‘before’
2. А вот в случае с шаблонами - да придется немного переменить логику:
$controller->do_some_requred_action();
$controller->render();
fastcgi_finish_request();
$controller->do_some_deferred_action();
Нужно учесть, что подобная оптимизация понадобится в двух случаях:
1. Обработка POST запросов, которая обычно оканчивается редиректом - внедрение в любой код будет простым.
2. Обработка AJAX действий (обычно оканчивается выдачей JSON) - чуть сложнее, но переписывать шаблонную систему врядли понядобится.
насколько я понимаю PHP-FPM используется только для nginx?
если я правильно помню, то использовать mod_rewrite апача не получится.
поправьте, кто знает точно.
кстати, неплохо было бы написать статью о deamonPHP и работу с ним. как работает форк процессов, как запустить у себя для отдачи как-то страницы в плоть до уровня веб-сервера
Использовал на podivis.net при загрузке файлов.
Еще там был хак, связанный с наличием отсутствия функции fastcgi_finish_request(). В таком случае нужно было определить пользовательскую ф-ю с идентичным именем, а в ней использовать ob_*() функции php. Фишка в том, что, чтобы заставить сервер отправить контент заранее, нужно чтобы размер контента достигнул определенного значения. Т.к. иногда странички были совсем маленькие, Юра предложил конкатенировать к ним кучу пробелов. И это работало =)
@tarasov
php-fpm не зависит от Web сервера - это FastCGI контейнер и его можно использовать с любым сервером, поддерживающим FastCGI.
Поправляю