大型 tmp 文件传输在 nginx php-fpm 代理中挂起

大型 tmp 文件传输在 nginx php-fpm 代理中挂起

我们有一台服务器,该服务器正在运行一个 WordPress 站点,并在 Ubuntu 20 LTS 上安装了 serverpilot 的 nginx 堆栈。

非常大的上传似乎卡在了 nginx 代理和 PHP 之间的切换中,我已经知道如何排除故障,而不是只是戳它看看会发生什么(这很少是一种很好的时间利用方式或前进的方式)。据我所知,我已经增加了必要的超时时间并提高了所有磁盘限制,但我显然仍然缺少一些东西。

对于我们的用例,我们需要允许上传高达 50GB 的文件,在运行标准 LAMP 堆栈的临时环境中,我们可以毫无问题地完成此操作。对于小于 ~2GB 的文件,我们没有遇到任何问题,但任何超过这个大小的文件都可能会失败,也可能不会,这取决于一些我无法追踪的标准。在之前解决问题时,文件复制停止的时间似乎是任意的(最大 10GB 时成功),但在当前配置下(见下文),当 PHPtmp文件达到 2GB 时,它会一直停止。

当我们上传非常大的文件时,我们可以看到传入的临时文件消耗磁盘空间,直到整个文件都存在/mnt/tmp/[nginx_tmp_path](是的,有足够的可用磁盘空间)。之后,我们可以看到文件被复制到 phptmp路径,但几秒钟后,phptmp文件的大小停止增长,复制过程挂起。最终,达到 600 秒超时之一,并记录错误(见下文)。在此屏幕截图中,我们有一个已完成的(从浏览器/最终用户的角度来看)14.8GB 的​​上传,在 2GB 左右时挂在临时文件传输上。 PHP tmp 文件停止增长

在 600 秒期限结束时,Apache 错误日志中出现以下内容:

(70008)Partial results are valid but processing is incomplete: [client <MY_IP>] AH01075: Error dispatching request to : (reading input brigade), referer: https://<THE_URL>/wp-admin/media-new.php

nginx 错误日志显示如下:

2512066#0: *9 upstream timed out (110: Connection timed out) while sending request to upstream, client: <MY_IP>, server: <INTERNAL_IP>, request: "POST /wp-admin/async-upload.php HTTP/2.0", upstream: "http://127.0.0.1:81/wp-admin/async-upload.php", host: "<THE_URL>", referrer: "https://<THE_URL>/wp-admin/media-new.php"

值得注意的是,这些消息直到上传文件完全到达服务器 10 分钟后才会出现在日志文件中,也就是文件复制似乎暂停/挂起 9 分钟或更长时间后。我确信某些原因导致文件复制卡住,然后最终达到超时 - 我不认为问题出在超时本身。在文件复制停止和日志文件中出现超时错误之间的过渡期间,服务器上没有增加或异常的活动,所有服务都按预期运行和响应。

使用当前配置,PHPtmp文件始终增长到 2097152 KB(根据du -a),这使我相信我达到了尚未发现的内置文件大小限制。

相关的 nginx 服务器配置选项是:在server上下文中:

    proxy_connect_timeout 600;
    proxy_read_timeout 600;
    proxy_send_timeout 600;
    proxy_max_temp_file_size 51200m;

    fastcgi_connect_timeout 600;
    fastcgi_read_timeout 600;
    fastcgi_send_timeout 600;
    fastcgi_request_buffering off;

    keepalive_timeout 600;
    send_timeout 600;

    client_max_body_size 0;
    client_body_temp_path /mnt/tmp;
    client_body_in_file_only clean;

Apache 的 VirtualHost 配置:

    RequestReadTimeout header=0 body=0
    Timeout 3600
    ProxyTimeout 3600

最后,PHP 配置:

memory_limit = -1
max_execution_time = 0
max_input_time = -1
post_max_size = 50G
upload_max_filesize = 50G
default_socket_timeout = -1

我不知道是什么导致了我所看到的症状。任何指点都非常感谢!

补充说明:这些症状让我感觉它并不相关,但万一呢......该网站通过 WP Rocket 运行,但没有像 CloudFlare 这样的外部代理服务。

更新:我在 nginx 配置中添加了以下几行:

    proxy_http_version 1.1;
    proxy_set_header Connection "";

这稍微改变了症状,但没有解决问题。经过这一更改,行为已恢复到我之前解释的情况,文件传输在不可预测的位置停滞。下面的第一个示例在 3823176 KB 处停止,第二个示例在 3264364 KB 处停止。行为差异的原因对我来说毫无意义,但值得报告。 在此处输入图片描述 在此处输入图片描述

更新2:我已经能够明确地将此问题归咎于tmpnginx 和 php 之间文件交接的问题,但我似乎无法确定导致该过程挂起的具体原因。

tmp我们可以通过在 nginx 配置中添加以下几行来跳过 nginx 代理并仅使用 PHP :

    proxy_buffering off;
    proxy_request_buffering off;

通过这种配置,文件直接进入/mnt/tmp/php<RND_STR>,当上传完成时,我们的应用程序会正确地挑选出文件tmp并完成其任务。

但是,这会使上传速度减慢到大约 1/3 的可用带宽,因此这不是一个好的解决方案。但是,这确实证明了这不是应用程序问题。

事情是这样的:

  1. 用户上传一个大文件(我们的用例最大值是 50GB)
  2. 文件tmp完整到达nginx位置
  3. 尝试将文件从 nginx 复制tmp到 PHP tmp- 复制过程将在 3GB 到 10GB 之间的某个不可预测的地方停滞几秒钟。[3b] 此时,我们可以看到两个tmp文件和 PHPtmp文件都包含一些字节,这些字节应该会增长,直到等于 nginxtmp文件的大小,但事实并非如此。两个文件将保持原样,直到达到 600 秒超时之一(见上文),然后日志文件中会出现错误,两个文件都会tmp消失。[3c] 如果文件小于 3GB,则每次都会成功。如果文件超过 3GB,有时可以成功,有时则不行 - 文件越小,成功的可能性就越大。
  4. 绕过nginx的tmp工作原理完全符合预期,只是上传速度较慢。

当文件非常大时,nginx 和 PHP 之间的文件交接过程中肯定会出现一些问题tmp,我很想弄清楚它是什么。

相关内容