Я пытаюсь выполнить вызов AJAX (через JQuery), который инициирует довольно длительный процесс. Я бы хотел, чтобы сценарий просто отправил ответ, указывающий на то, что процесс запущен, но JQuery не вернет ответ, пока сценарий PHP не будет запущен.
Я пробовал это с заголовком «закрыть» (см. Ниже), а также с буферизацией вывода; ни один из них не работает. Есть догадки? или это то, что мне нужно сделать в JQuery?
<?php
echo( "We'll email you as soon as this is done." );
header( "Connection: Close" );
// do some stuff that will take a while
mail( 'dude@thatplace.com', "okay I'm done", 'Yup, all done.' );
?>
Ответы:
На следующей странице руководства PHP (включая примечания для пользователя) предлагается несколько инструкций о том, как закрыть TCP-соединение с браузером, не завершая PHP-скрипт:
Предположительно, для этого требуется немного больше, чем отправка закрытого заголовка.
Затем OP подтверждает: да, это помогло : указав на заметку пользователя № 71172 (ноябрь 2006 г.), скопированную здесь:
Позже, в июле 2010 года, в соответствующем ответе Arctic Fire связал еще две пользовательские заметки, которые были дополнением к предыдущей:
источник
Connection: close
заголовок может быть перезаписан другим программным обеспечением в стеке, например, обратным прокси в случае CGI (я наблюдал такое поведение с nginx). См. Ответ @hanshenrik по этому поводу. В целомConnection: close
выполняется на стороне клиента и не может рассматриваться как ответ на этот вопрос. Соединение должно быть закрыто со стороны сервера .Необходимо отправить эти 2 заголовка:
Поскольку вам нужно знать размер вашего вывода, вам нужно будет буферизовать его, а затем сбросить его в браузер:
Кроме того, если ваш веб-сервер использует автоматическое сжатие gzip для вывода (например, Apache с mod_deflate), это не сработает, потому что фактический размер вывода изменяется, а Content-Length больше не является точным. Отключите сжатие gzip для конкретного скрипта.
Для получения дополнительной информации посетите http://www.zulius.com/how-to/close-browser-connection-continue-execution.
источник
header("Content-Encoding: none\r\n");
этого параметра, чтобы apache не сжимал его.ob_flush()
не обязательно, и фактически вызывает уведомлениеfailed to flush buffer
. Я вынул его, и это отлично сработало.ob_flush()
линия была необходима.Вы можете использовать Fast-CGI с PHP-FPM для использования этой
fastcgi_end_request()
функции . Таким образом, вы можете продолжить некоторую обработку, пока ответ уже отправлен клиенту.Вы найдете это в руководстве по PHP здесь: FastCGI Process Manager (FPM) ; Но эта функция отдельно не описывается в руководстве. Вот выдержка из PHP-FPM: PHP FastCGI Process Manager Wiki :
fastcgi_finish_request ()
Область: функция php
Категория: Оптимизация
Эта функция позволяет ускорить выполнение некоторых запросов php. Ускорение возможно при наличии в процессе выполнения скрипта действий, не влияющих на реакцию сервера. Например, сохранение сеанса в memcached может произойти после того, как страница была сформирована и передана на веб-сервер.
fastcgi_finish_request()
- это функция PHP, которая останавливает вывод ответа. Веб-сервер немедленно начинает передавать ответ клиенту «медленно и грустно», а php в то же время может делать много полезных вещей в контексте запроса, таких как сохранение сеанса, преобразование загруженного видео, обработка всех видов статистики и др.fastcgi_finish_request()
может вызывать выполнение функции выключения.Примечание:
fastcgi_finish_request()
есть причуда , где вызовыflush
,print
илиecho
будут прекратить сценарий рано.Чтобы избежать этой проблемы, вы можете позвонить
ignore_user_abort(true)
прямо до или послеfastcgi_finish_request
звонка:источник
Полная версия:
источник
Лучшее решение - разветвить фоновый процесс. В unix / linux это довольно просто:
Для получения лучших примеров вам следует взглянуть на этот вопрос:
PHP выполняет фоновый процесс
источник
Предполагая, что у вас есть сервер Linux и root-доступ, попробуйте это. Это самое простое решение, которое я нашел.
Создайте новый каталог для следующих файлов и предоставьте ему полные разрешения. (Позже мы сможем сделать его более безопасным.)
Поместите это в файл с именем
bgping
.Обратите внимание на расширение
&
. Команда ping будет выполняться в фоновом режиме, пока текущий процесс перейдет к команде echo. Он будет пинговать www.google.com 15 раз, что займет около 15 секунд.Сделайте его исполняемым.
Поместите это в файл с именем
bgtest.php
.Когда вы запрашиваете bgtest.php в своем браузере, вы должны быстро получить следующий ответ, не дожидаясь примерно 15 секунд, пока команда ping завершится.
Теперь на сервере должна выполняться команда ping. Вместо команды ping вы можете запустить сценарий PHP:
Надеюсь это поможет!
источник
Вот модификация кода Тимбо, которая работает со сжатием gzip.
источник
Я нахожусь на общем хосте и
fastcgi_finish_request
настроен для полного выхода из скриптов. Мне тоже не нравитсяconnection: close
решение. Его использование требует отдельного подключения для последующих запросов, что требует дополнительных ресурсов сервера. Я прочитал статью вTransfer-Encoding: cunked
Википедии и узнал, что0\r\n\r\n
ответ прекращается. Я не тестировал это полностью на разных версиях браузеров и на разных устройствах, но он работает во всех 4 моих текущих браузерах.источник
Вы можете попробовать сделать многопоточность.
вы можете создать сценарий, который выполняет системный вызов (с использованием shell_exec ), который вызывает двоичный файл php со сценарием для выполнения вашей работы в качестве параметра. Но я не думаю, что это самый безопасный способ. Может быть, вы можете усложнить ситуацию, отключив процесс php и другие вещи
В качестве альтернативы в phpclasses есть класс, который это делает http://www.phpclasses.org/browse/package/3953.html . Но я не знаю специфики реализации
источник
&
символ, чтобы запустить процесс в фоновом режиме.TL; DR Ответ:
Функциональный ответ:
источник
Ваша проблема может быть решена путем параллельного программирования на php. Я задал вопрос об этом несколько недель назад здесь: Как можно использовать многопоточность в приложениях PHP
И получил отличные ответы. Одно мне особенно понравилось. Автор сослался на учебник « Простая параллельная обработка в PHP» (сентябрь 2008 г .; автор johnlim), который действительно может очень хорошо решить вашу проблему, поскольку я уже использовал его для решения аналогичной проблемы, возникшей пару дней назад.
источник
Ответ Джоэри Себрехтса близок, но он уничтожает любой существующий контент, который может быть помещен в буфер, прежде чем вы захотите отключиться. Он не вызывается
ignore_user_abort
должным образом, что приводит к преждевременному завершению сценария. Ответ diyism хорош, но в целом неприменим. Например, у человека может быть больше или меньше буферов вывода, которые этот ответ не обрабатывает, поэтому он может просто не работать в вашей ситуации, и вы не будете знать почему.Эта функция позволяет вам отключиться в любое время (если заголовки еще не отправлены) и сохраняет созданный вами контент. Дополнительное время обработки по умолчанию не ограничено.
Если вам тоже нужна дополнительная память, выделите ее перед вызовом этой функции.
источник
Примечание для пользователей mod_fcgid (пожалуйста, используйте на свой страх и риск).
Быстрое решение
Принятый ответ Джоэри Себрехтса действительно работает. Однако, если вы используете mod_fcgid, вы можете обнаружить, что это решение не работает само по себе. Другими словами, когда вызывается функция очистки, соединение с клиентом не закрывается.
FcgidOutputBufferSize
Параметр конфигурации mod_fcgid может быть виноват. Я нашел этот совет в:Прочитав вышесказанное, вы можете прийти к выводу, что быстрым решением было бы добавить строку (см. «Пример виртуального хоста» в конце):
в файле конфигурации Apache (например, httpd.conf), в файле конфигурации FCGI (например, fcgid.conf) или в файле виртуальных хостов (например, httpd-vhosts.conf).
Детали и второе решение
Вышеупомянутое решение отключает буферизацию, выполняемую mod_fcgid для всего сервера или для определенного виртуального хоста. Это может привести к снижению производительности вашего веб-сайта. С другой стороны, это может быть не так, поскольку PHP выполняет буферизацию самостоятельно.
Если вы не хотите отключать буферизацию mod_fcgid , есть другое решение ... вы можете принудительно очистить этот буфер .
Приведенный ниже код делает именно это, основываясь на решении, предложенном Джоэри Себрехтсом:
По сути, добавленная строка кода заполняет буфер mod_fcgi , заставляя его очищаться . Число «65537» было выбрано, потому что значение
FcgidOutputBufferSize
переменной по умолчанию - «65536», как указано на веб-странице Apache для соответствующей директивы . Следовательно, вам может потребоваться изменить это значение соответствующим образом, если в вашей среде установлено другое значение.Моя среда
Пример виртуального хоста
источник
это сработало для меня
источник
Хорошо, так что в основном способ, которым jQuery выполняет запрос XHR, даже метод ob_flush не будет работать, потому что вы не можете запустить функцию для каждого onreadystatechange. jQuery проверяет состояние, а затем выбирает необходимые действия (завершение, ошибка, успех, тайм-аут). И хотя мне не удалось найти ссылку, я помню, что слышал, что это работает не со всеми реализациями XHR. Метод, который, как я считаю, должен работать для вас, представляет собой нечто среднее между ob_flush и опросом с постоянным кадром.
И поскольку сценарии выполняются в режиме реального времени, вы получаете выполнение при очистке буферов. Чтобы сделать это полезным, измените console.log на метод обратного вызова, определенный в основных настройках сценария, для получения данных и действий с ними. Надеюсь это поможет. Ура, Морган.
источник
Альтернативным решением является добавление задания в очередь и создание сценария cron, который проверяет наличие новых заданий и запускает их.
Недавно мне пришлось сделать это таким образом, чтобы обойти ограничения, накладываемые общим хостом - exec () и др. Был отключен для PHP, запускаемого веб-сервером, но мог работать в сценарии оболочки.
источник
Если
flush()
функция не работает. Вы должны установить следующие параметры в php.ini, например:источник
Последнее рабочее решение
источник
Попробовав много разных решений из этого потока (после того, как ни одно из них не сработало для меня), я нашел решение на официальной странице PHP.net:
источник