入门
HTTP/2 旨在解决许多 HTTP/1.x 的缺点。现在的网页使用很多 HTML、CSS、JavaScript、图片等资源,在 HTTP/1.x 中,每个资源必须被显式请求。这会是一个漫长的过程。浏览器先获取 HTML,然后一边解析页面,一边获取更多资源。服务器必须等着浏览器发起每次请求,这就导致网络会经常空闲、未充分利用起来。
为了改善延迟,HTTP/2 引入了 Server Push
。Server Push
允许资源在明确被请求之前,由服务器推送这些资源给浏览器。服务器通常知道需要加载哪些额外资源,因此可以在响应初始请求时一起推送这些资源。这让服务器能充分利用剩余空闲带宽来改善页面加载时间。
在协议层,HTTP/2 Server Push
由 PUSH_PROMISE
帧发起。 一个 PUSH_PROMISE
表明了服务器预测浏览器将会发起的请求。浏览器一接收了该 PUSH_PROMISE
,就会知道服务器将要分发资源。若浏览器之后发现需要这个资源,它就会等着该 Push
完成,而不是发送一个新请求。这就减少了浏览器在网络上花的时间。
net/http 包中的 Server Push
Go 1.8 从 http.Server
中引入了对 Server Push
响应的支持。如果运行的是 HTTP/2 的服务器,并且进来的连接使用了 HTTP/2 ,这个特性就能用了。在每个 HTTP 的处理程序中,你都可以通过检查 http.ResponseWriter
是否实现了 http.Pusher
接口,来判断服务器是否支持 Server Push
。
例如,如果服务器知道渲染页面时需要 app.js
,处理程序可以在 http.Pusher
可用时这样初始化一个 Push
:
1 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
该 Push
会为 app.js
创建了一个合成请求,把 app.js
合并到 Push_Promise
中,然后将合成请求转发给服务器处理程序,服务器处理程序会生成响应。Push
的第二个参数指定了包含在 Push_PROMISE
中的额外 header。 例如,如果 app.js
的响应有 Accept-Encoding
上的不同,那 PUSH_PROMISE
应该包括 Access-Encoding
的值。
1 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
完整示例见:
1 | $ go get golang.org/x/blog/content/h2push/server |
运行服务器,加载 https://localhost:8080,就能在浏览器开发者工具上看到服务器 推送了 app.js
和 style.css
。
在响应之前 Push
在发送响应之前调用 Push
方法是个好主意,否则可能会意外产生重复响应。例如,假设写入了部分响应:
1 | <html> |
然后调用 Push("a.css", nil)
,浏览器可能会在接收到 PUSH_PROMISE
之前开始解析 HTML。这会导致除了接收到 PUSH_PROMISE
,还会再发起一个 a.css
请求,服务器会生成 2 次 a.css
。 在写入响应之前调用 Push
完全避免掉了这种可能。
什么时候使用 Server Push
?
考虑在网络空闲的情况下使用 Server Push
。刚给你的 web 应用发送完 HTML?别等了,开始使用 Server Push
推送客户端需要的资源吧。还在使用内联资源(inline-style-sheet)来减小延迟吗?别用了,换成 Server Push
吧。重定向是另一个使用 Server Push
的好时机,因为客户端这时候正在往返请求上浪费时间。还有很多可以使用 Server Push
的情形,我们仅仅是做个入门。
要是一下几点不提一下的话是我们的失职。
只能推送你服务器有权推送的资源,第三方服务器或者 CDN 的资源无法推送。
除非确信客户端需要这些资源,否则别推送,不然的话会浪费带宽。要是浏览器已经缓存了这些资源的话,必须要避免重复推送。
天真地推送所有资源给页面会让性能更糟,在不能确定的情况下,要慎重。
以下资源做了不错的额外补充:
- HTTP/2 Push: The Details
- Innovating with HTTP/2 Server Push
- Cache-Aware Server Push in H2O
- The PRPL Pattern
- Rules of Thumb for HTTP/2 Push
- Server Push in the HTTP/2 spec
尾声
在 Go 1.8 中,标准库为 HTTP/2 Server Push
提供了一个开箱即用的支持,为优化你的网页应用提供了更多灵活性。
打开 HTTP/2 Server Push demo 看一下 Server Push
的实际效果。
参考资料 & 说明
[ 1 ] 本文翻译自 Go Blog 的 HTTP/2 Server Push。
[ 2 ] 本文在翻译过程中参考了 在 Go 中使用 HTTP/2 Server Push。该文章源地址在 在 Go 中使用 HTTP/2 Server Push。
[ 3 ] 本文在翻译过程中进行了小部分的演绎,请知悉。由于本人水平有限,如有错讹,可以在留言区指出,不胜感激。