HTTP 的理想性能
提高 Web 性能的一个简明的目标是减少用户的等待时间,将页面尽可能快地呈递到用户面前。
对于 HTTP 来说,这就意味着一个理想的协议交互应该是这样的:
也就是说,一个页面的加载需要在最少的往返次数里上传和下载最小的数据量。
额外的数据传送不仅耗费了更多的时间,还可能导致拥塞的丢包,严重影响性能。
以这样的标准来看 HTTP 的话如何呢,我们又可以如何改进它?
HTTP/1.1
从许多方面来看,HTTP/1.1 都是一个优秀的协议,但是从现代 Web 的使用方式来看,HTTP/1.1的性能是明显不够的,一般网页加载过程大概是这样:
嗯…看起来并不是那么理想。
客户端需要请求服务器多次来获取 HTML 中的图片资源,先是 HTML,接着是 CSS 和 JavaScript。这个过程中的每一次数据交换,都使页面加载的完成多等待了一个往返的等待时间。有悖于我们理想中的 “最少的往返次数“ 。
还有,只是为了获取页面中的其他数据而去发送请求,也与我们的 “最少数据传送” 原则相违背。
因为冗长的请求头例如 Referer、User-Agent 和 Cookie 在重复的在每一次请求中被传送。
最后,因为 HTTP/1 中存在队头阻塞,在实际应用中常常把资源整合进 CSS spriting、使用内联 和 串联,这些都是非常巧妙的 HTTP/1 表现技巧,但这也是有代价的。这致使客户端下载了更多的数据,违背了我们 "最小下载量" 的理念,也意味着我们并没有做到绝对快地将网页呈现在用户面前。
总的来说,HTTP/1.1 并不是一无是处,在性能上还是有可取之处的。比如它的缓存机制,避免了客户端下载已经下载过的数据。还有条件请求,避免了在已有先前的数据的情况下重复下载大文件。
HTTP/2
HTTP/2 试图从这几个方面来解决问题:
- 多路传输意味着队头阻塞将不再是问题,你可以在一个单一的 HTTP 连接中加载整个网页,不需要去做多余的请求,数据浪费优化技术也可以放在一边了。
- 报头压缩解决了冗余的报头带来的资源浪费,现在你可以接受几十(甚至数百)个IP数据包请求,越来越接近“最小数据量”的目标了。
- HTTP/2 服务器推送允许服务器预测客户端将会需要的资源,省去了多余的客户端请求。
所以,一次 HTTP/2 的交互差不多是这样的:
你可以看到服务器自动的把 CSS、Javascript 和 图片资源发送给了客户端,不需要客户端去主动请求。它知道客户端将要去请求这些资源,所以利用服务器推送把资源发送给了客户端,减少了一次网络往返。
请注意,这并不是说,这都是很容易的;还有很多关于 HTTP/2 公认的问题,特别是在推送的时机上。我接下来会单独讨论。
HTTP/2 + 缓存摘要
关于服务器推送的一个常见问题是:“如果客户端已经有缓存了,该怎么办?” 因为推送总是默认的,服务器还是有可能发送客户端不需要的东西。
HTTP/2 利用 RESET_STREAM 赋予了客户端取消推送的能力来解决这个问题。尽管如此,还是有一个网络往返被浪费掉了。记住,我们的理想是只发送客户端需要的数据。
一个解决方案是利用一个缓存摘要来让服务器知道客户端已经存在的资源。
因为缓存摘要利用了 Golumb 压缩集,所以客户端只需要利用少于一千个字节就能告诉服务器自己已有的资源。
这样,我们就避免了额外的往返、数据并联和串联的浪费,更贴近我们的目标了。
目前,缓存摘要只是一个提议,但在 HTTP 社区关注度非常高,希望在不远的将来能看到它在实际中运用。
TCP
截至目前,我还没有谈到浏览器使用的其它协议对性能产生的影响。
然而,许多信息以上的图表中都没有表示出来。TCP 在新建一个连接之前需要进行三次握手。
这意味着只需要更少的网络往返就可以建立连接,但却在每一次连接中增加了一些数据。
TCP 快速连接 允许应用程序发送 SYN 和 SYN+ACK 数据包来避免这个问题,但目前只支持 Linux 和 OS X。此外,TFO 和 HTTP 的使用也出现了一些棘手的问题,但社区已经在着手解决了。
TFO 无法保证在 SYN 上发送的数据只出现一次,很容易被复制甚至恶意攻击。所以,HTTP POST 并不是一个很好的方式来进行第一次 TFO 请求。更让人头疼的是,一些 GET 也存在问题,浏览器不知道如何去侦测哪一个 URL 是用来发送这个的。
TLS
在 TCP 握手完成之后,TLS 又开始了另一次握手:
在 HTTP 可以进行数据传送之前 TLS 先要进行两次往返。如果这不是第一次访问服务器,session tickets 能避免其中的一次往返。
不久以后,在不是第一次访问的情况下,TLS 1.3 将会支持 “0 次往返”。也就是说,HTTP 将能在第一次往返中就进行数据的传送,避免了一些延迟。然而,像 TCP 快速连接那样,你需要保证第一次往返中发送的数据不出问题。
下一代 HTTP
TCP 快速打开和 TLS 1.3 都能减少新建连接的通信量。另一个方法是尽可能的利用好已经打开了的连接。
为此,人们正积极讨论如何能让连接聚集更加的有效。这不仅能减少新建连接的开销,更让已经存在的连接更加的高效,使 TCP 保持持久和繁忙。
其中包含了将证书推送到客户端,以使该连接可以被更多终端使用。
一些更激进的方案也在讨论中,比如用 UDP 替换 TCP,QUIC。过度到 QUIC 还有很长的路要走。但从性能上来考虑,能不改变客户端的操作系统的情况下在一次往返之内完成握手是非常吸引人的。此外,能在缓冲中获取到数据意味着 TCP 中的队头阻塞将不再是问题。在发送少量数据之后就可以将页面传送到用户面前。
距离 QUIC 的实现还远,所以我们短时间内不会见到基于 UDP 的 HTTP 标准。但是我们可以从 QUIC 中来认识我们想要 TCP 如何更加高效,使 Web 的性能突飞猛进。错综复杂的互联网将会如何发展,让我们拭目以待。
原文链接:Ideal HTTP Performance