08.04 2011

Порой перед разработчиком встает задача проделать действительно долгую операцию после отображения страницы, как например рассылка почтовых сообщений или обмен данными с удаленным сервером. Можно положить эту задачу на плечи крона, но если это не удобно/медленно/кошерно, приходится выкручиваться и изобретать странные решения. Одним из таких решений является выполнение долгой операции непосредственно после рендеринга страницы, что может подвесить соединение на пару (десятков, сотен) секунд, или, если поступить добросовестно и оборвать соединение, пройдет незаметно для пользователя. Об этом методе сегодня и поговорим.

Если обратится к спецификации HTTP, можно найти пару замечательных заголовков:

Connection: close - говорит браузеру оборвать соединение при достижении конца файла
Content-Length: n - устанавливает длину документа

Посмотрим реализацию на PHP

set_time_limit(0);
ignore_user_abort(true);

ob_start(); // start buffer

// out page content
echo "This make take some time, please wait";

$length = ob_get_length();

// magic
header('Connection: close');
header("Content-Length: " . $length);
header("Content-Encoding: none");
header("Accept-Ranges: bytes");

ob_end_flush();
ob_flush();
flush();

// background

echo "if you see this, i am not working =(";

//long, long operation

for ($i = 0; $i <= 100000; $i++) {
    mail("me@localhost", "spam", $i);
}

Не забывайте про параметр max_execution_time в php.ini, которым ограничено время выполнения вашего скрипта. Content-Encoding установлен в none для того, что бы исключить изменения размера gzip модулями сервера, что приведет к неверной работе. Впрочем это не гарантирует 100% решение проблемы, так что используйте этот трюк с умом.

comments powered by Disqus