发送http响应后继续处理php
我的脚本被服务器调用。 从服务器,我会收到ID_OF_MESSAGE
和TEXT_OF_MESSAGE
。
在我的脚本中,我将处理传入的文本并使用params生成响应: ANSWER_TO_ID
和RESPONSE_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提出了这个问题,引用了这些文章:
- https://www.zulius.com/how-to/close-browser-connection-continue-execution/
- 尽早closures连接
- http://php.net/manual/en/features.connection-handling.php
我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应用程序中使用multithreading
- http://php.net/manual/kr/pthreads.installation.php
祝你好运。
在使用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如果内容长度达到。
希望这会有所帮助