Мой скрипт вызывается сервером. С сервера я получу ID_OF_MESSAGE
и TEXT_OF_MESSAGE
.
В моем сценарии я обрабатываю входящий текст и генерирую ответ с параметрами: ANSWER_TO_ID
и RESPONSE_MESSAGE
.
Проблема в том, что я отправляю ответ на "ID_OF_MESSAGE"
, но сервер, который отправляет мне сообщение для обработки, установит свое сообщение как доставленное мне (это означает, что я могу отправить ему ответ на этот идентификатор), после получения ответа от ответа 200.
Одним из решений является сохранение сообщения в базе данных и создание некоторой cron, которая будет работать каждую минуту, но мне нужно немедленно создать ответное сообщение.
Есть ли какое-то решение, как отправить на сервер HTTP-ответ 200, а затем продолжить выполнение php-скрипта?
Большое спасибо
Да. Вы можете сделать это:
ignore_user_abort(true); set_time_limit(0); ob_start(); // do initial processing here echo $response; // send the response header('Connection: close'); header('Content-Length: '.ob_get_length()); ob_end_flush(); ob_flush(); flush(); // now the request is sent to the browser, but the script is still running // so, you can continue...
Я видел много ответов здесь, которые предлагают использовать ignore_user_abort(true);
но этот код не нужен. Все это гарантирует, что ваш скрипт продолжит выполнение до того, как ответ будет отправлен в случае, если пользователь отменит (закрыв свой браузер или нажав клавишу «Выключить», чтобы остановить запрос). Но это не то, о чем вы просите. Вы просите продолжить выполнение ПОСЛЕ отправки ответа. Все, что вам нужно, это следующее:
// Buffer all upcoming output... ob_start(); // Send your response. echo "Here be response"; // Get the size of the output. $size = ob_get_length(); // Disable compression (in case content length is compressed). header("Content-Encoding: none"); // Set the content length of the response. header("Content-Length: {$size}"); // Close the connection. header("Connection: close"); // Flush all output. ob_end_flush(); ob_flush(); flush(); // Close current session (if it exists). if(session_id()) session_write_close(); // Start your background work here. ...
Если вы обеспокоены тем, что ваша фоновая работа займет больше времени, чем время выполнения скриптов по умолчанию PHP, тогда set_time_limit(0);
на вершине.
Если вы используете FastCGI-обработку или PHP-FPM, вы можете:
session_write_close(); //close the session fastcgi_finish_request(); //this returns 200 to the user, and processing continues // do desired processing ... $expensiveCalulation = 1+1; error_log($expensiveCalculation);
Источник: http://fi2.php.net/manual/en/function.fastcgi-finish-request.php
Изменен ответ на @vcampitelli. Не думайте, что вам нужен close
заголовок. Я видел двойные заголовки в Chrome.
<?php ignore_user_abort(true); ob_start(); echo '{}'; header($_SERVER["SERVER_PROTOCOL"] . " 202 Accepted"); header("Status: 202 Accepted"); header("Content-Type: application/json"); header('Content-Length: '.ob_get_length()); ob_end_flush(); ob_flush(); flush(); sleep(10);
Я потратил несколько часов на эту проблему, и я пришел с этой функцией, которая работает на Apache и Nginx:
/** * respondOK. */ protected function respondOK() { // check if fastcgi_finish_request is callable if (is_callable('fastcgi_finish_request')) { /* * This works in Nginx but the next approach not */ session_write_close(); fastcgi_finish_request(); return; } ignore_user_abort(true); ob_start(); $serverProtocole = filter_input(INPUT_SERVER, 'SERVER_PROTOCOL', FILTER_SANITIZE_STRING); header($serverProtocole.' 200 OK'); header('Content-Encoding: none'); header('Content-Length: '.ob_get_length()); header('Connection: close'); ob_end_flush(); ob_flush(); flush(); }
Вы можете вызвать эту функцию до вашей длительной обработки.
Я задал этот вопрос Расмусу Лердорфу в апреле 2012 года, сославшись на эти статьи:
Я предложил создать новую встроенную функцию PHP, чтобы уведомить платформу о том, что никакой дополнительный вывод (на stdout?) Не будет сгенерирован (такая функция может позаботиться о закрытии соединения). Расмус Лердорф ответил:
См. Gearman . Вы действительно не хотите, чтобы ваши интерфейсные веб-серверы выполняли бэкэнд-обработку таким образом.
Я вижу его точку зрения и поддерживаю его мнение для некоторых приложений / сценариев загрузки! Однако в некоторых других сценариях решения от vcampitelli и др. Являются хорошими.
Для этого я использую функцию php register_shutdown_function.
void register_shutdown_function ( callable $callback [, mixed $parameter [, mixed $... ]] )
http://php.net/manual/en/function.register-shutdown-function.php
Изменить : Вышеуказанное не работает. Кажется, я был введен в заблуждение какой-то старой документацией. Поведение register_shutdown_function изменилось с момента ссылки на PHP 4.1
Существует еще один подход, и его стоит рассмотреть, если вы не хотите вмешиваться в заголовки ответов. Если вы начнете поток в другом процессе, вызываемая функция не будет ждать ответа и вернется в браузер с завершенным http-кодом. Вам нужно будет настроить pthread .
class continue_processing_thread extends Thread { public function __construct($param1) { $this->param1 = $param1 } public function run() { //Do your long running process here } } //This is your function called via an HTTP GET/POST etc function rest_endpoint() { //do whatever stuff needed by the response. //Create and start your thread. //rest_endpoint wont wait for this to complete. $continue_processing = new continue_processing_thread($some_value); $continue_processing->start(); echo json_encode($response) }
После того, как мы выполним $ continue_processing-> start (), PHP не будет ждать результата возврата этого потока, и поэтому, поскольку рассматривается точка rest_endpoint. Сделано.
Некоторые ссылки для помощи с pthreads
Удачи.
в случае использования php file_get_contents, закрыть соединение недостаточно. php все еще ждет, пока eh ведьма отправит сервер.
мое решение состоит в том, чтобы прочитать «Content-Length:»
вот пример:
response.php:
<?php ignore_user_abort(true); set_time_limit(500); ob_start(); echo 'ok'."\n"; header('Connection: close'); header('Content-Length: '.ob_get_length()); ob_end_flush(); ob_flush(); flush(); sleep(30);
Обратите внимание на «\ n» в ответ на закрытие строки, если не считать fget во время ожидания eof.
read.php:
<?php $vars = array( 'hello' => 'world' ); $content = http_build_query($vars); fwrite($fp, "POST /response.php HTTP/1.1\r\n"); fwrite($fp, "Content-Type: application/x-www-form-urlencoded\r\n"); fwrite($fp, "Content-Length: " . strlen($content) . "\r\n"); fwrite($fp, "Connection: close\r\n"); fwrite($fp, "\r\n"); fwrite($fp, $content); $iSize = null; $bHeaderEnd = false; $sResponse = ''; do { $sTmp = fgets($fp, 1024); $iPos = strpos($sTmp, 'Content-Length: '); if ($iPos !== false) { $iSize = (int) substr($sTmp, strlen('Content-Length: ')); } if ($bHeaderEnd) { $sResponse.= $sTmp; } if (strlen(trim($sTmp)) == 0) { $bHeaderEnd = true; } } while (!feof($fp) && (is_null($iSize) || !is_null($iSize) && strlen($sResponse) < $iSize)); $result = trim($sResponse);
Как вы можете видеть, этот сценарий доцент ожидает, если длина контента будет достигнута.
надеюсь, что это поможет