发送http响应后继续处理php

我的脚本被服务器调用。 从服务器,我会收到ID_OF_MESSAGETEXT_OF_MESSAGE

在我的脚本中,我将处理传入的文本并使用params生成响应: ANSWER_TO_IDRESPONSE_MESSAGE

问题是,我发送的响应包含"ID_OF_MESSAGE" ,但是发送消息给我的服务器会在接收到http响应200之后,将其消息设置为发送给我(这意味着我可以发送给他对该ID的响应)。

解决scheme之一是将消息保存到数据库,并使每分钟运行一些cron,但我需要立即生成响应消息。

有一些解决scheme如何发送到服务器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); 但是这个代码是没有必要的。 所有这一切都是确保脚本在用户中止(通过closures浏览器或按下escape来停止请求)中止之前发送响应之前继续执行。 但这不是你要求的。 您要求在发送回复后继续执行。 所有你需要的是以下内容:

  // 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中看到重复closures标题。

 <?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年4月向Rasmus Lerdorf提出了这个问题,引用了这些文章:

我build议开发一个新的PHP内置函数来通知平台不会有进一步的输出(在标准输出?)将被生成(这样的function可能会负责closures连接)。 Rasmus Lerdorf回应说:

见Gearman 。 你真的不希望你的前端Web服务器做这样的后端处理。

我可以看到他的观点,并支持他对一些应用程序/加载场景的意见! 然而,在其他一些情况下,vcampitelli等人的解决scheme是好的。

我使用php函数register_shutdown_function。

 void register_shutdown_function ( callable $callback [, mixed $parameter [, mixed $... ]] ) 

http://php.net/manual/en/function.register-shutdown-function.php

编辑 :上面不工作。 看来我被一些旧的文档误导了。 自PHP 4.1 链接 链接以来,register_shutdown_function的行为已经发生了变化

还有另一种方法,值得考虑的是,如果你不想篡改响应头。 如果在另一个进程上启动一个线程,被调用的函数将不会等待它的响应,并且会以一个最终的http代码返回到浏览器。 您将需要configurationpthread

 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的情况下,连接closures是不够的。 PHP仍然等待由服务器发送的女巫。

我的解决scheme是阅读“内容长度:”

这里是样本:

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”在响应closures行,如果不是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); 

正如你可以看到这个脚本剂量等待eof如果内容长度达到。

希望这会有所帮助