在发送http响应后继续处理php
我的脚本被服务器调用。从服务器我将收到ID_OF_MESSAGE
和TEXT_OF_MESSAGE
。在发送http响应后继续处理php
在我的脚本中,我会处理传入的文本并使用params生成响应:ANSWER_TO_ID
和RESPONSE_MESSAGE
。
的问题是,我要送响应进来的"ID_OF_MESSAGE"
,但服务器作为其交付给我给我的消息处理将设置他的信息(这意味着我可以给他针对这一ID),接收HTTP响应后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);
暗示放在这里的响应但是这个代码是没有必要的。所有这些都是确保脚本在用户中止(通过关闭浏览器或按下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);
。
不错的代码!很有用! – 2016-02-12 10:13:24
尝试了很多不同的组合,这是一个工作!谢谢Kosta Kontos !!! – 2016-08-05 10:43:27
对于我而言,在nginx,php 5.6中不起作用 – Dave 2016-08-10 22:38:15
我为此使用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的行为已经发生变化linklink
这不是要求的 - 这个函数基本上只是扩展了脚本终止事件,并且仍然是输出缓冲区的一部分。 – fisk 2016-01-03 14:48:46
@Fiskie你是对的。我添加了一个编辑。 – martti 2016-01-08 11:15:33
发现它值得赞赏,因为它显示什么是行不通的。 – Trendfischer 2016-09-09 13:06:34
由@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);
我在原答案中提到过这个,但我也会在这里说。您不一定需要关闭连接,但是会发生什么情况是,将在同一连接上请求的下一个资产将被迫等待。因此,您可以快速提供HTML,但之后您的JS或CSS文件之一的加载速度可能会很慢,因为连接必须在获得PHP的响应之后才能获得下一个资源。所以出于这个原因,关闭连接是一个好主意,所以浏览器不必等待它被释放。 – 2015-09-17 03:42:33
还有另外一种方法及其值得考虑的是,如果你不想篡改响应头。如果你在另一个进程上启动一个线程,被调用的函数将不会等待它的响应,并且会以最终的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考虑。完成了。
一些链接,以帮助并行线程
- How can one use multi threading in PHP applications
- http://php.net/manual/kr/pthreads.installation.php
好运。
如果你使用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
在PHP的file_get_contents的情况下使用,连接关闭是不够的。 php仍然等待服务器发送eof女巫。
我的解决办法是阅读 '内容长度:'
这里是示例:
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);
正如你可以看到这个脚本dosent等待约EOF如果内容长度达到。
希望这将有助于
我花在这个问题了几个小时,我来到这个功能,在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月,理由是这些文章:
- https://www.zulius.com/how-to/close-browser-connection-continue-execution/
- close a connection early
- http://php.net/manual/en/features.connection-handling.php
我提出一个新的PHP内置的发展在函数中通知平台没有进一步的输出(在标准输出?)将被生成(这样的功能可能会负责关闭连接)。 Rasmus Lerdorf回应:
请参阅Gearman。你真的不希望你的前端Web服务器做这样的后端处理。
我可以看到他的观点,并支持他对一些应用程序/加载场景的看法!但是,在其他一些情况下,vcampitelli等人的解决方案是很好的解决方案。
是否有可能通过保持连接来执行此操作? – Congelli501 2014-07-09 19:14:33
优秀!这是对这个问题的唯一回应,实际上起作用! 10p + – 2014-08-01 19:13:33
真棒回答!我改变的唯一的东西是'set_time_limit(0);'。您可能希望它运行时间超过默认的30秒,但如果进入无限循环,无限期地可能会导致问题!我在'php.ini'文件中设置了更长的值。 – 2014-08-21 14:25:46