技术干货:HTTP/2 之服务器推送 (Server Push) 最佳实践

阅读数:144 2019 年 10 月 30 日 13:23

技术干货:HTTP/2之服务器推送(Server Push)最佳实践

HTTP/1.X 出色地满足互联网的普遍访问需求,但随着互联网的不断发展,其性能越来越成为瓶颈。IETF 在 2015 年发布了 HTTP/2 标准, 着重于提高 HTTP 的访问体验, HTTP2 优势主要包括: 二进制传输、头部压缩、多路复用和服务器推送 (Server Push)。

截止目前, 大部分 CDN 厂商已经宣布支持 HTTP/2,然而”支持”大多省略了服务器推送 (ServerPush) 特性。估计这和 nginx 开源版本没有支持 Server Push 相关。
为提供完备的 HTTP2 能力,腾讯 CDN 现已完成 HTTP/2 的 Server Push 支持,并完成了详细的性能测试。

序言

在介绍 Server Push 功能之前,先来分析网站的加载过程。图 1 是腾讯课堂 ( https://ke.qq.com/index.html ) 的时间瀑布图。

a) 首先浏览器请求主页面 index.html,服务端响应内容;

b) 获取到主页应答,浏览器开始解析主页的 html 标签,发现构建 DOM 树还需要 CSS, GIF, JS 等资源;

c) 发起针对 CSS,GIF,JS 的内容请求;

d) 获取并解析 JS 和 CSS 等内容, 然后继续请求依赖资源。

技术干货:HTTP/2之服务器推送(Server Push)最佳实践

图 1 腾讯课堂域名的时间瀑布图

图 2 是简化的浏览器和服务器的交互过程,横轴代表时间轴,每个虚线区间是 1 个 RTT。红色竖线表示 DOM 加载完成的时间。从图中可知,虽然存在并发传输, 但主页 index.html 和依赖的资源 common.css、0684a8bf.css、comb.nowrap.0b772fee.js 等总体上是顺序的,等待资源响应的时间减慢了主页面加载速度。并发传输并不能提高串行解析的资源访问体验。

如果服务端接收到客户端主请求,能够“预测”主请求的依赖资源,在响应主请求的同时,主动并发推送依赖资源至客户端。客户端解析主请求响应后,可以”无延时”从本地缓存获取依赖资源, 减少访问延时, 提高访问体验,也加大了链路的并发能力。Server Push 正是基于此原理来提高网络体验。

技术干货:HTTP/2之服务器推送(Server Push)最佳实践

图 3 说明了若采用服务端推送的功能,则 JS/CSS 资源基本可以和 HTML 资源同步到达,浏览器可以“无延时”获取 JS/CSS 资源,客户端的延时最多可以减少一个 RTT。

构建一个简单的例子来验证我们的说法。图 4 所示为 simple_push.html 代码,页面依赖资源 simple_push.js 和 simple_nopush.js, 页面大小均不超过 1KB,主要时间消耗在传输延时。如图 5 所示为推送 simple_push.js 和不推送 simple_nopush.js 的效果对比。

技术干货:HTTP/2之服务器推送(Server Push)最佳实践

图 4 推送测试 HTML 代码

技术干货:HTTP/2之服务器推送(Server Push)最佳实践

图 5 不推送 & 推送的效果对比

我们上线了一个测试 demo 网站。网页上展示一张世界地图,由 400 个小图片组成。对比三种访问方式:HTTP/1.1、HTTP/2(无 Server Push)和 HTTP/2(Server Push)。Server Push 选择推送第 150~179 个共 30 个小图。访问性能数据对比如图 6 所示:可以发现预推送比无推送有一定的性能提升(受网络延时和客户端行为影响,结果存在波动,后文有相应分析)。

技术干货:HTTP/2之服务器推送(Server Push)最佳实践

图 6 demo 网站测试

简要介绍了 Server Push 的优化原理之后,伴随而来的疑问,推送什么资源,怎么去推送,以及比其他优化技术有什么优势?读完本章,这些问题将一一得到解答,文章最后用实例展示 Server Push 的应用场景和性能优化效果。

1 推送实现

1、标识依赖资源

W3C 候选推荐标准建议了依赖资源的两种做法:文件内 < link> 标签和 HTTP 头部携带, 表示该资源后续会被使用, 可以预请求, 关键字 preload 修饰这个资源, 写法如下:

a) 静态 Link 标签法:

复制代码
<link rel="preload" href="push.css" as="style">

b) HTTP 头表示法:

复制代码
Link: <push.css>; rel=preload; as=style

其中 rel 表明了资源 </push.css> 是预加载的,as 表明了资源的文件类型。另外,link 还可以用 nopush 修饰,表示浏览器可能已经有该资源缓存,指示有推送能力的服务端不主动推送资源,只有当浏览器先检查到没有缓存,才去指示服务端推送资源,nopush 格式写成:

复制代码
Link: </app/script.js>; rel=preload; as=script;nopush。

2、推送资源

用户访问 CDN,主要包括直接访问的边缘节点, 若干中间节点和客户源站,路径中的每层都可以对请求做分析,预测可能的依赖资源,通过插入静态标签或者增加响应头部返回给浏览器。 CDN 的推送主要采用头部携带推送信息。

a) 客户端指定推送资源

客户端通过 url 或者请求头说明需要的资源 url,写法如下:

复制代码
Url:http://http2push.gtimg.com/simple_push.html?req-push=simple_push.js

或者:

复制代码
GET /simple_push.html HTTP/1.1
Host: http2push.gtimg.com
User-Agent: curl/7.49.1
Accept: */*
X-Push-Url: simple_push.js

b) CDN 节点指定推送资源

CDN 节点针对请求资源配置推送资源, 基础配置如下:

复制代码
location ~ “/simple_push.html$” {
http2_server_push_url /simple_push.js
}

c) 源站指定推送资源

通过增加响应头 link 通知客户端或者 CDN 节点,后续希望推送的依赖资源,中间具有 推送功能的节点 (如 CDN 节点) 可以基于此信息进行资源请求与推送.

3、功能实现

图 7 所示为 CDN 的 Server Push 架构, 基本流程如下:

a) 用户请求到达服务器之后,依赖资源预测模块根据请求头或者配置预测浏览器需要的资源, 该推送资源 url 必须是和主请求是同一 host。如果不属于同一 host,服务器拒绝推送资源。

b) 服务器通过 PUSH_PROMISE 桢告诉浏览器准备推送的资源路径,该信息在原主请求流上发送,必须优先主请求响应发送,否则浏览器可能在推送资源到达前已经发起了依