Продолжить обработку после закрытия соединения
Есть ли в PHP способ закрыть соединение (по сути, сообщите браузеру, что больше нет данных), но продолжайте обработку. Конкретное обстоятельство, о котором я думаю, это то, что я бы хотел обслуживать кэшированные данные, а затем, если бы срок действия кэша истек, я бы по-прежнему обслуживал кэшированные данные для быстрого ответа, закрывал соединение, но продолжал обработку для регенерации и кэширования новых данные. По сути, единственная цель состоит в том, чтобы сделать сайт более отзывчивым, поскольку не было бы случайной задержки, пока пользователь ожидает регенерации контента.
ОБНОВИТЬ:
У PLuS есть самый близкий ответ на то, что я искал. Чтобы уточнить для пары людей, я ищу что-то, что позволяет следующие шаги:
- Страница пользовательских запросов
- Соединение открывается с сервером
- PHP проверяет, не истек ли срок хранения кэша, если он еще свеж, обслуживает кэш и закрывает соединение (КОНЕЦ ЗДЕСЬ) Если срок действия истек, перейдите к 4.
- Срок действия кэша истек
- Закройте соединение, чтобы браузер знал, что он не ждет больше данных.
- PHP регенерирует свежие данные и кэширует их.
- PHP выключается.
ОБНОВИТЬ:
Это важно, это должно быть чисто PHP-решение. Установка другого программного обеспечения не вариант.
8 ответов
Я наконец-то нашел решение (благодаря Google мне просто пришлось продолжать пробовать разные комбинации поисковых терминов). Благодаря комментарию от arr1 на этой странице (это примерно две трети пути вниз по странице).
<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort(true);
ob_start();
echo 'Text the user will see';
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush(); // All output buffers must be flushed here
flush(); // Force output to client
// Do processing here
sleep(30);
echo('Text user will never see');
Я еще не проверял это на самом деле, но, короче говоря, вы отправляете два заголовка: один сообщает браузеру, сколько именно данных ожидать, а другой сообщает браузеру закрыть соединение (что он будет делать только после получения ожидаемого количества содержание). Я еще не проверял это.
Если вы работаете под fastcgi, вы можете использовать очень изящные:
fastcgi_finish_request ();
http://php.net/manual/en/function.fastcgi-finish-request.php
Более подробная информация доступна в двойном ответе.
Вы можете сделать это, установив неограниченное время и игнорируя соединение
<?php
ignore_user_abort(true);
set_time_limit(0);
см. также: http://www.php.net/manual/en/features.connection-handling.php
Насколько я знаю, если вы не используете FastCGI, вы не можете разорвать соединение и продолжить выполнение (если вы не получили ответ Endophage на работу, что мне не удалось). Так что вы можете:
- Используйте cron или что-то в этом роде, чтобы планировать такие задачи
- Используйте дочерний процесс, чтобы закончить работу
Но это становится хуже. Даже если вы породите дочерний процесс с proc_open()
, PHP будет ждать его завершения до закрытия соединения, даже после вызова exit()
, die()
, some_undefined_function_causing_fatal_error()
, Единственный обходной путь, который я нашел, - это порождение дочернего процесса, который сам порождает дочерний процесс, например так:
function doInBackground ($_variables, $_code)
{
proc_open (
'php -r ' .
escapeshellarg ("if (pcntl_fork() === 0) { extract (unserialize (\$argv [1])); $_code }") .
' ' . escapeshellarg (serialize ($_variables)),
array(), $pipes
);
}
$message = 'Hello world!';
$filename = tempnam (sys_get_temp_dir(), 'php_test_workaround');
$delay = 10;
doInBackground (compact ('message', 'filename', 'delay'), <<< 'THE_NOWDOC_STRING'
// Your actual code goes here:
sleep ($delay);
file_put_contents ($filename, $message);
THE_NOWDOC_STRING
);
У PHP нет такой настойчивости (по умолчанию). Единственный способ, которым я могу придумать, - запустить задания cron для предварительного заполнения кэша.
Может компилировать и запускать программы из PHP-CLI(не на виртуальном хостинге> VPS)
Кэширование
Для кеширования я бы так не поступил. Я бы использовал redis как мой кеш LRU. Это будет очень быстро (тесты), особенно когда вы компилируете его с клиентской библиотекой, написанной на C.
Автономная обработка
Когда вы устанавливаете очередь сообщений beanstalkd, вы также можете делать отложенные посылки. Но я бы использовал redis brpop / rpush для выполнения другой части очереди сообщений, потому что redis будет быстрее, особенно если вы используете клиентскую библиотеку PHP (в пространстве пользователя C).
НЕ умеет компилировать или запускать программы из PHP-CLI(на виртуальном хостинге)
set_time_limit
в большинстве случаев этот set_time_limit недоступен (из-за безопасного режима или max_execution_time
директива), чтобы установить 0, по крайней мере, на виртуальном хостинге. Также хостинг на самом деле не хочет, чтобы пользователи долго удерживали процессы PHP. В большинстве случаев ограничение по умолчанию установлено на 30.
Cron
Используйте cron для записи данных на диск, используя Cache_lite. Некоторая тема stackru уже объясняет это:
- crontab с wget - почему он запускается дважды?
- Команды Bash не выполняются при выполнении задания cron - PHP
- Как я могу отладить скрипт PHP CRON, который, кажется, не работает?
Также довольно легко, но все же хакерский. Я думаю, вы должны обновить (>VPS), когда вы должны сделать такой взлом.
Асинхронный запрос
В крайнем случае вы можете выполнить асинхронное кэширование данных, например, используя Cache_lite. Имейте в виду, что виртуальный хостинг не любит, когда вы задерживаете много длительных процессов PHP. Я бы использовал только один фоновый процесс, который вызывает другой, когда он достигает max-execution-time
директивы. Я бы отметил время, когда запускается скрипт, и между парой вызовов кеша я бы измерял затраченное время, а когда оно приближалось ко времени, я выполнял другой асинхронный запрос. Я бы использовал блокировку, чтобы убедиться, что запущен только 1 процесс. Таким образом, я не буду мочиться от поставщика, и это может быть сделано. С другой стороны, я не думаю, что написал бы что-нибудь из этого, потому что это было бы глупо, если вы спросите меня. Когда я доберусь до этого масштаба, я перейду на VPS.
Если вы делаете это для кэширования контента, вы можете вместо этого рассмотреть возможность использования существующего решения для кэширования, такого как memcached.
Нет. Что касается веб-сервера, то запрос от браузера обрабатывается механизмом PHP, и это все. Запрос длится столько же, сколько и PHP.
Вы могли бы быть в состоянии fork()
хоть.