「如何实现流动式软件发布」线上课堂开课啦,快来报名参与课堂抽奖吧~ 了解详情
写点什么

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

2019 年 10 月 30 日

技术干货: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 等内容, 然后继续请求依赖资源。



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


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


如果服务端接收到客户端主请求,能够“预测”主请求的依赖资源,在响应主请求的同时,主动并发推送依赖资源至客户端。客户端解析主请求响应后,可以”无延时”从本地缓存获取依赖资源, 减少访问延时, 提高访问体验,也加大了链路的并发能力。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 的效果对比。



图 4 推送测试 HTML 代码



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


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



图 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.1Host: http2push.gtimg.comUser-Agent: curl/7.49.1Accept: */*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 桢告诉浏览器准备推送的资源路径,该信息在原主请求流上发送,必须优先主请求响应发送,否则浏览器可能在推送资源到达前已经发起了依赖资源请求,造成重复和浪费.


c) 依赖资源请求模块构造和主请求一样的请求信息,在本地或后端服务器请求推送资源,并主动创建新的 HTTP/2 请求流,后续服务器就可以发送资源响应,推送资源响应在服务端创建的流上传输,主页面响应在原始流传输。



图 7 CDN 的 Server Push 模块改造示意图


CDN 节点的推送资源发送顺序在主请求响应之前,如图 8 所示,主要基于以下因素考量:


d) 推送资源一般是静态的缓存命中率高的资源,如 JS、CSS、字体和图片等。这些资源可以从源站预先推送并缓存到 CDN 节点。相比之下, 主页面变更较多,需要等待网络 IO 去源站取数据。同时,CDN 边缘节点到浏览器的 RTT 一般是比 CDN 节点到源站的 RTT 更短。所以在取到主页面最新响应之前,有充足的时间去推送资源。


e) 资源推送可以探测提高 TCP 拥塞窗口,窗口逐渐增大,后续可以一次性发送完主页面响应。TCP 拥塞窗口对推送影响将在下文第三部分讨论。


f) 在等待主请求响应的网络 IO 时间期间,推送资源可以是无优先级关系,资源推送优先级对推送影响也将在下文第三部分讨论。



图 8 推送时间点位于主页面响应之前


2Server Push 技术对比

1、纵向对比

Server Push 相对应没有 Server Push 的具体提升如下:


a) Nopush 加载耗时:Tnopush = RTT+ max(RTT, size(HTML)/BandWidth)+size(JS)/BandWidth


b) push 耗时:Tpush = RTT + size(HTML)/BandWidth + size(JS)/BW


c) 改善效率:diff =1 - Tpush/TnoPush


所以决定推送是否有改善性能的衡量因素是 size(HTML/BandWidth)和 RTT 谁大。这里引入 BDP(BandWidth-Delay product, 带宽时延乘积)概念。BDP 描述了单位时间内该带宽能传输的数据大小。如果 size(HTML)< BDP,推荐使用 push;反之不推荐使用 push。


2、横向对比

HTTP/1.1 中有个资源内联(Resource Inlining)技术,把资源内容拷贝到 HTML 标签中。比如说< script >可以装载 js 的内容,< style >可以装载 CSS 的内容等。这样 JS 或者 CSS 的内容就会在第一个响应中推送给浏览器。虽然说它可以做到网站加速。但是它有很多 server push 没有的缺点。例如资源不能脱离 HTML 被浏览器单独缓存,并且这个资源在多个 url 中重复传输多遍。这在多个 url 共享这个资源的场景是不明智的做法。而使用 Server Push,在 CDN 能适用更丰富的应用场景。


3 使用场景分析

理论上,在带宽足够的环境下,把需要的资源预先推送给客户端,必然能够节省获取资源时间,提升页面访问速度。但由于 TCP 慢启动、资源加载优先级、浏览器缓存等因素约束,我们在实际测试中发现,Server Push 并不总能带来页面加载性能的提升。本节深入探讨下什么场景下的资源适合使用推送。


1、TCP 慢启动


先复习下 TCP 慢启动特性:为了防止网络拥塞,TCP 将放弃超出拥塞窗口大小的数据。只有当拥塞串口大小的数据传输完成,这个窗口大小将乘以 2。如此,能够传输的数据以 2 的倍数增长。假设拥塞窗口大小为 14kB,下图展示了某些情况下,推送比不推送的效率没有提升。



图 9 tcp 慢启动对服务器推送的影响


对比图 9 中子图 1 和子图 2,子图 1 虽然预推送了 / style.css,但是第一次 RTT 只传输了/style.css 的 4KB 数据,剩下的 16KB 在第 2 个 RTT 完成。子图 1 总共需要 2 个 RTT 的时间,和子图 2 没有进行推送用了同样多的时间。子图 3 使用了 3RTT 完成了整个网站的传输,这会比没有推送使用更多的时间。


2、资源加载优先级


先看下面一个网站例子:


<html><head><script src=”1.js”></script><script src=”3.js”></scirpt><script src=”4.js”></script></head><body></body></html>
复制代码


其中 1.js 会调用 2.js 文件,3.js 和 4.js 没有调用其他 JS。


正常没推送的例子加载时间表格会是



图 10 资源加载优先级的 nopush&push 效果图


可以看出是因为 1.js 的加载优先级本应该在 3.js 和 4.js 之前,但是预先推送了 3.js 和 4.js,然而 1.js 需要重新请求,并触发 2.js 请求,导致等待 1RTT 接收 2.js。所以 Push 比 No Push 的效率更差。


3、内核缓冲区

HTTP/2 的请求优先级并不能影响已经在内核发送缓冲区的数据。假设内核发送缓冲区大小比 TCP 拥塞串口大,导致服务端发送低优先级的数据,存在内核缓冲区。这时,后续有高优先级的响应必须等内核缓冲区空出才能被完成。假设我们访问一个 HTML 页面,这个 HTML 页面需要回源站取数据,而 HTML 需要的静态 JS 资源缓存在 CDN 边缘节点上。在回源站的等待时间内,把静态 JS 资源发送给浏览器。如果这时候静态 JS 资源很大,塞满了内核发送缓冲区,此时 HTML 响应已经到达 CDN 边缘节点,却不得不等内核缓冲区有空间才能继续发送。等待浏览器解析 HTML 内容后续的 link 请求也会被推迟。


4、浏览器缓存

推送浏览器已缓存的资源有可能使的加载时间更长,并且浪费带宽资源。重复推送已缓存的资源,如果没有额外的空闲带宽传输,网络会阻塞它之后正常的请求,导致拖累了整个网站的加载时间。


4 网站测试

我们对现网一些网页进行 Server Push 性能测试,因为推送要求同一个域名下的 HTTP/2 请求,为了规避非 HTTP/2 和跨余名带来的干扰,我们设置了代理节点,代理节点完成 HTTP/2 支持和域名收归,同时配置 Server Push 功能,观察网页的加载收益。为了准确测试 Push 带来网络时延变化,需要稳定的网络环境,在 chrome 设置网络环境 mytest(RTT: 200ms, Download: 29Mb/s, Upload: 14Mb/s),以下的例子都在该网络环境进行测试。


1、腾讯新闻

按照前面描述的推送适用场景,用这个腾讯新闻页面做测试。主请求页面大小为 11.6K。可以看出,预先推送 js、css、图片等资源给客户端带来的网站性能变快。



图 11 腾讯新闻页面



图 12 腾讯新闻页面的无推送 &推送对比图


2、腾讯客服

腾讯客服页面不支持 HTTPS 协议。之所以用这个页面是因为该网页页面主请求比较小,并且有 JS、CSS 触发的次优先级资源请求。我们把这个网页下载下来,并做了一些推送资源域名收归等必要的处理,放在 CDN 边缘节点做测试。这并没有改变网站的资源和请求顺序,不影响测试效果。


图 13 是腾讯客服的页面。图 14 列出腾讯客服页面的所有请求。我们关注下具体几种情况的时间轴:无推送、推送小文件、推送大文件。小文件推送预先在第一个 RTT 把 3 个第 3 层请求才能触发的资源(tcss.ping.js、cdn_djl.js、layer.css)预先推送给浏览器。大文件推送是预先推送了 indexBanner.png。


从图 14 中的无推送和推送 3 个小文件的子图中,红色虚竖线是指不包括 indexBanner.png 的加载完成时间,由于 3 个小文件(尤其是次优先级请求 tcss.ping.js)的提取推送,比无推送的时间延迟要短。但是又从无推送和推送大文件的子图中看到,如果无优先级顺序地推送大文件 indexBanner.png(782KB)对缩短网站时延无帮助。



图 13 腾讯客服页面



图 14 无推送 &推送小文件 &推送大文件的对比图


5 总结

虽然本章的测试用例只是庞大互联网网页的冰山一角,文章不能覆盖各种网页场景。但是以下的一些总结建议是有实践意义的。


1、在合适的时机,推送合适的资源,Push 比 No Push 带来的网站时延提升是明显的。在网络带宽足够承载推送资源的前提下,我们预先推送浏览器后续请求需要的资源,网站的整体加载时间得到缩短。但是现实网络环境有不一样的延时和带宽。慢速网络环境影响 TCP 拥塞窗口增长的速度,除非主页面请求足够小,Push 才能看到效果。


2、即使是错误地实施某些推送策略(比如说推送过大文件),带来的最严重后果,也就是改善不明显。所以建议是多做一些推送策略的尝试,直到把合适的资源在合适的时机把资源推送给浏览器。


3、网站往 HTTP/2 的环境迁移是个趋势。迁往 HTTP/2 需要将页面的所有请求尽量收归到同一域名,并且剥离出主页面的资源文件成多个独立的请求。假如你的网站已迁移到 HTTP/2,而且网站的主请求不大,但是可能会触发很多资源请求。建议 push 这些资源。另外不要推送存放在浏览器 cookie 的资源,这只会浪费带宽。


4、目前的 Server Push 推送机制没有解决浏览器已经具有资源缓存,而服务器已经推送到网络中,虽然浏览器可以发送 RST 桢拒绝推送流,但是服务器推送的资源已经在网络中等待浏览器接收。现在已经有一些规范草案尝试用协商缓存摘要来解决问题。


5、CDN 中的负载均衡机制可能会将低优先级的推送资源送入到系统缓存区,这会影响高优先级资源的推送效率问题。引入 QUIC 替代 TCP,可以对缓存中推送资源进行分级,高优先级资源先发。


6、未来或将引入 AI 分析取代固定推送实现智能化推送。


本文转载自公众号云加社区(ID:QcloudCommunity)。


原文链接:


https://mp.weixin.qq.com/s/d11OJ6iepqI2kJ9tczGYBQ


2019 年 10 月 30 日 13:231547

评论

发布
暂无评论
发现更多内容

Confluence 数据中心版本接近生命周期了

HoneyMoose

在线JSON转Go Struct工具

入门小站

工具

GraphQl Calculator计算指令@distinct:使用表达式对列表进行去重

杜艮魁

数据中台 graphql

模块二作业

毛先生

GitHub阅读量最高的文章竟是图解Java,不愧是Alibaba内部资料

Java~~~

Java 架构 面试 JVM 基础

阿里P8终于总结出这份SpringBoot分布式架构精髓笔记

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

如何利用 U 盘制作系统启动盘?

村雨遥

windows 9 月日更

🚄【Redis干货领域】从底层彻底吃透AOF原理(基础篇)

李浩宇/Alex

redis aof Redis 协议 9月日更 9 月日更

专项1

刘帅强

app测试

世界顶级安全专家耗时三年写出了这份4308页的Linux笔记

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

Go- 方法-2

HelloBug

go 方法

美团面试:请手写一个快排,被我怼了

胧月

程序员 面试 算法

RedHat 8 如何检查端口是否联通

HoneyMoose

物联网实时监管 区块链云端留痕 公章何时何地何人用过一清二楚

CECBC区块链专委会

Go- 方法-1

HelloBug

go 方法

Ubuntu Server 20.04 搭建安装Harbor

玏佾

Docker k8s Harbor

区块链落地进行时:专利猛增,构建工业互联网信任价值

CECBC区块链专委会

【VueRouter 源码学习】第一篇 - 环境搭建与路由模式介绍

Brave

源码 vue-router 9 月日更

Linux之ssh-add命令

入门小站

Linux

Go- 结构体

HelloBug

go 结构体

软件工程师必备沟通技巧

俞凡

沟通 认知

华为顶级网络工程师分享出这份TCP/IP网络编程笔记!已封神

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

Promise.all() 原理解析及使用指南

devpoint

Promise 异步任务 9 月日更

IntelliJ IDEA 中如何将 POM 中的版本号快速提出为属性

HoneyMoose

世界顶级安全专家整理出的这份笔记告诉你Linux应该怎么学

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

大厂慌了!由国外技术工程师亲自操刀的微服务实战手册限时分享

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

GitHub破百万访问的阿里神作:并发实现原理JDK源码笔记

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

阿里P8纯手写SQL文档:收获不止SQL优化抓住SQL的本质

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

如何大规模交付高质量、高一致性的RESTful API及配套产物

刘宇

API OpenAPI REST API

发布半小时登上GitHub首页的Spring Boot实战笔记,竟是京东T8编写

Java~~~

Java spring 架构 面试 Spring Boot

德勤:区块链成为现实,采用逐渐增多

CECBC区块链专委会

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