Web 开发者的 HTTP/2 性能优化指南

8,581 阅读10分钟
原文链接: github.com

HTTP/2改变了Web开发者优化网站的方式。在HTTP/1.1中,为了压缩5%的页面加载速度,人们会通过雪碧图、内联代码、细分域名、合并代码等方式,来想方设法地优化TCP连接和HTTP请求。

HTTP/2带来了些许便利。一般网站无需复杂的构建和部署流程即可获得30%的性能提升。在这篇文章中,我们会讨论HTTP/2下网站优化的最佳实践。

HTTP/1.1中的Web优化

HTTP/1.1中大多数的网站性能优化技术都是减少向服务器发起的HTTP请求数。浏览器可以同时建立有限个TCP连接,而通过这些连接下载资源是一个线性的流程:一个资源的请求响应返回后,下一个请求才能发送。这被称为线头阻塞。

因此,Web开发者开始将尽可能多的资源塞进一个连接中,并寻找其他办法来避免浏览器出现线头阻塞。在HTTP/2中,这样的实践事实上会增加页面的加载时间。

HTTP/2下的Web优化新观念

HTTP/2的优化需要不同的思维方式。Web开发者应该专注于网站的缓存调优,而不是担心如何减少HTTP请求数。通用的法则是,传输轻量、细粒度的资源,以便独立缓存和并行传输。

HTTP/2 Multiplexing

这种转变的出现是因为HTTP/2的多路复用头部压缩特性。多路复用使得不同的请求共用一个TCP连接,允许多个资源并行下载,避免建立多个连接带来不必要的额外开销。它消除了HTTP/1.1中的线头阻塞问题。头部压缩进一步减少了多个HTTP请求的开销,因为每个请求开销都小于未压缩的等价HTTP/1.1请求。

HTTP/2还有两个改变会影响到你的Web优化:流优先级服务端推送。前者允许浏览器指定接受资源的顺序,后者允许服务端主动发送额外的资源。为了尽可能利用这些特性,Web开发者需要抛弃一些HTTP/1.1中形成直觉的最佳实践。

HTTP/2 Web优化最佳实践

停止合并文件

在HTTP/1.1中,Web开发者往往将整个网站的所有CSS都合并到一个文件。类似的,JavaScript也被压缩到了一个文件,图片被合并到了一张雪碧图上。合并CSS、JavaScript和图片极大地减少了HTTP的请求数,在HTTP/1.1中能获得显著的性能提升。

HTTP/1.1 file concatenation

但是,在HTTP/2中合并文件不再是一项最佳实践。虽然合并依然可以提高压缩率,但它带来了代价高昂的缓存失效。即使有一行CSS改变了,浏览器也会强制重新加载你 所有的 CSS声明。

另外,你的网站不是所有页面都使用了合并后的CSS或JavaScript文件中的全部声明或函数。被缓存之后倒没什么关系,但这意味着在用户第一次访问时这些不必要的字节被传输、处理、执行了。HTTP/1.1中请求的开销使得这种权衡是值得的,而在HTTP/2中这实际上减慢了页面的首次绘制。

HTTP/2 file concatenation

Web开发者应该更加专注于缓存策略优化,而不是压缩文件。将经常改动和不怎么改动的文件分离开来,就可以尽可能利用CDN或者用户浏览器缓存中已有的内容。

停止内联资源

内联资源是文件合并的一个特例。它指的是将CSS样式表、外部的JavaScript文件和图片直接嵌入HTML页面中。例如,如果你的网页如下所示:

<html>
  <head>
    <link rel="stylesheet" href="/style.css">
  </head>
  <body>
    <img src="logo.png">
    <script src="scripts.min.js"></script>
  </body>
</html>

你可以通过内联工具获得下面的代码:

<html>
  <head>
    <style>

      body {
        font-size: 18px;

        color: #999;
      }
    </style>
  </head>
  <body>
    <img src="data:image/png;base64,Rw0KGgoAAAANSUhEUgAAAEAABA...">
    <script>console.log('Hello, World!');</script>
  </body>
</html>

在极端情况下,这确实能够减少给定网页的HTTP请求数。但是,和文件合并一样,HTTP/2优化时你不应该内联文件。

内联意味着浏览器不能缓存单个的资源。如果你将所有页面使用的CSS声明嵌入了每一个HTML文件,这些文件每次都要从服务端获取。这导致用户在访问任何页面时都要传输额外的字节。

内联同样会破坏流优先级。如果你的CSS、JavaScript和图片嵌入在HTML中,你实际上将它们的优先级提升到了HTML内容相同的级别。这意味着浏览器无法按照偏好的顺序请求资源,潜在地增加了首次渲染时间。

与其内联资源,Web开发者应该用好HTTP/2的服务端推送功能。服务端推送使得你的Web服务器可以告诉客户端:“稍等,你刚请求的HTML页面过会渲染时会用到这些图像和CSS文件”。理论上这和内联资源效果相同,但它不会破坏流优先级,并允许你充分利用CDN和用户的本地浏览器缓存。

停止细分域名

细分域名是让浏览器建立更多TCP连接的通常手段。浏览器限制了单个服务器的连接数量,但是通过将网站上的资源切分到几个域上,你可以获得额外的TCP连接。它避免了线头阻塞,但也带来了显著的代价。

Splitting website resources across multiple domains

细分域名在HTTP/2中应该避免。每个细分的域名都会带来额外的DNS查询、TCP连接和TLS握手(假设服务器使用不同的TLS证书)。在HTTP/1.1中,这个开销通过资源的并行下载得到了补偿。但在HTTP/2中就不是这样了:多路复用使得多个资源可以在一个连接中并行下载。同时,类似于资源内联,域名细分破坏了HTTP/2的流优先级,因为浏览器不能跨域比较优先级。

如果你目前使用了域名细分但希望利用HTTP/2,你不必重构你的整个代码库。在我们博客上的一篇文章域名细分和SPDY混用中提到,浏览器能够识别使用同一个TLS证书的多个服务器。一旦你这样做了,浏览器会重用多个服务器之间的SPDY或HTTP/2请求。这仍然会导致多个DNS查询,但如果你同时想在HTTP/1.1和HTTP/2下获得最佳性能,这算是一个优雅的妥协之举。

一些最佳实践依然有效

幸运的是,HTTP/2没有改变所有的Web优化方式。一些HTTP/1.1中的最佳实践在HTTP/2中依然有效。剩下的文章讨论了这些技巧,无论你在HTTP/1.1还是HTTP/2优化都能用上。

减少DNS查询时间

在浏览器可以请求网站资源之前,它需要通过域名系统(DNS)获得你的服务端IP地址。直到DNS响应前,用户看到的都是白屏。HTTP/2优化了Web浏览器和服务器之间的通信方式,但它不会影响域名系统的性能。

因为DNS查询的开销可能会很昂贵,尤其是当你从根名字服务器开始查询时,最小化网站使用的DNS查询数仍然是一个明智之举。使用HTML头部的<link rel='dns-prefetch' href='...' />可以帮助你提前获取DNS记录,但这不是万能的解决方案。

使用CDN

光在地球表面绕行一圈需要大约130毫秒。这个延迟你 无法 避免——物理使然。光缆和无线连接的不完善之处,以及全球因特网的拓扑结构,使得你电脑上的一个网络包至少要300-400毫秒才能传输到半个世界外的服务器。用户可以觉察到100毫秒的延迟,唯一克服物理规律的办法就是将你的Web资源通过CDN放在地理上更靠近来访者的服务器节点上。

利用浏览器缓存

你可以进一步利用内容分发网络,将资源存储在用户的本地浏览器缓存中,除了产生一个304 Not Modified响应之外,这避免了任何形式的数据在网络上传输。

最小化HTTP请求大小

尽管HTTP/2的请求使用了多路复用技术,在线缆上传输数据仍然需要时间。同时,减少需要传输的数据规模同样会带来好处。在请求端,这意味着尽可能多地最小化cookie、URL和查询字符串的大小。

最小化HTTP响应大小

当然了,另一端也是这样。作为Web开发者,你会希望服务端的响应尽可能的小。你可以最小化HTML、CSS和JavaScript文件,优化图像,并通过gzip压缩资源。

减少不必要的重定向

HTTP 301和302重定向在迁移到新平台或者重新设计网站时难以避免,但如有可能应该被去除。重定向会导致一圈额外的浏览器到服务端往返,这会增加不必要的延迟。 你应该特别留意重定向链,上面需要多个重定向才能到达目的地址。

像301和302这样的服务端重定向虽不理想,但也不是世界上最糟的事情。它们可以在本地被缓存,所以浏览器可以识别重定向URL,并且避免不必要的往返。元标签中的刷新(如<meta http-equiv="refresh"...)在另一方面开销更大,因为它们无法被缓存,而且在特定浏览器中存在性能问题。

结论和警示

以上寥寥数语介绍了HTTP/2的Web优化。防止文件合并、资源内联和域名细分不仅能够加速网站,还使得构建和部署流程更加简单。

不过,还有一些需要警醒的地方。大多数的服务器、内容分发网络(包括CloudFlare)和现有的应用还不支持服务端推动。服务器和CDN会迅速跟进,但为了让你的应用享受到服务端推送的好处,你还需要对代码库进行一些修改。

同时记住,HTTP/2的性能提升取决于你服务的内容。比如,依赖于外部资源的网站相比于那些HTTP请求更少的网站,会从HTTP/2的多路复用获得更大的性能提升。