Nginx 1.13.9:支持HTTP/2服务器端推送

Nginx 1.13.9:支持HTTP/2服务器端推送

我们很高兴地宣布,Nginx 1.13.9 于2018年2月20日正式发布,这个版本支持HTTP/2的服务器端推送。对于Nginx PLUS的用户,HTTP/2服务器端推送的支持将包含在即将到来的 Nginx PLUS R15版本,这个版本预计于四月份发布。

服务器端推送,是HTTP/2标准定义的功能。这个功能允许服务器预判客户端可能会用到的资源,并提前将资源推送给客户端。这样,我们就可以减少页面加载时间,为用户提供更流畅的体验。

服务器端推送可以为客户端预先准备样式表、图片等页面加载需要的文件。你需要仔细甄别哪些资源需要推送,尽量推送客户端确实需要请求的,避免推送客户端已经缓存的资源。

在这篇博文中,我们将描述:

  • HTTP/2服务器端推送的基本配置

  • 如何验证HTTP/2服务器端推送生效了(使用浏览器工具或者nghttp)

  • 使用响应头Link自动推送内容

  • 选择性推送内容

  • 衡量HTTP/2服务器推送的效果

  • 配置HTTP/2服务器端推送

    要在页面加载时推送资源,使用http2_push指令:

    Nginx 1.13.9:支持HTTP/2服务器端推送

    验证HTTP/2服务器端推送

    你可以用下面两种方法轻松地验证服务器端推送生效了:

  • 浏览器的开发者工具

  • HTTP/2的命令行工具,比如nghttp

  • 使用开发者工具验证(以Google Chrome为例)

    下面是如何使用浏览器的开发者工具验证服务器端推送是生效的,这里使用Chrome作为例子。在下图中,开发者工具中Network页中的Initiator列显示出我们通过服务器端推送,将几个资源伴随对/demo.html的请求一同返回给了客户端。

    Nginx 1.13.9:支持HTTP/2服务器端推送

    通过命令行工具验证(nghttp)

    除了浏览器的开发者工具,你还可以使用命令行工具nghttp来验证服务器端推送是有效的。你可以从github上下载并安装nghttp,也可以使用操作系统的软件包管理工具来安装(比如在Ubuntu中,使用apt-get安装nghttp2-client)。

    Nginx 1.13.9:支持HTTP/2服务器端推送

    图中的星号代表资源是由服务器端推送的。

    自动推送资源给客户端

    很多时候,把需要推送的资源列举在nginx配置中不是一个很好的做法。因此,Ngnix也支持解析Link预加载响应头,然后自动推送响应头中提到的资源。想要开启预加载,在配置中开启http2_push_preload指令。

    Nginx 1.13.9:支持HTTP/2服务器端推送

    比如,使用Nginx做请求代理(HTTP,FastCGI,或者其他请求类型)时,上游服务器可以在返回的响应头中增加一个Link字段。

    Nginx 1.13.9:支持HTTP/2服务器端推送

    Nginx解析这个头部字段内容,并自动推送资源/style.css。Link字段中的路径必须是绝对路径,可以带查询参数。

    想要推送多个资源,你可以在头部添加多个Link字段,或者只用一个Link,但用逗号分隔多个资源说明。

    Nginx 1.13.9:支持HTTP/2服务器端推送

    如果你不想让Nginx推送一个已经加载过的资源,向头部加一个nopush参数

    Nginx 1.13.9:支持HTTP/2服务器端推送

    当http2_push_preload已经开启,你也可以在nginx配置中添加Link响应头来推送资源。

    Nginx 1.13.9:支持HTTP/2服务器端推送

    选择性推送资源给客户端

    HTTP/2的标准中没有解决什么资源应该推送、什么资源不需推送的问题。不过很显然,推送的资源最好不是已经缓存过的内容,否则这次推送就没有意义了。

    一个可行的方案是,只在客户端第一次访问网站时推送资源。你可以在程序中检查是否有该客户端对应的session存在,只有在没有session时才推送,换言之,也就是选择性地推送资源。

    假设客户端设计良好,在首次之后的请求中都会携带session cookie,那么这个方案就能保证每一个资源只在第一次请求时被推送一次。

    Nginx 1.13.9:支持HTTP/2服务器端推送

    测试服务器端推送的效果

    为了衡量服务器端推送的效果,我们创建一个简单的页面/demo.html,它引用了一个独立的css文件/style.css,这个css又引用了两个图片,我们使用三种不同的配置测试页面加载的速度:HTTP, HTTPS, HTTP/2。

    Nginx 1.13.9:支持HTTP/2服务器端推送

    我们将进行多个测试:

  • HTTP使用多个GET请求请求资源,浏览器在发现需呀某个资源时再进行请求。

  • 有preload标识的HTTP在第一次请求返回后得到preload标识,开始让浏览器加载这些依赖资源。

  • HTTP/2的服务器端推送会预先将资源直接推送给浏览器。

  • 它们的行为是由Chrome的开发者工具衡量的。我们多次重复,取出每一种配置的典型性能,并根据RTT值(由ping得到)进行加权调整后得到下面的数据:

    Nginx 1.13.9:支持HTTP/2服务器端推送

    一些基本的观察

  • 建立HTTP链接需要一个RTT时间,建立HTTPS或者HTTP/2链接需要两个RTT时间。

  • 由于HTTP和CSS的资源体积小于一个MTU值,所以一个GET操作在一个RTT时间就可以完成。

  • 当DOM loaded事件发生时,才真正地发起一个请求,来请求需要的资源。

  • 解释结果:预加载

  • 服务器推送为预加载减少了一个RTT时间,推送资源是在第一个请求返回时同步返回的,而preload预加载资源则需要额外花一个RTT的时间,其中第一个请求返回需要0.5个RTT,preload使用的GET请求到达需要0.5个RTT时间。

  • 测试备注

  • 我们进行了多次测量。每次测试开始时浏览器都没有缓存,也没有到nginx的keepalived链接。我们为nginx配置了keepalive_timeout和http2_idle_timeout,以便快速关闭keepalived链接。

  • 无论使用preload标签还是服务器端推送,字体都无法有效地预加载,这是一个Chrome的已知问题。即便一个字体已经加载过了,Chrome还是会重复地请求字体资源。

  • 需要注意的是,测试开始前一定要清楚缓存,并在返回的响应中设置缓存过期时间控制头。

  • Chrome会缓存preload预加载的资源。即便你禁用缓存,这些缓存还是会生效,就算你明确地清除浏览器缓存,实际上缓存也不是每次都能被清除。Chrome有时也会不顾缓存地存在而去加载资源,这些加载请求在测试结果中我们已经剔除了。

  • 我们手动设置了/etc/hosts,所以测试过程中不受DNS延迟的影响。

  • 我们没有测试在已经有缓存的情况下的效果。我们测试过程中,每次测试都是清除了缓存的,服务器端推送进行得太快,根本没机会在请求中中断请求。

  • 结论

    这些测试都很简单,主要用来讲诉preload标签和服务器端推送的机制。在简单的情形下,服务器端推送相比于preload标签,可以带来一个RTT时间的优化,在很多未优化过的情境中,可能带来的优化效果更明显。

    真实使用场景中有更多的变量。有更多可能用到的资源,也有更大的机会,会因为你错误地使用服务器推送而浪费了带宽。浏览器间的不一致也会影响最终的效果,你需要做的工作肯定比这个例子复杂得多。

    比如,Chrome团队撰写了一个服务器端推送配置推荐细则,他们在复杂的网站上测试了各种优化配置的效果。如果你想部署HTTP/2的服务器端推送,那么他们的报告《Rules of Thumb for HTTP/2 Push》非常值得一读。

    最后实际的结论就是,HTTP/2服务器端推送让你可以预先制定哪些资源需要推送加载,相比于返回一个preload提示,服务器端推送有切实可见的性能提升。当然,错误的配置可能会导致你浪费带宽,所以上线前请认真评估和检查你的配置。

    英文原文:http://www.nginx.com/blog/nginx-1-13-9-http2-server-push/

    译者:诗书塞外