“html
互联网工程任务组(IETF)于2014年12月将HTTP/2标准提议递交至互联网工程指导组(IESG)进行讨论,于2015年2月17日被批准,于2015年5月以RFC 7540正式发表。-维基百科前端
回过头来看,距离HTTP/2标准发布已通过去了五年。这五年,前端发生了技术翻天覆地的变化: IE浏览器逐渐式微,MVVM框架的兴起和繁盛,WebAssembly的萌芽。nginx
这五年,HTTP/2 的普及和推开也无比迅速。面试
根据最新的数据,HTTP/2的浏览器支持程度已经来到了使人振奋的96%。浏览器
“缓存
HTTP/2 相较于 HTTP1.x 有哪些改进?性能优化
上面这个问题已经成为前端面试的必考题。二进制分帧、多路复用、头部压缩、服务端推送这几个关键词你们已经熟的不能再熟。服务器
HTTP/2带来了显而易见的性能提高,又有着如此高的浏览器支持度,和极低的使用成本,不用简直天理难容。目前中国各大网站都使用 HTTP/2 提高网站加载性能和用户体验。markdown
但咱们再看HTTP/2的几大特性,二进制分帧、多路复用、头部压缩这几个自没必要多说,只要开启了HTTP/2,就可使用这些特性,给网站带来性能优化。那么,Server Push去哪了?cookie
咱们找遍了中国各大门户网站,都没有看到Server Push的身影。问题出在哪?
简单回顾一下Server Push的概念:
“
服务器推送(server push)指的是,尚未收到浏览器的请求,服务器就把各类资源推送给浏览器。
上面这张图片看起来很美妙,当使用Server Push时,CSS和HTML一块儿返回,从而减小了一个RTT的时间。
让咱们试着解决第一个问题:
为何Server Push没有获得普遍的应用?
“
ServerPush是HTTP/2 协议里面惟一一个须要开发者本身配置的功能,其余功能都是服务器和浏览器自动实现,不须要开发者关心。
是不是由于须要配置,才使得Server Push难以推广?
Nginx和Apache等网关早已支持Server Push功能,只须要简单配置后,在HTML的Response的Header中带上:
Link: ; rel=preload; as=style
复制代码
即可以轻松实现ServerPush😁。
一两句简单的配置,没法阻挡开发者对于极致性能的追求,那么还有其余缘由吗?
上图中Without Push和With Push对比只是最简单的场景,基本不可用于实际的生产中。
在实际的场景中,为了提高加载速度和减轻服务器的负载,通常会使用CDN进行资源的加载。因而乎,咱们的加载时序图就会变成这样:
彷佛也不复杂,只要咱们的CDN支持Server Push,同Nginx同样,带上相应的Header,即可以实现ServerPush。
继续研究一下 ServerPush CDN的支持状况:在2016年4月28日,国外最大的提供商cloudflare宣布支持ServerPush。
而反观国内,各大CDN厂商仿佛对ServerPush这一HTTP/2特性熟视无睹。腾讯云、阿里云两大云厂商CDN产品文档都没有相关说明。只有一家小厂”又拍云“在2018年出过一篇PR文章:
通过重重查找,咱们发现了一些端倪:
腾讯云+社区的一篇文章在开头指出,腾讯云已经支持ServerPush,而且进行了相关的性能测试。
万事俱备,只欠东风。然而,历史包袱是沉重的。静态资源和HTML不一样域让CDN ServerPush成为了梦。
以常见的一个源站与CDN不一样域的页面为例,HTML做为Web请求的入口,为了不CDN Cache致使用户没法即时更新应用,通常选择不走CDN,直接解析到Origin的Nginx上。
而JS、CSS、IMG等静态资源,则是走CDN的域名。
HTML和CSS不在同一个域下,根本没法Push。
访问一下国内的各大网站,发现基本上都是用了这种静态资源与HTML不一样域的方案,要实现ServerPush,就要推进CDN主域化。
CDN主域化是一个复杂、充满风险的工程,尤为是在业务高速运转时进行,无异于高速路上换轮胎。其方案要通过层层设计,本文在此就不作研究。
假设咱们已经实现了CDN主域化,Server Push是否是能够立刻提高性能呢?
“
现阶段,Server没法得知Client是否有Cache
若是Client已经有该资源的Cache,那么Push的静态资源会毫无心义地浪费带宽。虽然浏览器能够经过RST_STREAM阻止Server继续向Client发送资源,可是一部分资源已经在网络中进行传输。
饿了么的这篇文章:
给咱们带来的比较好的启示,文章中提到, 为了不静态资源和HTML不一样域、Push Cache这两大问题,选择了不Push静态资源,而是Push CGI请求。
相较于Preload CGI逻辑,ServerPush CGI请求可以减小1个RTT时间,而且不用考虑Cache的问题,从而稳定地带来性能上的提高。
目前,IETF已经有相关草案在进行讨论,使用Cache Digests,请求带上客户端的缓存状况供服务器识别,从而解决ServerPush的Push Cache问题。
目前 103 Early Hints 信息状态响应码也一样处于草案阶段,经过简单的HTTP回包来容许用户在服务器还在准备响应数据的时候预加载一些资源。
相较于Server Push,它避免了Push Cache的问题,但性能上没有Server Push极致。
下图总结了各类方案(Preload、103 Early Hints、Server Push、Server Push + Cache Digests)的性能:
(注:不考虑HTTPS TLS握手的RTT)
能够看到,Server Push + Cache Digests在性能上拥有很大的优点。但愿它能尽快成为规范。
Nginx Team对Server Push在实际场景下首次加载作了一次benchmark,以下所示:
原文:Introducing HTTP/2 Server Push with NGINX 1.13.9
Server Push在这种场景下也能带来必定性能提高。
在什么状况下,咱们应该使用Server Push?
毫无疑问,当Client与Server的过长而带宽十分充足时,ServerPush节省的一个RTT能带来很好的优化。
一个新的问题是:RTT多长时,该使用ServerPush?
Google Chrome小组的一篇文章给出了计算公式:
Rules of Thumb for HTTP/2 Push
翻译以下:
遗憾的是,咱们没法正常得知用户的带宽和RTT时间。
为了不Push Cache这一问题,咱们能够只为第一次访问的用户进行Push。
能够考虑使用Cookie来鉴别是否为第一次访问的用户,但也须要注意Cookie并不能完整的描述全部静态资源Cache的状况,举个例子:
Client Cookie没有失效,可是CSS资源的Cache已经失效,此时Server由于Client有Cookie而选择不进行Push。
固然,也能够设计更复杂的方案在Cookie中作标记,告诉Server Push的时机。
还好已经有方案来实现:
H2O 服务器提供了一个叫做 cache-aware server push的方案,原理就是将全部缓存过的资源都记录在 cookie 里,这样服务器就知道哪些资源不须要被推送了。
不过,在 cookie 里记录全部的资源路径会占用不少的空间,所以还须要将路径压缩一下。
这里能够考虑使用bloom filter来减小cookie数据量,能够点击查看这篇文章:
若是应用使用了SSR,Push静态资源可能会占用加载HTML的带宽,从而增长首屏时间。CSR的场景更适合Server Push。
因为Server Push可能会占据Client端本就窄小不充裕的带宽,在某些场景下,可能会起到拔苗助长的效果。
另外一个缘由是Server Push使用冷TCP链接,TCP的慢启动致使加载资源的效率比热TCP连接更慢
而下一个页面的资源由Service Worker来主动fetch相较来讲要更为合适。
Introducing HTTP/2 Server Push with NGINX 1.13.9
Rules of Thumb for HTTP/2 Push
To push, or not to push?! - The future of HTTP/2 server push - Patrick Hamann - JSConf EU 2018