简体   繁体   English

运行并发PHP脚本

[英]Running concurrent PHP scripts

I'm having the following problem with my VPS server. 我的VPS服务器出现以下问题。

I have a long-running PHP script that sends big files to the browser. 我有一个长期运行的PHP脚本,它将大文件发送到浏览器。 It does something like this: 它执行以下操作:

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

This basically reads the file from the server's file system and sends it to the browser. 这基本上是从服务器的文件系统读取文件并将其发送到浏览器。 I can't just use direct links(and let Apache serve the file) because there is business logic in the application that needs to be applied. 我不能只使用直接链接(并让Apache提供文件),因为应用程序中需要应用业务逻辑。

The problem is that while such download is running, the site doesn't respond to other requests. 问题在于,在运行此类下载时,该站点不会响应其他请求。

The problem you are experiencing is related to the fact that you are using sessions. 您遇到的问题与您正在使用会话有关。 When a script has a running session, it locks the session file to prevent concurrent writes which may corrupt the session data. 当脚本具有正在运行的会话时,它将锁定会话文件,以防止可能导致会话数据损坏的并发写入。 This means that multiple requests from the same client - using the same session ID - will not be executed concurrently, they will be queued and can only execute one at a time. 这意味着来自同一客户端的多个请求(使用相同的会话ID)将不会同时执行,它们将排队,并且一次只能执行一个。

Multiple users will not experience this issue, as they will use different session IDs. 多个用户将不会遇到此问题,因为他们将使用不同的会话ID。 This does not mean that you don't have a problem, because you may conceivably want to access the site whilst a file is downloading, or set multiple files downloading at once. 这并不意味着您没有问题,因为您可能会想在下载文件时访问该站点,或者设置一次下载多个文件。

The solution is actually very simple: call session_write_close() before you start to output the file. 解决方案实际上非常简单:在开始输出文件之前,请调用session_write_close() This will close the session file, release the lock and allow further concurrent requests to execute. 这将关闭会话文件,释放锁并允许进一步的并发请求执行。

Your server setup is probably not the only place you should be checking. 服务器设置可能不是您应该检查的唯一位置。

Try doing a request from your browser as usual and then do another from some other client. 尝试照常从浏览器发出请求,然后再从其他客户端发出另一个请求。

Either wget from the same machine or another browser on a different machine. 从同一台计算机上的wget或另一台计算机上的另一个浏览器。

In what way doesn't the server respond to other requests? 服务器不以什么方式响应其他请求? Is it "Waiting for example.com..." or does it give an error of any kind? 是“正在等待example.com ...”还是给出任何错误?

I do something similar, but I serve the file chunked, which gives the file system a break while the client accepts and downloads a chunk, which is better than offering up the entire thing at once, which is pretty demanding on the file system and the entire server. 我做类似的事情,但我将文件分块处理,这使文件系统在客户端接受并下载块时有所中断,这比一次提供全部内容要好,这对文件系统和整个服务器。

EDIT: While not the answer to this question, asker asked about reading a file chunked. 编辑:虽然不是这个问题的答案,但问问者询问了如何读取分块的文件。 Here's the function that I use. 这是我使用的功能。 Supply it the full path to the file. 提供文件的完整路径。

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;
}

The same happens go to me and i'm not using sessions. 同样的事情发生在我身上,我没有使用会话。 session.auto_start is set to 0 My example script only runs "sleep(5)", and adding "session_write_close()" at the beginning doesn't solve the problem. session.auto_start设置为0我的示例脚本仅运行“ sleep(5)”,并且在开头添加“ session_write_close()”不能解决问题。

Check your httpd.conf file. 检查您的httpd.conf文件。 Maybe you have "KeepAlive On" and that is why your second request hangs until the first is completed. 也许您具有“ KeepAlive On”功能,这就是为什么您的第二个请求挂起直到第一个请求完成。 In general your PHP script should not allow the visitors to wait for long time. 通常,您的PHP脚本不应允许访问者等待很长时间。 If you need to download something big, do it in a separate internal request that user have no direct control of. 如果您需要下载较大的内容,请在用户无法直接控制的单独内部请求中进行下载。 Until its done, return some "executing" status to the end user and when its done, process the actual results. 在完成之前,向最终用户返回一些“正在执行”状态,完成后将处理实际结果。

I have tried different approaches (reading and sending the files in small chunks [see comments on readfile in PHP doc], using PEARs HTTP_Download) but I always ran into performance problems when the files are getting big. 我尝试了不同的方法(使用PEARs HTTP_Download读取和发送小块文件(请参见PHP文档中有关readfile注释),但是当文件变大时,我总是会遇到性能问题。

There is an Apache mod X-Sendfile where you can do your business logic and then delegate the download to Apache. 有一个Apache mod X-Sendfile ,您可以在其中执行业务逻辑,然后将下载委派给Apache。 The download will not be publicly available. 该下载将不会公开提供。 I think, this is the most elegant solution for the problem. 我认为,这是解决该问题的最优雅的方法。

More Info: 更多信息:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM