原文地址:https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/
我们很高兴地宣布,2018年2月20日发布的NGINX 1.13.9支持HTTP / 2服务器推送。对于NGINX Plus用户,即将发布的NGINX Plus R15版本将包含HTTP / 2服务器推送支持,计划于2018年4月发布。
HTTP / 2规范中定义的服务器推送允许服务器抢先将资源推送到远程客户端,预计客户端可能很快会请求这些资源。通过这样做,您可以在页面加载操作中将RTT(往返时间 - 请求和响应所需的时间)减少一个RTT或更多,从而为用户提供更快的响应。
服务器推送可用于为客户提供样式表,图像以及呈现网页所需的其他资源。您应该注意只推送所需的资源; 不要推送客户端可能已经缓存的资源。
在这篇博文中,我描述了:
- HTTP / 2服务器推送的基本配置
- 如何验证HTTP / 2服务器推送是否正常(使用浏览器工具或
nghttp
) - 使用
Link
标题自动推送内容 - 有选择地推送内容
- 衡量HTTP / 2服务器推送的效果
配置HTTP / 2服务器推送
要将资源与页面加载一起推送,请http2_push
按如下所示使用该指令:
server {
# Ensure that HTTP/2 is enabled for the server
listen 443 ssl http2;
ssl_certificate ssl/certificate.pem;
ssl_certificate_key ssl/key.pem;
root /var/www/html;
# whenever a client requests demo.html, also push
# /style.css, /image1.jpg and /image2.jpg
location = /demo.html {
http2_push /style.css;
http2_push /image1.jpg;
http2_push /image2.jpg;
}
}
验证HTTP / 2服务器推送
您可以使用以下两种方法之一轻松验证服务器推送是否有效:
- Web浏览器中的开发人员工具
- 一个命令行的HTTP / 2客户端如
nghttp
使用开发人员工具进行验证(Google Chrome)
以下以Google Chrome为例,说明如何使用Web浏览器中的开发人员工具来验证服务器推送是否有效。在该图中,Chrome开发人员工具的“ 网络”标签上的“ 启动器”列指出,作为/demo.html请求的一部分,有多个资源被推送到客户端。
启动器列表示服务器推送用于发送资源
使用命令行客户端进行验证(nghttp
)
除了Web浏览器工具,您还可以使用nghttp2.org项目中的nghttp
命令行客户端来验证服务器推送是否有效。您可以从GitHub下载命令行客户端,或在可用的位置安装相应的操作系统软件包。对于Ubuntu,请使用该软件包。nghttp
nghttp2-client
在输出中,星号(*)表示服务器推送的资源。
$ nghttp -ans https://example.com/demo.html id responseEnd requestStart process code size request path 13 +84.25ms +136us 84.11ms 200 492 /demo.html 2 +84.33ms * +84.09ms 246us 200 266 /style.css 4 +261.94ms * +84.12ms 177.83ms 200 40K /image2.jpg 6 +685.95ms * +84.12ms 601.82ms 200 173K /image1.jpg
自动将资源推送给客户
在很多情况下,列出您希望推送到NGINX配置文件中的资源是不方便的,甚至是不可能的。出于这个原因,NGINX也支持拦截Link
预加载头的约定,然后推送这些头中标识的资源。要启用预加载,请http2_push_preload
在配置中包含指令:
server { # Ensure that HTTP/2 is enabled for the server listen 443 ssl http2; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; # Intercept Link header and initiate requested Pushes location = /myapp { proxy_pass http://upstream; http2_push_preload on; } }
例如,当NGINX作为代理服务器(用于HTTP,FastCGI或其他流量类型)运行时,上游服务器可以Link
为其响应添加如下标题:
Link: </style.css>; as=style; rel=preload
NGINX拦截这个头并开始服务器推送/style.css。Link
标题中的路径必须是绝对路径 - 不支持像./style.css这样的相对路径。该路径可以选择包含查询字符串。
要推送多个对象,可以提供多个Link
标题,或者更好的是,将所有对象包含在逗号分隔的列表中:
Link: </style.css>; as=style; rel=preload, </favicon.ico>; as=image; rel=preload
如果你不想让NGINX推送一个预先加载的资源,请nopush
在头文件中添加一个参数:
# Resource is not pushed Link: </nginx.png>; as=image; rel=preload; nopush
当http2_push_preload
启用时,您还可以通过在NGINX配置中设置响应标题来启动预加载服务器推送:
add_header Link "</style.css>; as=style; rel=preload";
有选择地向客户推送资源
HTTP / 2规范没有解决确定是否推送资源的挑战。显然,如果你知道他们可能需要这些资源并且不太可能已经缓存了资源,那么最好只将资源推送给客户端。
一种可能的方法是仅在首次访问该网站时将资源推送给客户。例如,您可以测试会话cookie的存在情况,并Link
有条件地设置标题,以便仅在会话cookie不存在时才预加载资源。
假设客户端运行良好,并在随后的请求中包含cookie,使用以下配置,NGINX每次浏览器会话仅向客户端推送一次资源:
server { listen 443 ssl http2 default_server; ssl_certificate ssl/certificate.pem; ssl_certificate_key ssl/key.pem; root /var/www/html; http2_push_preload on; location = /demo.html { add_header Set-Cookie "session=1"; add_header Link $resources; } } map $http_cookie $resources { "~*session=1" ""; default "</style.css>; as=style; rel=preload, </image1.jpg>; as=image; rel=preload, </image2.jpg>; as=style; rel=preload"; }
测量HTTP / 2服务器推送的效果
为了衡量服务器推送的效果,我们创建了一个简单的测试页面/demo.html,它引用了一个单独的样式表/style.css。样式表进一步引用两个图像。我们使用三种不同的配置测试了页面加载时间:
- 连续
GET
小号(无优化) -浏览器加载的资源时,才发现他们被要求 - 预加载提示 - 预加载提示(
Link
标题)包含在第一个响应中以告诉浏览器加载依赖关系 - 服务器推送(仅限HTTP / 2) - 依赖性被先发送到浏览器
测试了三种配置,以测量服务器推送时HTTP / 2的影响
我们使用HTTP,HTTPS或HTTP / 2对每个配置进行了多次测试运行。前两种配置适用于所有三种协议,并且服务器仅推送到HTTP / 2。
该行为是使用Chrome开发人员工具测量的。评估每个配置的最常见行为并对其进行平均,并将时间与链路的RTT(如使用所测量的ping
)相关联以说明每种方法的机械效应。
显示许多往返行程的测试结果是在每种配置下发生的
一些基本观察
- 已加载DOM是启动新连接并检索demo.html页面的时间。样式表是检索CSS资源的时间。
- 通过HTTP建立连接需要1个RTT; 对于HTTPS和HTTP / 2,它需要2个RTT。
- HTML和CSS资源的有效负载小于最大传输单元(MTU)大小,因此
GET
操作在大约1个RTT中完成。
解读结果:预加载提示
- 预加载提示对CSS资源的影响很小,因为它是在HTML资源中直接引用的,并且HTML资源可以快速传递。浏览器在HTML页面交付后立即启动CSS请求。
- 预加载提示确实可以快速开始下载在CSS资源(这里是两个图像)中声明的资源。使用预加载提示时,下载可以更快地启动1 RTT。
- 预加载提示的净节省可以是1 RTT或更多。在并行下载资源时,浏览器必须打开一个或多个其他连接。性能取决于缓慢的请求(较大的响应)是先排定还是延迟,而新的连接是打开的。这种不可预测的请求顺序考虑了HTTP和HTTP / 2的1-RTT加速以及HTTPS的2-RTT加速。
解释结果:服务器推送
- 服务器推进通过进一步的1 RTT改进了预加载提示时间。推送“响应”是在响应第一个请求的同时发起的,而预加载提示响应发生了1个RTT延迟 - 0.5个RTT用于响应第一个请求,另加0.5个RTT用于预加载
GET
请求。
测试笔记
- 每个配置都有多个测试运行。每次运行都从一个空的浏览器缓存开始,并且没有建立与NGINX服务器的保持连接。NGINX指令
keepalive_timeout
并http2_idle_timeout
用于快速关闭保持连接。 - 目前似乎无法将字体资源推送到Chrome,可能是由于已知的复杂情况。Chrome甚至已经推送了一个字体资源的明确请求。
- 注意在每次测试之前明确清除浏览器缓存,并且所有内容都提供了过期的缓存控制标头。
- Chrome缓存预先加载的资源。当您禁用缓存时,这些缓存的资源不会始终被忽略,并且不会通过明确的“清除浏览器缓存”操作得到一致的清除。(在Chrome中,禁用缓存的一种方法是在开发工具网络选项卡上选中禁用缓存复选框。要清除浏览器缓存,可以在打开开发工具并右键单击浏览器刷新按钮并选择清空缓存和硬重新加载) 。
- 一些尝试预加载内容导致Chrome无法成功重新验证缓存副本,然后正常下载资源。这些尝试不计入测量中。
- Chrome为所有使用先前接受的自签名证书的新SSL连接添加了不必要的2-RTT延迟。该测试使用CA签名的Let's Encrypt证书进行,以避免此2-RTT延迟。
- DNS延迟通过编辑本地/ etc / hosts文件解决。
- 这个简单的测试并不试图测量客户端已经拥有资源缓存副本的服务器推送效果。在这种情况下,在每次测试之前,所有高速缓存都已被清除,而且大多数推送都很快取消。
结论
为了突出预加载提示和服务器推送的机制,此测试非常简单。与简单情况下的预装载提示相比,服务器推送改进了1-RTT,与未优化的顺序GET
请求和从属资源的发现相比,服务器推送有了更大的改进。
更实际的用例有更多的变量:多个依赖资源,多个资源,甚至通过推送已经缓存或不需要立即需要的资源来浪费带宽。浏览器不一致还会影响性能。从这个简单的测试你的里程将会有所不同。
例如,Chrome团队已经发布了关于何时部署服务器推送的一些详细建议,并且已经在更复杂的网站上进行了测量,以比较未优化,预加载提示和服务器推送HTTP / 2的影响。对于考虑在生产中部署HTTP / 2服务器推送的人来说,他们的HTTP / 2推送报告的经验法则值得一读。
务实的结论是,如果您可以预先确定需要哪些资源,那么让上游服务器发送预加载提示会带来真正的好处。推动这些资源的额外好处虽然很小但是可以衡量,但可能会导致浪费带宽和延迟所需资源。您应该仔细测试和监控任何服务器推送配置。
附录:HTTP / 2推送如何工作?
以下信息部分基于Jake Archibald的非常详细的HTTP / 2推送研究,比我认为的博客文章更加艰难。
HTTP / 2服务器推送通常用于在客户端请求资源时抢先发送从属资源。例如,如果客户端请求网页,则服务器可能会将相关样式表,字体和图像推送到客户端。
当客户端进行HTTP / 2连接时,服务器可以选择使用连接发起一个或多个推送响应。这些推送发送客户端未明确请求的资源。
客户端可以拒绝推送(通过发送RST_STREAM
帧)或接受推送。客户端将推送的内容存储在与HTTP / 2连接关联的本地“推送缓存”中。
稍后,当客户端使用已建立的HTTP / 2连接请求资源时,它将检查连接的推送缓存,以查看是否已完成或正在传输对请求的响应。它首先使用缓存资源来为资源发出新的HTTP / 2请求。
任何推送的资源都会保留在每个连接推送缓存中,直到(a)它被使用或者(b)HTTP / 2连接关闭:
- 如果使用资源,客户端将获取副本并删除推送缓存中的条目。如果资源可缓存,则客户端可以将其副本缓存在其HTTP页面缓存中。
- 如果因任何原因关闭了HTTP / 2连接,则会删除其本地推送缓存。
这有几个含义:
- 即使推送的内容是新鲜的,浏览器中的HTTP页面缓存中的内容也优先于推送缓存中的内容。
- HTTP / 2连接可以在不同的页面加载中共享。由于一次页面加载而推送的资源可以在不同页面加载请求时使用。
- 具有凭证的请求使用不具有凭证的请求的不同HTTP / 2连接; 例如,如果浏览器为资源发出非凭证请求,则可能无法找到使用跨源请求推送的资源(凭证)。
您可以在Jake Archibald的HTTP / 2推送比我想到的博客文章更加强硬的问题中查看更详细的问题列表。
HTTP / 2服务器推送是一个有趣的功能。确保彻底测试HTTP / 2服务器推送配置,并准备在出现更可预测的缓存感知行为的情况下回退到预加载。