在我的网站启用 HTTP/2 后,我发现性能急剧下降。下载速度变得非常慢,大图像请求会阻塞其他 API 调用。
以下是一个示例网页来说明此问题:
<img src="img.jpg">
<script>
for(var i=0;i<50;i++)
setTimeout(()=>{
fetch('foo.txt');
},i*100);
</script>
(img.jpg
是一张约 700KB 的图像和foo.txt
一个小型文本文件。所有内容均直接由 nginx 提供。)
以下是未启用 HTTP/2 时的时间线图(listen 443 ssl
):
... 当启用 HTTP/2 时(listen 443 ssl http2
):
您可以看到 HTTP/2 导致img.jpg
和的加载时间更长foo.txt
。
以下是站点配置:
server {
listen 443 ssl http2; # when performing http2 example
# listen 443 ssl; # when performing http1 example
server_name h2.test.**********;
root /home/******/h2-test/;
}
我在 Ubuntu 16.04.6 LTS 上使用 nginx 1.14.2。您对解决此问题有什么建议吗?
答案1
以下主要是“理论性”的答案:
我认为您观察到的是双重问题:测试的性质(请求特别小的文件)以及 Chrome 行为/HTTP/2 的实现。
如果您查看 HTTP/2 图像,您会发现相当多 (18) 个请求同时结束。这可能告诉您,即使它们不是同时开始,它们也会并行运行。
甚至可以在网络上将一条消息的部分内容与另一条消息的部分内容混合在一起
因此看起来好像这就是 Chrome 正在做的事情 - 看到对服务器的未完成的请求,并尝试通过相同的多路复用连接并行发出另一个请求。
由于某种原因,您的服务器在并行请求方面存在某种“扩展”问题。
你说这foo.txt
是一个小文件,但它到底有多小?
有http2_chunk_size指令(默认值为8k
),这意味着 HTTP/2 响应被分成该大小的块,如果请求确实并行运行,并且foo.txt
小于8k
,那么将导致一个请求“等待”foo.txt
另一个请求的完成foo.txt
,直到总共8k
积累了 块以输出到浏览器。
我建议使用该指令并将其降低到小于所要求的文件大小,和/或使用大于的文本文件重复测试8k
。
最后,如果连接不可靠,你可能会遇到 TCP HOL 阻塞问题,这在 HTTP/2 上更为严重。引用这里:
但是,是否存在会使情况恶化的场景?是的。原因是,由于流水线/多路复用功能,使用 HTTP/2 的应用程序可以通过单个 TCP 连接发送更多请求。意外的延迟变化或影响 TCP 队首段的丢失使 HTTP/2 更有可能遇到 HOLB,其影响也更大。基本上,接收器处于空闲状态,直到队首恢复,而所有后续段都由 TCP 堆栈保留。这意味着图像可能已成功下载,但由于 HOLB 仍不会显示。