谷歌浏览器不使用 http2 进行多路复用

Posted

技术标签:

【中文标题】谷歌浏览器不使用 http2 进行多路复用【英文标题】:Google Chrome does not do multiplexing with http2 【发布时间】:2018-01-05 03:47:27 【问题描述】:

我正在构建一个 webapp 并通过 http2 提供服务。但是,当我在 Google Chrome(版本 59.0.3071.115(官方构建)(64 位))的开发人员工具中分析网络时,很明显多路复用不起作用,因为只有 6 个活动连接(如 http1.1)其余连接排队。

这是为什么?还是我的预期不正确?

截图(可以看到协议是http2):

更新#1:

后端运行在nginx 1.13上; 我正在使用自定义模块加载器,它一次加载所有脚本(通过在循环中创建带有async 属性的脚本标签); 屏幕截图显示,第 8 行及以后的浏览器已收到开始下载资源的请求,但行的白色部分显示此脚本已排队,并且仅在插槽可用时才开始实际下载(请参阅第 8 行,一旦第 2、3 和 4 行完成,7 和 9 开始加载;第 11、12、13 和 5、6、7 行也是如此。

【问题讨论】:

您能否通过可以分享的最小示例重现此问题? @FrederikDeweerdt 感谢您的回复;我无法显示当前环境,但我会为此设置另一个环境,简约以检查确认问题。 【参考方案1】:

我认为这是 Chrome 中的一个错误,或者至少是一个不必要的限制。

这很容易测试。

我创建了一个简单的示例 html 文件,它下载了相同 javascript 文件的 25 个副本(使用查询参数使其看起来像不同的资源):

<!DOCTYPE HTML>
<html>
<head>
        <title>Test for Lots of JS files</title>
        <meta name="robots" content="noindex">
<body>
</body>
        <h1>This is a test for Lots of JS files</h1>

        <script src="/assets/js/test.js?v=01"></script>
        <script src="/assets/js/test.js?v=02"></script>
        <script src="/assets/js/test.js?v=03"></script>
        <script src="/assets/js/test.js?v=04"></script>
        <script src="/assets/js/test.js?v=05"></script>
        <script src="/assets/js/test.js?v=06"></script>
        <script src="/assets/js/test.js?v=07"></script>
        <script src="/assets/js/test.js?v=08"></script>
        <script src="/assets/js/test.js?v=09"></script>
        <script src="/assets/js/test.js?v=10"></script>
        <script src="/assets/js/test.js?v=11"></script>
        <script src="/assets/js/test.js?v=12"></script>
        <script src="/assets/js/test.js?v=13"></script>
        <script src="/assets/js/test.js?v=14"></script>
        <script src="/assets/js/test.js?v=15"></script>
        <script src="/assets/js/test.js?v=16"></script>
        <script src="/assets/js/test.js?v=17"></script>
        <script src="/assets/js/test.js?v=18"></script>
        <script src="/assets/js/test.js?v=19"></script>
        <script src="/assets/js/test.js?v=20"></script>
        <script src="/assets/js/test.js?v=21"></script>
        <script src="/assets/js/test.js?v=22"></script>
        <script src="/assets/js/test.js?v=23"></script>
        <script src="/assets/js/test.js?v=24"></script>
        <script src="/assets/js/test.js?v=25"></script>

</html>

然后我做了同样的事情,但添加了 async 属性,以防 Chrome 在处理 Javascript 时决定阻止下载:

        <script src="/assets/js/test.js?v=01" async=""></script>
        <script src="/assets/js/test.js?v=02" async=""></script>
        ....etc.

同样如此,但带有 defer 属性:

        <script src="/assets/js/test.js?v=01" defer=""></script>
        <script src="/assets/js/test.js?v=02" defer=""></script>
        ....etc.

/assets/js/test.js 文件为空。所以除了浏览器添加的那些之外,不会有执行延迟,也没有依赖。

我看到了一些有趣的结果!这都是使用 Chrome 60.0.3112.78 或 60.0.3112.101,我使用的是 Apache,但看到的结果与您在 Nginx 中看到的结果相同。

使用 HTTP/2 服务器,我们看到以下结果:

使用普通的script 标签,所有脚本都是并行加载的(但可能是按顺序执行的)。 HTTP/1.1 下没有 6 个连接限制:

使用 async script 标记,脚本以 6 个一组并行加载 - 正如您所指出的:

点击它们表明它们是通过 HTTP/2 下载的。

使用 defer script 标签,脚本与使用 async 标签的结果相同 - 一次限制为 6 次下载。

这没有意义 - Chrome 会限制您的 Javascript 下载,但前提是您使用异步或延迟来改善您的下载,以免阻塞渲染!

正如 sbordet 所说,视口中的图像不会发生同样的情况 - 所以多路复用确实可以在 Chrome 上工作,它似乎对异步或延迟模式下的 Javascript 进行了不必要的限制。如果您考虑在 HTTP/2 下不再将脚本捆绑在一起,这是一个真正的限制,因为许多人建议您不再需要这样做。

不会同样发生在 Firefox 和 Edge 上。虽然它确实发生在 Opera(基于 Chromium 的浏览器)上。

所以这是个坏消息。好消息是他们“可能”已经修复了它。当我尝试 Chrome Canary (62.0.3190.0) 时,我无法重复这种行为。但是,当我将 Web Page Test 与 Canary 一起使用时(它在用户代理字符串中给出 62.0.3190.1,因此应该几乎相同)它 可重复的,所以不能 100% 确定他们在之后修复了这个问题所有...

已经向 Chrome 团队提出了一个错误,所以看看他们怎么说:https://bugs.chromium.org/p/chromium/issues/detail?id=757191

总而言之,服务器和客户端上的 HTTP/2 目前看起来确实有点不稳定,因为双方都在调整和调整他们的实现,以充分利用这个仍然相对较新的协议。尽管如此,令人惊讶的是,Chrome 受到了这一打击,因为 Google 以他们的 SDPY 实现(HTTP/2 主要基于该实现)开始了这一点,所以你会期望他们走在曲线的前面而不是落后......

** 更新**

Chrome 团队回来确认这是对 Chrome 中当前 HTTP/2 实现的限制。当 HTTP/2 允许同时调用许多资产时,他们会看到性能问题,因此将非关键项目(包括异步/延迟和视口中不可见的项目)限制为 HTTP/1.1 限制为 6。

尽管 HTTP/2 具有在请求发送后对其进行优先排序的概念,但性能问题在它们被优先处理和发送之前就已经被发现(例如检查缓存、cookie 等),因此 HTTP/2 优先排序没有帮助在这里。

他们希望在未来改进这一点。

所以我猜我是对的,这是一个实现问题,因为我们已经习惯了新的 HTTP/2 世界并且必须为此优化我们的浏览器和服务器!

【讨论】:

添加了来自 Chrome 团队的反馈。 您好,从 2020 年开始,Chrome 团队尚未解决此问题 :( 您好,从 2021 年开始,有人可以确认这是否已修复?【参考方案2】:

Chrome 在使用 HTTP/2 时决定限制多路复用的原因可能有多种。

例如,当您下载包含大量图像的页面时,行为会非常不同,具体取决于图像是否显示在浏览器视口中。

您正在下载的文档是脚本,脚本可能会阻塞,或相互依赖,或以其他方式改变浏览器下载资源的方式。

事实上,如果您访问 HTTP/2 的在线示例,例如 https://http2.golang.org/gophertiles?latency=0,您会发现 Chrome 确实很好地复用了图像的下载(但前提是它们显示在视口中)。

因此,对于您的情况,可能与脚本有关;也许它们相互依赖,这就是为什么 Chrome 不能一次多路复用它们超过 6 个。

如果这是假定 HTTP/1.1 并且现在已被 HTTP/2 淘汰的 JavaScript 加载程序的限制,我不会感到惊讶。

您可以使用 Chrome 开发者工具中的“性能”标签来了解有关您网页性能的更多信息。

您还想查看Page Speed 等工具,它们可以让您了解如何优化您的页面。

总之,我认为这不是 Chrome 如何实现 HTTP/2 的问题,而是您的应用程序/脚本中没有针对 HTTP/2 优化的问题。

【讨论】:

感谢您的回复,但我认为您的任何 cmet 都不适用于我的情况。请参阅原始帖子中的更新 #1。此外,Page Speed 没有给出任何有意义的结果(我得到了 100/100)。

以上是关于谷歌浏览器不使用 http2 进行多路复用的主要内容,如果未能解决你的问题,请参考以下文章

http2多路复用

HTTP2特性优点解析

「知识拾遗」 http2/http3总结

http2.0的时代真的来了...

揭秘http2

优化你的HTTPS(下),你需要这么做