2012-01-17 162 views
7

我的VPS服务器出现以下问题。运行并发PHP脚本

我有一个长时间运行的PHP脚本,它将大文件发送到浏览器。它确实是这样的:

<?php 
header("Content-type: application/octet-stream"); 
readfile("really-big-file.zip"); 
exit(); 
?> 

这基本上读取服务器的文件系统中的文件,并将其发送给浏览器。我不能只使用直接链接(并让Apache服务于该文件),因为应用程序中存在需要应用的业务逻辑。

问题是,虽然此类下载正在运行,但该站点不响应其他请求。

+0

不是说这是问题,但是当服务大文件时,您应该始终调用'set_time_limit(0);'。它目前对你来说应该没有什么区别,但是如果你在某些时候将它移到* Windows平台,可能会遇到潜在的问题。 – DaveRandom 2012-01-17 11:47:23

+1

你是如何发现这个问题的?你是通过从同一台机器发出多个请求来测试它吗?你在使用会话吗? – DaveRandom 2012-01-17 11:53:48

+0

@DaveRandom当我尝试下载多个文件时(他们被排队等待下载),我注意到了这个问题。我正在使用会话 - 只是试过了,看起来这个限制不会影响其他会话。感谢您的想法 - 我现在进一步调查。 – 2012-01-17 12:07:07

回答

30

您遇到有关的事实,你正在使用的会话的问题。当脚本具有正在运行的会话时,它会锁定会话文件以防止可能破坏会话数据的并发写入。这意味着来自同一客户端的多个请求(使用相同的会话ID)将不会同时执行,它们将排队并且一次只能执行一个请求。

多个用户不会遇到此问题,因为他们将使用不同的会话ID。这并不意味着您没有问题,因为您可能想要在下载文件时访问网站,或者一次下载多个文件。

该解决方案实际上非常简单:在开始输出文件之前调用session_write_close()。这将关闭会话文件,释放锁并允许执行进一步的并发请求。

+0

谢谢,那就是问题所在。 – 2012-01-17 12:37:42

+0

好点和+1 – Lion 2012-01-20 18:02:02

+0

简直太棒了。你让我很快乐! – Claudix 2012-10-25 09:39:57

1

服务器以何种方式不响应其他请求?它是“等待example.com ...”还是它给出了任何类型的错误?

我做了类似的事情,但是我提供了分块的文件,当客户端接受并下载一个块时,它给文件系统一个中断,这比一次提供整个东西要好,这对于文件系统和整个服务器。

编辑:虽然不是这个问题的答案,asker问关于阅读文件chunked。这是我使用的功能。为它提供文件的完整路径。

function readfile_chunked($file_path, $retbytes = true) 
{ 
$buffer = ''; 
$cnt = 0; 
$chunksize = 1 * (1024 * 1024); // 1 = 1MB chunk size 
$handle = fopen($file_path, 'rb'); 
if ($handle === false) { 
    return false; 
} 
while (!feof($handle)) { 
    $buffer = fread($handle, $chunksize); 
    echo $buffer; 
    ob_flush(); 
    flush(); 
    if ($retbytes) { 
     $cnt += strlen($buffer); 
    } 
} 
    $status = fclose($handle); 
    if ($retbytes && $status) { 
     return $cnt; // return num. bytes delivered like readfile() does. 
} 
    return $status; 
} 
+0

新请求正在等待下载完成。我不太确定如何在PHP中实现块下载? – 2012-01-17 11:47:16

+0

嗨,Emil M.我添加了我用来以块读取文件的函数。 – 2012-01-17 13:16:02

+0

我刚刚答复了你的答案,请让我解释一下。 readfile不会将文件内容读入内存,然后将其写入标准输出。不,它调用内部函数'_php_stream_passthru()',这是一个类似于你的C循环,除非它不执行刷新,因为'output_buffering' INI设置无论如何都会实现这一点。对于输出已经压缩的内容,'zlib.output_compression'等应该是错误的。你的建议只是增加了复杂性,没有任何好处。 – TerryE 2014-07-09 15:45:20

0

我已经尝试了不同的方法(阅读和小块[查看PHP文档上readfile评论]发送文件,用梨HTTP_Download),但我总是遇到了性能问题时,文件越来越大。

有一个Apache mod 您可以在其中执行业务逻辑,然后将下载委托给Apache。下载将不会公开。我认为,这是针对该问题的最优雅的解决方案。

更多信息:

2

您的服务器设置可能不是您应该检查的唯一位置。

尝试像往常一样从浏览器执行请求,然后从其他客户端执行另一个请求。

来自同一台机器的wget或另一台机器上的另一台浏览器。

0

同样的事情发生在我身上,我没有使用会话。 session.auto_start设置为0 我的示例脚本只运行“sleep(5)”,并且在开头添加“session_write_close()”并不能解决问题。

0

检查你的httpd.conf文件。也许你有“KeepAlive On”,这就是为什么你的第二个请求挂起,直到第一个请求完成。一般而言,您的PHP脚本不应允许访问者等待很长时间。如果您需要下载一些大的内容,请在用户无法直接控制的单独内部请求中进行。在完成之前,向最终用户返回一些“执行”状态,并在完成后处理实际结果。