首页>安全资讯>HTTP/2 来了,HTTP/2 推送功能详解

HTTP/2 来了,HTTP/2 推送功能详解

HTTP/2 (h2)来了,而且看起来很不错,它最有趣的新功能之一是h2 推送,该功能可使服务器不必等浏览器先发出确切请求就向浏览器发送数据。

这个功能非常有用,因为通常我们需要等待浏览器解析.html并找到所需的链接后才能发送数据。而这一般会在数据到达浏览器前造成2次(取回.html需要1次,取回资源还需要1次)往返延时(RTT)。所以,有了h2 推送,我们就能消除多余的RTT,使我们的网站速度更快!这在概念上与.html中的内联关键CSS/JS相似,但需要更好地利用缓存。推送功能还有助于在连接开始时更好地利用带宽。网上有大量帖子在讨论该功能的基础知识、怎样在不同平台上使用它以及怎样改进它。

HTTP/2推送功能

图1:载入index.html后推送功能的工作原理

在没有带宽限制的理想世界中,理论上我们可以将我们所有的资源通过.html进行推送,这样浏览器完全不需要等待(请看图1右边),但不幸的是,实际上稍微多推送一点数据就会导致网站加载速度明显减慢。

本文目的在于探讨一些决定推送功能效果及其在实际应用中的影响的底层问题。本文对新手应该也不难理解,而一些程度较高的读者可以在一些参考链接中进行更深入的了解,并对第二章中的观点进行思考。

Colin Bendell说:“如果你只是正确配置了那些最基本的东西(例如缓存和.html),那么推送功能的效果可能会有所减弱,但推送功能所解决的问题会一直在那里。我们应该考虑将h2推送作为保险策略。”

1. 底层的基本原理和限制

h2推送功能的性能严重依赖于底层网络协议和h2协议本身的其他方面。在此我们浅显地介绍一下这些原理,以便稍后在第二章中在实践层面讨论它们。

1.1 带宽和TCP慢启动

在互联网上,每个连接都有一个带宽限制。如果我们试图一次发送过多的数据,网络就会抛弃多余的数据,以保持连接畅通,避免溢出和拥塞(数据包丢失)。因此,可靠的TCP协议使用一种叫做慢启动的机制,该机制意味着我们通过首先发送少量数据来启动连接,只有当网络能够处理(没有发生数据包丢失)的时候,我们才会增大数据发送速率。实际上,在大部分linux服务器上,这个最初的拥塞窗口值(cwnd)大约为14kB。只有当浏览器确认成功收到这14kB的数据(通过发送ACK消息来确认)后,该值会倍增为28kB,然后我们可以发送同样大小的数据。在下一个ACK消息到达后,我们发送的数据可以增加到56kB,依此类推。

注:实际上,cwnd也能以其他方式增加,但其行为和上述过程类似,所以我们使用基于ACK的模型,因为这种模型便于我们思考。

TCP慢启动的效果

图2:TCP慢启动的效果(与图1中的理论性能对比)

这意味着在一个冷连接中,不管怎样我们在第一个RTT时间内只能发送14kB数据到浏览器:如果我们推送更多数据,它们将缓存在服务器上,直到浏览器发送ACK消息确认收到这14kB数据。这种情况下,推送可能没有额外好处:如果浏览器随ACK消息发出一个新的请求,那么会有相似的效果(需要2个RTT时间下载全部资源,见图2)。当然,在一个热的或者说重复使用的连接中,cwnd已经变得很大,那么你就能在1个RTT时间内推送更多数据。在这篇很棒的文档的第一章,可以看到关于这个问题的更深入探讨。

注意,这主要是因为HTTP/2只使用1个连接,而大部分HTTP/1.1版本使用6个平行的连接。有趣的是,有报告称增大初始cwnd对一般的HTTP/2来说并无太大帮助,但还不清楚这种说法是否也适用于h2推送。

1.2 优先权

HTTP/2只使用一个单独的TCP连接,在此连接中,它能够同时处理多个不同来源的数据。如果有多个资源等待传输,h2会运用优先级来决定哪个数据应该被最先发送。基本上,每个资源会有一个给定的发送顺序:例如.html是最重要的,所以它的优先级为1,.css和.js优先级分别为2和3,图片优先级为4。

如果我们有相同优先级的数据等待传输,那么它们会被交叉发送:服务器轮流发送每个数据的一段,见图3。这种交叉发送可能导致两个相同优先级的资源都被延迟,因为这种情况下需要的时间比单独下载一个资源时间更长。这种机制在处理渐进式的流媒体资源和经过解析的资源时能很好地工作,如渐进式的jpg或.html资源,但在处理那些需要全部下载完成后才能使用的资源(如果.css、.js、和字体文件)时可能效果会差一些。

HTTP/2优先级和交叉发送

图3:HTTP/2优先级和交叉发送

HTTP/2优先级比这些例子复杂得多,需要将优先级排列成树形结构,还允许子树之间的互相依赖关系和权重,来帮助决定怎样分配带宽(以及怎样交叉发送)。很多有趣的帖子对此进行了探讨。

1.3 缓冲区

网络在各个层次上都使用很多缓冲区(路由器、手机信号发射塔、操作系统核心等)来处理突然增多的传入数据,这些数据无法立即传输。大部分情况下,一旦数据处于其中一个缓冲区中,就不能再把它取出或重新安置了。这将导致HTTP/2优先级无法正确使用。举个例子,假如我们人为地在.html之后推送3个大图片,那么这些图片将装满缓冲区。然后浏览器对.html进行解析,发现一个重要的.css文件并请求它。服务器想要在图片的剩余部分之前发送这个文件(因为它阻塞了页面呈现),但却无法做到,因为不能将数据存入缓冲区。唯一的选择就是在一些图片数据之后发送.css,这大大延迟了页面加载时间,见图4。

HTTP/2缓冲区

图4:大缓冲区可能引起关键数据的延迟,服务器只能重新分配用户空间缓冲区的优先级

一个可能的解决方案是,尽可能限制核心缓冲区的使用,只有当数据能够被立即发送的时候才向核心缓冲区存入数据,Kazuho Oku对此做了详细说明。但这只解决了一部分问题,因为在网络中可能会有缓冲区膨胀。如果网络允许服务器以很高速率发送数据,只对网络路径上大的内部缓冲区中的数据进行阻塞,那么我们会看到同样有害的影响。这对于热连接成了一个大问题,可能有更多的数据同时停在路途中。谷歌的工程师们进一步探讨了这个问题。有趣的是,他们认为h2推送功能仅以正确顺序推送关键资源对此问题有所帮助。这意味着知道确切顺序对于h2推送发挥最大作用是很重要的(在.html之后直接推送图片很可能是个坏主意)。

1.4 缓存

现代浏览器(以及如CDN一类的网络中间件)使缓存的重度使用能够预先迅速加载已下载的资源,而不用向原始服务器发出一个新的请求。

当然这意味着我们不想推送那些已缓存的资源,因为这会浪费带宽并可能延迟其他尚未缓存的资源。问题在于要知道哪个资源已被缓存;理想状态下,浏览器在请求index.html时会先发送一个缓存概要,但这个概念并未被正式版的HTTP/2采用,而且现在的任何一种浏览器都没有这么做。相反,正式的方法是浏览器会发送一个RST_STREAM回答PUSH_PROMISE,以取消推送。但实际上,某些浏览器也并不这么做,而是会很高兴地接受他们已缓存的资源的推送。即使这些浏览器使用RST_STREAM,我们也要怀疑这到底有多大效果:很多(或全部)推送数据在RST_STREAM到达服务器时可能还在途中或缓冲区中,服务器很可能返回大量空数据。

RST_STREAM

图5:RST_STREAM可以被用来取消h2推送

为研究这些缺陷,我们等待一个正式的浏览器版本,几种选择被提出来,见这篇很棒的文章的第三章。规范的概念是,让服务器设置一个cookie,详细说明推送了哪个资源,并在收到每个请求时检查这个cookie,看还保留哪些推送目标。这看起来在实际中会工作得很好,尽管当资源被从客户端的缓存中移除的情况下会失败。

相关资讯:

HTTP/2技术解密:如何处理线端阻塞HOLB

HTTP/2是HTTP协议的一个激动人心的修订版本。它提供的性能提升是毫无疑问的。但与此同时,它也带来了一个问题,那就是如何最好地使用这些来自现实世界部署中的新功能和经验,来为我们提供最好的指导。

HTTP2.0提速,让网站飞起来-HTTP2.0优势详解

HTTP/2.0(超文本传输协议第2版,最初命名为HTTP 2.0),是HTTP协议的的第二个主要版本,使用于万维网。HTTPS/2.0将比HTTP 1.1传输速度更快,由于强制使用https加密...

转载:HTTPS,SPDY,HTTP/2三者性能的简单对比

Firefox 35是第一个默认开启支持HTTP/2协议的浏览器。Google现在在其服务器上同时支持了HTTP/2第14版草案和SPDY协议,这就给了我们一个基于同一个网页...

HTTP/2性能更好,有必要升级到HTTP/2吗

根据W3Techs的调查数据显示,目前大约有11%的网站使用了新型的互联网通信协议–HTTP/2,而在一年之前,其占比只有2.3%。使用HTTP/2之后,网站性能平均...