上传文件慢(nginx + http2)

上传文件慢(nginx + http2)

我正在将 php webapp 从 apache 服务器迁移到 nginx。除上传文件外,一切正常。

在当前服务器上使用 apache 上传一个 156 MB 的文件大约需要 11 秒,而在新服务器上使用 nginx 上传同一个文件大约需要 38 秒。

第一种方法(新服务器上 apache 与 nginx 共存)

首先我考虑的是服务器宽带问题,因此我在新服务器上安装了 Apache,以便与 nginx 使用不同的端口访问相同的 web 应用程序。

上传时间与旧服务器相同,约为 11 秒。新服务器没有问题。

第二种方法(nginx 作为反向代理)

阅读有关 apache 和 nginx 的文章,似乎 nginx 在资产和缓存方面速度更快,但处理 php 文件时速度较慢(您需要将请求传递给 php fpm)。

我尝试使用 nginx 作为 apache 的反向代理。资产由 nginx 提供,php 请求通过代理传递到 apache。效果很好,上传时间约为 11 秒。

我以为我已经解决了所有问题。在进行所有这些测试时,我已禁用 tls 证书。我通过 80 端口以非安全方式对其进行了测试。启用 tls 证书后,我回到 ~38s 进行上传。

新服务器是安全的并使用 HTTP/2(~38 秒)。

当前使用 apache 的服务器也是安全的,但是使用的是 HTTP/1.1(~11s)。

第三种方法(回到 nginx 和 php fpm)

我回到第一步,使用反向代理并没有获得任何好处。仅使用 nginx 和 php fpm 且没有证书的 Web 应用程序运行正常:在约 11 秒后再次上传。

因此,使用带有证书和 HTTP/2 的 nginx 是个问题。

第四种方法(配置nginx)

我读过关于流控制窗口的三个有趣的资源:

如何在上传时使用多路复用 http2 功能

提供 HTTP/2 上传速度改进

HTTP/2 流量控制

我认为在默认 nginx 配置下,从客户端到服务器上传大文件可能有很多往返。我尝试了各种配置 nginx(缓冲和 http2)的方法,以获得较大的控制窗口,但事实证明默认配置效果很好。

我用的是nghttp命令查看客户端和服务器在上传大文件时的交互。完全没有往返,客户端可以不间断地发送整个文件,耗时略长于 ~11 秒(但可以接受)。该命令的输出如下所示:

[  0.116] Connected
The negotiated protocol: h2
[  0.176] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[  0.176] send PRIORITY frame <length=5, flags=0x00, stream_id=3>
          (dep_stream_id=0, weight=201, exclusive=0)
[  0.176] send PRIORITY frame <length=5, flags=0x00, stream_id=5>
          (dep_stream_id=0, weight=101, exclusive=0)
[  0.177] send PRIORITY frame <length=5, flags=0x00, stream_id=7>
          (dep_stream_id=0, weight=1, exclusive=0)
[  0.177] send PRIORITY frame <length=5, flags=0x00, stream_id=9>
          (dep_stream_id=7, weight=1, exclusive=0)
[  0.177] send PRIORITY frame <length=5, flags=0x00, stream_id=11>
          (dep_stream_id=3, weight=1, exclusive=0)
[  0.177] send HEADERS frame <length=56, flags=0x24, stream_id=13>
          ; END_HEADERS | PRIORITY
          (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
          ; Open new stream
          :method: POST
          :path: /
          :scheme: https
          :authority: www.xxxxxxx.com
          accept: */*
          accept-encoding: gzip, deflate
          user-agent: nghttp2/1.40.0
          content-length: 156631084
[  0.177] send DATA frame <length=16384, flags=0x00, stream_id=13>
[  0.177] send DATA frame <length=16384, flags=0x00, stream_id=13>
[  0.177] send DATA frame <length=16384, flags=0x00, stream_id=13>
[  0.177] send DATA frame <length=16383, flags=0x00, stream_id=13>
[  0.222] recv SETTINGS frame <length=18, flags=0x00, stream_id=0>
          (niv=3)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):128]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65536]
          [SETTINGS_MAX_FRAME_SIZE(0x05):16777215]
[  0.222] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=0>
          (window_size_increment=2147418112)
[  0.224] send SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  0.224] send DATA frame <length=1, flags=0x00, stream_id=13>
[  0.273] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>
          ; ACK
          (niv=0)
[  0.273] recv WINDOW_UPDATE frame <length=4, flags=0x00, stream_id=13>
          (window_size_increment=2147418111)
[  0.273] send DATA frame <length=16384, flags=0x00, stream_id=13>
[  0.274] send DATA frame <length=16384, flags=0x00, stream_id=13>
[  0.274] send DATA frame <length=16384, flags=0x00, stream_id=13>
[  0.274] send DATA frame <length=16384, flags=0x00, stream_id=13>

(...)

[ 14.587] send DATA frame <length=16384, flags=0x00, stream_id=13>
[ 14.587] send DATA frame <length=16384, flags=0x00, stream_id=13>
[ 14.587] send DATA frame <length=16384, flags=0x00, stream_id=13>
[ 14.587] send DATA frame <length=16384, flags=0x00, stream_id=13>
[ 14.587] send DATA frame <length=16384, flags=0x00, stream_id=13>
[ 14.587] send DATA frame <length=44, flags=0x01, stream_id=13>
          ; END_STREAM
[ 17.404] recv (stream_id=13) :status: 200
[ 17.404] recv (stream_id=13) server: nginx
[ 17.404] recv (stream_id=13) date: Wed, 27 Jan 2021 07:57:30 GMT
[ 17.404] recv (stream_id=13) content-type: text/html; charset=UTF-8
[ 17.404] recv (stream_id=13) content-length: 0
[ 17.404] recv (stream_id=13) x-frame-options: SAMEORIGIN
[ 17.404] recv (stream_id=13) x-xss-protection: 1; mode=block
[ 17.404] recv (stream_id=13) x-content-type-options: nosniff
[ 17.404] recv HEADERS frame <length=126, flags=0x05, stream_id=13>
          ; END_STREAM | END_HEADERS
          (padlen=0)
          ; First response header
[ 17.404] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
          (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])

但是当客户端是浏览器时,我又陷入了上传卡顿,耗时约 38 秒。

我有proxy_request_buffering on,nginx 处理所有上传,然后将请求发送到 fastcgi sock。

我不明白为什么 nginx 服务器与 nghttp 客户端交互需要的时间是可以接受的,并且上传过程不会中断,而当客户端是浏览器时,需要大约 38 秒。浏览器与服务器的通信方式是否与 nghttp 不同?

有没有办法调试浏览器和服务器之间的通信?开发人员工具似乎没有提供像我发布的日志这样的信息。

nginx 如何处理 http2 上的上传?有人能解释一下吗?或者给我一些线索,告诉我要寻找什么?

答案1

HTTP/2 上传缓冲区自动调整是 Cloudflare 提出的补丁。详细的问题解释和修复请访问

https://blog.cloudflare.com/delivering-http-2-upload-speed-improvements/

“每个 HTTP/2 流都有自己的流量控制窗口,并且连接中的所有流都有连接级流量控制。如果太紧,发送方将被流量控制阻止”

快速解决方法是禁用 http/2 或应用补丁。

这可能不是一个解决方案,请添加我发现的更多详细信息。

相关内容