【问题标题】:Running concurrent PHP scripts运行并发 PHP 脚本
【发布时间】:2017-07-07 19:41:53
【问题描述】:

我的 VPS 服务器出现以下问题。

我有一个长时间运行的 PHP 脚本,可以将大文件发送到浏览器。它做了这样的事情:

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

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

问题在于,当此类下载正在运行时,该站点不会响应其他请求。

【问题讨论】:

  • 这不是问题所在,但在提供大文件时,您应该始终调用set_time_limit(0);。目前它对您没有任何影响,但如果您在某个时候将其移至 Windows 平台,可能会解决您可能遇到的潜在问题。
  • 您是如何发现问题的?您是否通过从同一台机器发出多个请求来测试这一点?你在使用会话吗?
  • @DaveRandom 我在尝试下载多个文件时注意到了这个问题(它们已排队等待下载)。我正在使用会话——刚刚尝试过,看起来这个限制不会影响其他会话。感谢您的想法 - 我现在会进一步调查。

标签: php apache concurrency


【解决方案1】:

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

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

解决方法其实很简单:在开始输出文件之前调用session_write_close()。这将关闭会话文件,释放锁定并允许执行更多并发请求。

【讨论】:

  • 谢谢,这就是问题所在。
  • 真是救命稻草,真希望我几年前偶然发现。
【解决方案2】:

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

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

从同一台机器或不同机器上的另一个浏览器中获取 wget。

【讨论】:

    【解决方案3】:

    服务器以什么方式不响应其他请求?是“Waiting for example.com...”还是任何类型的错误?

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

    编辑:虽然不是这个问题的答案,但询问者询问了有关读取分块文件的问题。这是我使用的功能。向它提供文件的完整路径。

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

    【讨论】:

    • 新请求正在等待下载完成。我不太确定如何在 PHP 中实现块下载?
    • 嗨,Emil M。我添加了用于分块读取文件的功能。
    • 我刚刚对你的答案做了-1,所以让我解释一下。 readfile 不会将文件内容读入内存,然后将其写入标准输出。不,它调用内部函数_php_stream_passthru(),这是一个类似于您的C 循环,除了它不执行刷新,因为output_buffering INI 设置无论如何都会实现这一点。而zlib.output_compression 等对于输出已经压缩的内容应该是错误的。您的建议只是增加复杂性而没有任何好处。
    【解决方案4】:

    我尝试了不同的方法(使用 PEARs HTTP_Download 以小块读取和发送文件 [参见 PHP 文档中 readfile 上的 cmets]),但是当文件变大时我总是遇到性能问题。

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

    更多信息:

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          猜你喜欢
          • 2013-04-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-11-04
          • 2021-10-18
          • 2014-03-02
          • 2012-01-15
          相关资源
          最近更新 更多