Как закрыть соединение и продолжить выполнение скрипта
Порой перед разработчиком встает задача проделать действительно долгую операцию после отображения страницы, как например рассылка почтовых сообщений или обмен данными с удаленным сервером. Можно положить эту задачу на плечи крона, но если это не удобно/медленно/кошерно, приходится выкручиваться и изобретать странные решения. Одним из таких решений является выполнение долгой операции непосредственно после рендеринга страницы, что может подвесить соединение на пару (десятков, сотен) секунд, или, если поступить добросовестно и оборвать соединение, пройдет незаметно для пользователя. Об этом методе сегодня и поговорим.
Если обратится к спецификации 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("[email protected]", "spam", $i);
}
Не забывайте про параметр max_execution_time
в php.ini
, которым ограничено время выполнения вашего скрипта. Content-Encoding
установлен в none
для того, что бы исключить изменения размера gzip модулями сервера, что приведет к неверной работе. Впрочем это не гарантирует 100% решение проблемы, так что используйте этот трюк с умом.