尽早关闭连接

时间:2020-03-06 14:46:22  来源:igfitidea点击:

我正在尝试进行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( '[email protected]', "okay I'm done", 'Yup, all done.' );

?>

解决方案

我们可以尝试执行多线程。

我们可以制作一个脚本来进行系统调用(使用shell_exec),该脚本使用该脚本调用php二进制文件作为参数来完成工作。但是我认为这不是最安全的方法。也许我们可以通过更改php进程和其他内容来整理内容

另外,在phpclasses上有一个可以做到这一点的类http://www.phpclasses.org/browse/package/3953.html。但我不知道实施的细节

问题可以通过在php中进行一些并行编程来解决。我几周前在这里问了一个问题:如何在PHP应用程序中使用多线程

并得到了很好的答案。我特别喜欢一个。作者参考了PHP中的Easy Parallel Processing(2008年9月; johnlim),该教程实际上可以很好地解决问题,因为我已经使用它来解决了几天前出现的类似问题。

假设我们具有Linux服务器和root用户访问权限,请尝试此操作。这是我找到的最简单的解决方案。

为以下文件创建一个新目录,并为其赋予完全权限。 (稍后我们可以使其更安全。)

mkdir test
chmod -R 777 test
cd test

把它放在一个名为" bgping"的文件中。

echo starting bgping
ping -c 15 www.google.com > dump.txt &
echo ending bgping

注意。当当前进程移至echo命令时,ping命令将在后台运行。
它将对www.google.com进行ping操作15次,这大约需要15秒。

使它可执行。

chmod 777 bgping

将其放在名为" bgtest.php"的文件中。

<?php

echo "start bgtest.php\n";
exec('./bgping', $output, $result)."\n";
echo "output:".print_r($output,true)."\n";
echo "result:".print_r($result,true)."\n";
echo "end bgtest.php\n";

?>

当我们在浏览器中请求bgtest.php时,我们应该迅速获得以下响应,而无需等待
ping命令完成需要15秒。

start bgtest.php
output:Array
(
    [0] => starting bgping
    [1] => ending bgping
)

result:0
end bgtest.php

ping命令现在应该在服务器上运行。我们可以运行PHP脚本来代替ping命令:

php -n -f largejob.php > dump.txt &

希望这可以帮助!

好的,所以基本上jQuery进行XHR请求的方式,甚至ob_flush方法也将不起作用,因为我们无法在每个onreadystatechange上运行一个函数。 jQuery检查状态,然后选择要采取的正确操作(完成,错误,成功,超时)。而且尽管我找不到参考,但我记得听说这不适用于所有XHR实现。
我认为一种对我们有用的方法是在ob_flush和永久帧轮询之间进行交叉。

<?php
 function wrap($str)
 {
  return "<script>{$str}</script>";
 };

 ob_start(); // begin buffering output
 echo wrap("console.log('test1');");
 ob_flush(); // push current buffer
 flush(); // this flush actually pushed to the browser
 $t = time();
 while($t > (time() - 3)) {} // wait 3 seconds
 echo wrap("console.log('test2');");
?>

<html>
 <body>
  <iframe src="ob.php"></iframe>
 </body>
</html>

并且由于脚本是内联执行的,因此在刷新缓冲区时,我们将获得执行权。为了使此功能有用,请将console.log更改为在主脚本设置中定义的回调方法,以接收数据并对其执行操作。希望这可以帮助。干杯,摩根。

另一种解决方案是将作业添加到队列中,并创建一个cron脚本来检查并运行新作业。

最近,我不得不这样做,以避开共享主机exec()等人施加的限制,因为Web服务器运行的PHP已被禁用,但可以在Shell脚本中运行。

以下PHP手册页(包括用户注释)建议了有关如何在不结束PHP脚本的情况下关闭与浏览器的TCP连接的多种说明:

  • 连接处理文档

据说它比发送close标头还需要更多。

然后,OP确认:是的,这成功了:指向复制在此处的用户注释#71172(2006年11月):

Closing the users browser connection whilst keeping your php script running has been an issue since [PHP] 4.1, when the behaviour of register_shutdown_function() was modified so that it would not automatically close the users connection.
  
  sts at mail dot xubion dot hu Posted the original solution:

<?php
header("Connection: close");
ob_start();
phpinfo();
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();
flush();
sleep(13);
error_log("do something in the background");
?>

  
  Which works fine until you substitute phpinfo() for echo('text I want user to see'); in which case the headers are never sent!
  
  The solution is to explicitly turn off output buffering and clear the buffer prior to sending your header information. Example:

<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort(true); // just to be safe
ob_start();
echo('Text the user will see');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush(); // Strange behaviour, will not work
flush(); // Unless both are called !
// Do processing here 
sleep(30);
echo('Text user will never see');
?>

  
  Just spent 3 hours trying to figure this one out, hope it helps someone :)
  
  Tested in:
  
  
  IE 7.5730.11
  Mozilla Firefox 1.81

随后在2010年7月,在一个相关的答案中,Arctic Fire随后将两个跟进的用户注释链接到了上面的一个:

  • 连接处理用户注释#89177(2009年2月)
  • 连接处理用户注释#93441(2009年9月)

有必要发送以下两个标头:

Connection: close
Content-Length: n (n = size of output in bytes )

由于我们需要知道输出的大小,因此需要缓冲输出,然后将其刷新到浏览器:

// buffer all upcoming output
ob_start();
echo "We'll email you as soon as this is done.";

// get the size of the output
$size = ob_get_length();

// send headers to tell the browser to close the connection
header("Content-Length: $size");
header('Connection: close');

// flush all output
ob_end_flush();
ob_flush();
flush();

// if you're using sessions, this prevents subsequent requests
// from hanging while the background process executes
if (session_id()) session_write_close();

/******** background process starts here ********/

另外,如果Web服务器在输出上使用自动gzip压缩(即,使用mod_deflate的Apache),则此方法将不起作用,因为输出的实际大小已更改,并且Content-Length不再准确。禁用gzip压缩特定脚本。

有关更多详细信息,请访问http://www.zulius.com/how-to/close-browser-connection-continue-execution