HTTP/2 服务器推送 - 综合指南

yufei       6 年, 4 月 前       1544

在过去两年左右的时间里,对于性能意识比较强烈的开发人员的来说应该是发生了重大变化,HTTP/2 的出现可能是最重要的。

HTTP/2 不再是我们所梦想的功能。它已经到了,随之而来的是服务器推送!除了解决常见的 HTTP/1 性能问题 ( 例如,行头阻塞和未压缩的头部 ) 之外,HTTP/2 还为我们提供服务器推送。服务器推送允许我们在用户提出要求之前将其发送给用户,这是实现 HTTP/1 优化实践 ( 如内联 ) 的性能优势的一种优雅方式,但没有实践带来的缺点

什么是服务器推送,确定吗 ?

在 HTTP/2 服务器推送出现之前,访问网站始终遵循请求和响应模式。用户向远程服务器发送请求,并且在一定延迟的情况下,服务器以所请求的内容进行响应。

对 Web 服务器的初始请求通常是 HTML 文档。在这种情况下,服务器回复所请求的 HTML 资源。获取到了响应结果后,浏览器会解析 HTML,可能会发现需要引用其它的资源文件,例如样式表,脚本和图像。一旦发现,浏览器就会对这些资源发出单独的请求,然后获得对应的响应

这种典型的 Web 服务器通信流程图如下

这种机制的问题在于它迫使用户等待浏览器下载完整的 HTML 文档后才开始发现和检索关键资源。这会延迟渲染并增加加载时间

通过服务器推送,我们可以解决此问题。服务器推送允许服务器抢先 「 推送 」 网站资源到客户端,而无需用户明确要求它们,这种使用方式,我们就必须知道发送给用户的页面所需要的哪些资源,以提前发送给它们。

假如我们有一个网站,所有的页面都依赖于一个外部样式 forum.css,当用户从服务器请求 index.html 时,我们可以在发送 index.html 的响应后立即将 forum.css 推送给用户

这种资源的推送方式,流程图如下

比起等待服务器发送 index.html 然后继续等待浏览器请求和接收 fourm.css ,这种方式只需要等待服务器在初始请求中同时响应 index.htmlforum.css

可以想象,这可以减少页面的渲染时间。它还解决了一些其他问题,特别是在前端开发工作流程中的那些问题。

服务器端推送解决了什么问题 ?

虽然,「 减少多次往返服务器获取关键内容 」 是服务器推送解决的问题之一,但这并不是它唯一解决的问题。服务器推送可作为许多 HTTP/1 的特定优化方案的替代方案,例如将 CSSJavaScript 直接内联到HTML 中,以及使用 DATA URI 方案将二进制数据嵌入到 CSS 和 HTML 中

这些技术我们都曾经用在 HTTP/1 优化中,因为它们减少了加载页面时的「 感知渲染时间 」,也就是说,虽然页面的整体加载时间可能不会减少,但页面的加载速度似乎更快。毕竟,这是有道理的。如果将 CSS 内联到 <style> 标记内的 HTML 文档中,浏览器可以立即开始将样式应用于 HTML,而无需等待从外部源获取它们

这个概念适用于内联脚本和使用 Data URI 方案内联二进制数据,这种内联其它资源的流程一般如下

似乎是解决问题的好方法,对吧? 当然 - 对于 HTTP/1 工作流程,我们别无选择。然而,当我们这样做时,我们几乎也是饮鸩止渴,吞下的毒药是内联内容无法有效缓存。

将样式表或 JavaScript 文件之类的资源保持外部和模块化时,可以更有效地缓存它。当用户导航到需要该资源的后续页面时,可以从缓存中提取该页面,从而无需向服务器发送其他请求。

这种可选的缓存行为工作流程如下图所示

但是,当我们内联内容时,该内容没有自己的缓存上下文。它的缓存上下文与它内联的资源相同。例如,使用带内联 CSS 的 HTML 文档。如果 HTML 文档的缓存策略是始终从服务器获取标记的新副本,那么内联的 CSS 将永远不会自行缓存。当然了,它的一部分文档可能会被缓存,但包含这个重复 CSS 的后续页面将被重复下载。即使缓存策略更加宽松,HTML 文档通常也具有有限的保质期。不过,这是我们愿意在 HTTP/1 优化工作流程中做出的权衡。它确实有效,对初次访问者来说非常有效。第一印象往往是最重要的。

这些是服务器推送地址的问题。推送资源时,我们可以获得内联带来的实际好处,但我们也可以将资源保留自己的外部文件中,且拥有自己的缓存策略。对于这种方式,我们会在文章的结尾介绍它,但现在,我们继续

我们已经充分讨论了为什么应该考虑使用服务器推送,以及它为用户和开发人员修复的问题。现在让我们谈谈它是如何使用的

如何使用服务器端推送

使用服务器推送通常涉及使用 Link HTTP 响应头部字段,该响应头部字段采用的格式如下

Link: </forum.css>; rel=preload; as=style

请注意,通常说。你在上面看到的实际上是预加载资源提示,这是一种与服务器推送的相互独立且不同的优化,但大多数( 并非所有 ) HTTP/2 实现将推送包含预 preload 资源提示的 Link 响应头部中指定的资源。如果服务器或客户端选择不接受推送的资源,则客户端仍然可以单独对只是资源的提前获取

请求头中的 as=style 部分是可选的。它仅用于通知浏览器推送的资源的内容类型。上面代码中,我们使用 style 来指示推送的资源是样式表,当然了,你可以指定其他内容类型

虽说 as=style 可以省略,但重要的是,如果省略 as 值可能导致浏览器两次下载推送的资源。所以最好不要忘记它。

现在,既然你已经知道了如何触发推送事件,那我们要如何设置 Link 响应头部?

已知的方法有两种:

  1. 配置 Web 服务器,例如 Nginx 的 nginx.conf 和 Apache 的 httpd.conf.htaccess
  2. 调用后端语言相关的函数,例如 PHP 中的 header() 函数

在 Web 服务器中设置 Link 响应头部

下面的代码是配置 Apache ( 通过 httpd.conf.htaccess ) 在请求 HTML 文件时推送样式表的示例

<FilesMatch "\.html$">
    Header set Link "</fourm.css>; rel=preload; as=style"
<FilesMatch>

在这里,我们使用 FilesMatch 指令来匹配以 .html 结尾的文件的请求。当符合此条件的请求出现时,我们向响应添加一个 Link 头,告诉服务器推送 /forum.css 资源

题外话:Apache 的 HTTP/2 模块还提供了 H2PushResource 指令启动资源推送。该指令的文档指出,与使用Link 头方法相比,此方法可以更早地启动推送。但对于你的 Apache 来说,该模块可能不存在,所以本文后面显示的性能测试使用 Link 头方法

据我所知, Nginx 也在 1.13.9 版本开始支持 HTTP/2 。相关的配置可以参考 HTTP/2 服务器推送 - 入门指南

在后端语言中设置 Link 头部

设置 Link 头的另一种方法是通过服务器端语言。当我们无法更改或覆盖 Web 服务器的配置时,当我们需要动态的设置 Link 时,非常有用。

下面的代码演示了如何使用 PHPheader() 函数来设置 Link 响应头

header("Link: </forum.css>; rel=preload; as=style");

如果你的应用程序驻留在共享托管环境中,无法修改服务器的配置,那么可以继续使用此方法。你可以使用任何服务器端语言来设置 Link 响应头。但请在开始发送响应主体之前发送它们,请务必这样做,以避免潜在的运行时错误

推送多个资源

到目前为止,我们所有的示例都只说明了如何推送一个资源,如果我们想推送多个要怎么办呢?

这样做会有意义,对吧?毕竟,网络不仅仅是样式表

下面这种方式可以推送多个资源

Link: </css/styles.css>; rel=preload; as=style, </js/scripts.js>; rel=preload; as=script, </img/logo.png>; rel=preload; as=image

当我们需要推送多个资源时,只需要使用逗号 ( , ) 分隔每个 push 指令即可。因为资源提示是通过 Link 标记添加的,所以这种语法是就是混合使用了其它资源和 push 指令

例如下面的代码将 push 指令与 preconnect 资源提示混合使用

Link: </forum.css>; rel=preload; as=style, <https://fonts.gstatic.com>; rel=preconnect

添加多个 Link 头也能达到相同的效果,例如下面的代码是关于如何配置 Apache 为 HTML 文档请求设置多个 Link

<FilesMatch "\.html$">
    Header add Link "</forum.css>; rel=preload; as=style"
    Header add Link "</scripts.js>; rel=preload; as=script"
</FilesMatch>

这种语法比将一串以逗号分隔的值串在一起更方便,并且它的工作原理相同。唯一的缺点是它不是那么紧凑,但这种多久了几个字符以换取便利性是值得的。

现在你已经知道如何推送资源,让我们看看如何判断它是否有效

目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.