阅读更多

4顶
0踩

Web前端

转载新闻 Web性能优化与HTTP/2

2015-10-15 11:05 by 副主编 mengyidan1988 评论(0) 有8419人浏览



如今,互联网上的内容越来越丰富,过去几年时间,一个页面产生请求和整个大小都一直增长,这个趋势还会一直保持,对页面性能优化也要马不停蹄。



一个页面,会经历过加载资源,执行脚本,渲染界面的过程。我们知道,100ms对于计算机来说,可以干很多事情了,但是对于网络请求,可能一次RTT就没了。因此,页面加载对于Web性能是重中之重。

加载的快慢可以总结成受两个因素影响:阻塞与延迟。
1、阻塞。浏览器在解析到脚本时,会阻塞页面,等到脚本下载执行完才继续解析文档。此外,浏览器还会限制同域下的并行请求数,超过这个限制后的请求就会被阻塞住。
2、延迟。网络请求都不可避免会有延迟,网页上的延迟有两种,一是DNS查询,二是TCP连接。
克服这些缺点,我们有一些约定俗成的方案:
  • 静态资源要支持304,开启HTTP缓存控制
  • 开启gzip,压缩HTTP body
  • css放在html的head里,js在body底部
  • 合并请求
  • 使用雪碧图
  • 域名分区(突破并行限制,也避免传输过多cookie)
  • 使用cdn

这些方案基本都能立竿见影。但是,对于追求极致(KPI)的我们,这些还是远远不够的。我们从页面开始加载时说起。



避免重定向

重定向意味着要重新发起请求,当然我们没事也不会乱跳。这里要说的一种重定向是,访问HTTP站点,跳转到HTTPS。

避免这种跳转,我们可以用HSTS策略,就是告诉浏览器,以后访问我这个站点,必须用HTTPS协议来访问,让浏览器帮忙做转换,而不是请求到了服务器后,才知道要转换。只需要在响应头部加上 Strict-Transport-Security: max-age=31536000 即可。

预加载

DNS查询需要个RTT时间,在浏览器级别,系统级别都会有层DNS缓存,之前解析过的可以直接从本机缓存获取,以减少延迟。

Web标准提供了一种DNS预解析技术,因为服务器是知道页面即将会发生哪些请求的,那我们可以在页面顶部,插入 <link rel="dns-prefetch" href="//host/">,让浏览器先解析一下这个域名。那么,后续扫到同域的请求,就可以直接从DNS缓存获取了。

此外,Web标准也提供prefetch,prerender的预加载技术。prefectch会在浏览器空闲的时候,向所提供的链接发起请求,而prerender不仅会请求,还会帮你在后台渲染页面。如果在一个页面中,你知道用户有很大概率去点某个链接,可以尝试把这个链接加到prefetch或prerender,那么用户就会秒开这个页面了。

使用TCP、TLS最佳实践

HTTP请求要经过建立TCP连接这一步,而TCP为了可靠传输,建立连接需要三次握手。如果网站又接入了HTTPS,那还要额外多两次RTT时间以建立安全通道,这样耗费了很多时间。HTTP是建立在TCP、TLS之上,那么TCP的最佳实践,SSL的优化都是适用于HTTP的优化。

比如TCP慢启动过程非常影响性能的,我们可以把初始窗口调大,让慢启动更快。对于TLS可用缓存session_ticket之类的优化可以减少一次RTT。

内联

对于一些简单的页面,CSS样式和JavaScript脚本甚至图片,可以不必使用外联的方式引入,直接把子资源内嵌到HTML里,图片可以用base64编码内嵌,这相当于请求页面时,服务器顺便把子资源给一共推送过去了。传输的内容都一样,但减少好多请求了,自然节省不少时间。

不过这样做的缺点是浏览器无法缓存这些子资源,这种做法只能降低首次加载时间,所以需要看取舍了。可能比较适用于一次性的页面,类似活动之类的。

手动管理缓存

为了代码架构清晰,便于维护,我们都会用模块化的方式去编码,每个模块一个文件,这样带来的问题是一个页面需要很多文件,要很多请求,这对页面性能是不利的。合并是解决这个问题的好方法,但又因为HTTP缓存机制是基于URL的,只要某个模块一改动,整个合并资源都要重新下载。

在对性能要求较高,比如在移动设备环境上,我们可以利用HTML5中的localStorage特性,来实现手动控制缓存。大概的思路是,在定义模块时,同时将模块的代码和版本号分别储存到localStorage,在下一次打算请求模块之前,我们先判断模块的最新版本是不是在localStorage中,将不存在的模块组合在一起,请求动态合并的资源。

不过,这种方案可能会引发安全问题。假如同域下的其他页面被XSS攻击,坏人就可以篡改localStorage的内容,可能导致原来的页面代码被植入恶意程序。解决的方法是,在执行模块之前,算一下代码摘要,对比下服务器给的该模块的摘要,再决定是否使用。也可以使用SRI策略,由浏览器帮你做校验。


HTTP持久连接

HTTP持久连接可以重用已建立的TCP连接,减少三次握手的RTT延迟。浏览器在请求时带上 connection: keep-alive 的头部,服务器收到后就要发送完响应后保持连接一段时间,浏览器在下一次对该服务器的请求时,就可以直接拿来用。

以往,浏览器判断响应数据是否接收完毕,是看连接是否关闭。在使用持久连接后,就不能这样了,这就要求服务器对持久连接的响应头部一定要返回content-length标识body的长度,供浏览器判断界限。有时,content-length的方法并不是太准确,也可以使用 Transfer-Encoding: chunked 头部发送一串一串的数据,最后由长度为0的chunked标识结束。



HTTP管线化

HTTP管线化可以克服同域并行请求限制带来的阻塞,它是建立在持久连接之上,是把所有请求一并发给服务器,但是服务器需要按照顺序一个一个响应,而不是等到一个响应回来才能发下一个请求,这样就节省了很多请求到服务器的时间。不过,HTTP管线化仍旧有阻塞的问题,若上一响应迟迟不回,后面的响应都会被阻塞到。



bigpipe

目前大部分模型都是,服务器把逻辑处理完之后,一次性把整个响应输出。这里存在一个阻塞的过程,逻辑处理一般都涉及IO操作的都比较慢,而现代浏览器都支持边接收数据边渲染,所以其实服务器可以接收到请求时就把页面框架flush出来,如果页面包含多个较独立部分,也可以每处理完一部分就马上输出,这样可以缩短白屏。从用户感受上可能会更好,页面上一直有所反应,而不是一直白屏,完全不知道你在干嘛。

各种各样的优化,都在填HTTP/1.x留下的坑,HTTP/2带着填坑的使命,从根本上去解决这些问题。HTTP/1.x是一个文本协议,这注定它是非常冗余的协议,HTTP/2改变了这一点,在HTTP/1.x的语义上,将文本数据封装在帧里,并采用二进制编码。

下图中binary framing就是二进制分帧层,这里会将HTTP/1.x的header翻译成headers类型的帧,将body翻译成data类型的帧。



HTTP/2的性能怎样,akamai的这个demo(https://http2.akamai.com/demo)估计会让你很兴奋。

下面详细介绍下HTTP/2。

多路复用

在HTTP/2中,有两个非常重要的概念:帧(frame)和流(stream)。

1、帧(frame)

HTTP/2中数据传输的最小单位,因此帧不仅要细分表达HTTP/1.x中的各个部份,也优化了HTTP/1.x表达得不好的地方,同时还增加了HTTP/1.x表达不了的方式。

每一帧都包含几个字段,有length、type、flags、stream identifier、frame playload等,其中type代表帧的类型,在HTTP/2的标准中定义了10种不同的类型,包括上面所说的HEADERS frame和 DATA frame。此外还有
PRIORITY(设置流的优先级)
RST_STREAM(终止流)
SETTINGS(设置此连接的参数)
PUSH_PROMISE(服务器推送)
PING(测量RTT)
GOAWAY(终止连接)
WINDOW_UPDATE(流量控制)
CONTINUATION(继续传输头部数据)

2、流(stream)

“流”在HTTP/2中是一个逻辑上的概念,就是说在一个TCP连接上,我们可以向对方不断发送一个个的消息,这里每一个消息看成是一帧,而每一帧有个stream identifier的字段标明这一帧属于哪个“流”,然后在对方接收时,根据stream identifier拼接每个“流”的所有帧组成一整块数据。我们把HTTP/1.x每个请求都当作一个“流”,那么请求化成多个流,请求响应数据切成多个帧,不同流中的帧交错地发送给对方,这就是HTTP/2中的多路复用。



从上图我们可以留意到:
  • 不同的流在交错发送;
  • HEADERS 帧在 DATA 帧前面;
  • 流的ID都是奇数,说明是由客户端发起的,这是标准规定的,那么服务端发起的就是偶数了。


多路复用让HTTP连接变得很廉价,只需要创建一个新流即可,这不需要多少时间,而在HTTP/1.x时代却要经历三次握手时间或者队首阻塞等问题。而且创建新流默认是无限制的,也就是可以无限制的并行请求下载。不过,HTTP/2还是提供了 SETTINGS_MAX_CONCURRENT_STREAMS 字段在 SETTINGS 帧上设置,可以限制并发流数目,标准上建议不要低于100以保证性能。

优化Web性能有一个常用的技术,就是图片延迟加载,目的是除了节省流量外,还能避免图片资源与其他重要的脚本资源竞争下载。

HTTP/2提供了流的优先级与依赖性这种机制,可用 HEADERS 帧或 PRIORITY 帧设置,不过协议并没有提供如何处理优先级的具体算法,这可由服务器灵活应对。我用个例子来说明这个机制。
<!-- a.html -->
<html>
<body>
<script src="a.js"></script>
<img src="a.jpg">
<img src="b.jpg">
<link rel="stylesheet" type="text/css" href="style.css">
</body>
</html>

浏览器是边下载边解析的,文档解析器首先遇到a.js,它就会去下载并且阻塞页面,同时,资源探测器会继续向下扫描,发现a.jpg、b.jpg和style.css并服务器发起请求。在没有优先级机制时,a.jpg、b.jpg会跟重要的a.js、style.css竞争下载,但在HTTP/2中,浏览器可以给a.jpg、b.jpg设置较低的优先级,另外依赖关系为



这样服务器根据优先级信息,首先吐出a.js、style.css,再吐出图片,因此页面在没有图片的情况下提前进入可交互状态。例子所说的是在浏览器层面上harcode的一个优先级策略,再比如上文提到的prefetch就可以给一个更低的优先级。在代码层面上,也许之后会提供一些控制优先级的特性,类似于目前只有IE支持的lazyload attribute。
服务器推送

作为HTTP/2的一个重磅新功能,我们不要简单理解字面意思,其实不是你想推,想推就能推的,服务器要遵循请求-响应这个模型,只不过服务器对同一请求可以推送多个响应。客户端在交换 SETTINGS 帧时,设置字段 SETTINGS_ENABLE_PUSH(0x2) 为1显式允许服务器推送。

在HTTP/1.x时代,其实我们已经体验过了“服务器推送”,就是资源内嵌到HTML里。服务器在响应HTML时,就已经知道浏览器会请求哪些子资源了,这时一并响应这些子资源,可以节省了服务器到浏览器以及浏览器解析再发请求的这段延迟。但是内联的问题是浏览器不会缓存这些数据,这意味要浪费很多流量,而且有缓存时网页性能还是很好的。

服务器推送解决了这个问题。服务器在接受到请求时,分析出要推送的资源,先发个 PUSH_PROMISE 帧给浏览器。此帧包含一个新的流ID,还有header block fragment字段,内容是请求的头部信息,可理解为服务器模拟浏览器发起请求,然后再发送各个response header和response body。浏览器收到 PUSH_PROMISE 帧时,根据header block fragment字段里的url,可以知道当前有没有缓存,从而判断是否要接收。如果不要,浏览器就要发送个 RST_STREAM 来终止服务器推送。

如果浏览器不要这个推送,就会出现浪费流量的现象,因为整个过程都是异步的,在服务器接收到RST_STREAM时,响应很有可能部份发出或者全部发出了。这种情况只能视场景而定,若是流量浪费不能容忍,我们可以使用prefetch来替代,让浏览器尽早发现需要的资源,而HTTP/2中创建新的请求并不需要多少时间,所以大概多了个RTT的时间。

首部压缩

服务器推送,此推送非彼推送,一开始以为,是不是以后可以抛弃轮询这种技术了?并不是,该轮询还是要轮询。那么,在开启keep-alive的情况下,轮询在HTTP/2中的性能没什么提升吗?也并不是。

在HTTP/1.x中首部是没有压缩的,gzip只会压缩body,HTTP/2提供了首部压缩方案。一般轮询请求首部,特别是cookie占用很多大部份空间,首部压缩使得整个HTTP数据包小了很多,传输也就会更快。

刚开始spdy提出的首部压缩方案比较简单粗暴,直接像压缩body那样压缩首部,这看起来好像没什么不妥,但是有安全隐患,会有受到CRIME式攻击的可能性。这种攻击方法简单说,就是不断地利用已知数据去探测密文,达到破解的目的。无损压缩算法会有个特性,数据越冗余,压缩效率越好。而首部中的很多字段是已知的,我们只要构造个请求,请求中带有首部的某个字段,经压缩再加密后的密文长度就会有所变化,然后不断构造猜测该字段的值,同时观察密文的长度,慢慢地确定首部字段的值。
GET /pwd=0 HTTP/1.1
Cookie: pwd=123

GET /pwd=1 HTTP/1.1
Cookie: pwd=123

我们会发现,前者的密文长度比后者长,这样就确定了“d”,再慢慢的猜测,达到破解的目的。

HTTP/2中抛弃了这种方案,用专门设计的HPACK。它是在服务器和客户端各维护一个“首部表”,表中用索引代表首部名,或者首部键-值对,上一次发送两端都会记住已发送过哪些首部,下一次发送只需要传输差异的数据,相同的数据直接用索引表示即可,另外还可以选择地对首部值压缩后再传输。按照这样的设计,两次轮询请求的首部基本是一样的,那之后的请求基本只需要发送几个索引就可以了。



“首部表”有两种,一种是静态表,即HTTP/2协议内置了常用的一些首部名和首部键值对。另一种是动态表,保存自定义的首部或五花八门的键值对等,动态表可以通过SETTINGS帧的SETTINGS_HEADER_TABLE_SIZE规定大小。

一次完整的HTTP/2通信



  • 在尚未知道服务器是否支持HTTP/2时,http请求头部加上upgrade: h2c,表明客户端支持HTTP/2,询问服务器要不要切换协议。
  • 浏览器同时发送HTTP2-Settings头部,带上base64编码的SETTINGS frame。
  • 对于https请求,是在TLS握手时进行协商,浏览器发送ClientHello时,带上h2标志,表明客户端支持HTTP/2。
  • 若服务器不支持,则忽略upgrade头部,正常响应。若支持,则发送101响应,以空行结束响应,并开始发送HTTP/2帧。
  • 服务器要先响应connection preface,带上SETTINGS frame。
  • 服务器创建新流,推送a.js。然后继续发送index.html和a.js的response header、response body。
  • 浏览器收到PUSH_PROMISE帧,发现服务器要推送的内容已经在浏览器缓存里了,遂发送RST_STREAM拒绝推送。
  • 服务器收到RST_STREAM后,不再推送a.js剩下的数据。
  • 服务器因为一些原因想要关闭连接,发送GOAWAY帧。也可以由浏览器关闭,只要浏览器觉得之后不再有请求了。

后话

HTTP/2才刚刚正式发布不久,支持程度并没有那么好,以后应该有相当长的一段时间,HTTP/2要与HTTP/1.x共存。特别是,Win7快要成为下个XP的节奏,那么IE9就是下个IE6了。双协议部署上,可能会有不少麻烦之处。HTTP/1.x时代的很多优化,在HTTP/2是不必要的,也有冲突的,甚至是累赘。

比如子资源的位置,可以用HTTP/2优先级解决。
比如域名分区,在HTTP/2中本来可以用一个连接完成,却要用多个连接,这样就有性能损耗了。
比如合并、雪碧图,之前是为了减少请求,但在HTTP/2新起请求不费事,但拆分开来倒可以更好地利用浏览器缓存。还有类似的内联资源,可以用服务器推送,也同样可以更好地利用缓存。
更多具体的问题,需要在生产实践中得出了。

参考资料

《Web性能权威指南》
https://httpwg.github.io/specs/rfc7540.html(HTTP/2协议)
https://httpwg.github.io/specs/rfc7541.html(HPACK)
https://imququ.com/post/http2-resource.html(HTTP/2资料汇总)
https://imququ.com/post/server-push-in-http2.html(HTTP/2中的Server Push讨论)
https://www.gitbook.com/book/ye11ow/http2-explained/details(HTTP2讲解)
http://httparchive.org/
http://segmentfault.com/a/1190000002642924

本文转自:coding.js的微信公众号
  • 大小: 254.9 KB
  • 大小: 80.5 KB
  • 大小: 27.9 KB
  • 大小: 45.3 KB
  • 大小: 37 KB
  • 大小: 63.5 KB
  • 大小: 43.6 KB
  • 大小: 33.7 KB
  • 大小: 33.6 KB
  • 大小: 65.1 KB
  • 大小: 197.5 KB
4
0
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • web性能优化演示文档

    web性能优化演示文档

  • web性能优化--用gzip压缩资源文件

    #一、gzip压缩技术 gzip(GNU- ZIP)是一种压缩技术。经过gzip压缩后页面大小可以变为原来的30%甚至更小,这样,用户浏览页面的时候速度会快得多。...可见,gzip压缩是页面性能优化的一种有效方式。

  • WebApi性能优化

    文章目录一、WebApi调优二、WebApi 应用场景三、WebApi 性能瓶颈定位四、WebApi性能优化手段一-本地缓存五、WebApi 性能优化手段二-分布式缓存六、WebApi 性能优化手段三-Http缓存(响应缓存)七、WebApi 性能优化...

  • 前端性能优化学习 02 Web 性能指标

    Web 性能指标 我们已经直到性能的重要性,但当我们讨论性能的时候,让一个网页变得更快,具体指哪些内容? 事实上性能是相对的: 对于一个用户而言,一个站点可能速度很快(在具有功能强大的设备的快速网络上),而...

  • Web/前端性能优化

    什么是Web性能   一个大型网站架构模型如下图所示,对一个网站的性能进行优化,可以分为 Web 前端性能优化、应用服务器端性能优化、存储服务器端性能优化三层。网站的整体性能,需要所有开发者一同来维护。   ...

  • web页面性能优化方法总结

    最近老大给我布置了一道作业,让我去想想有哪些办法可以优化web页面的性能,回头一看,做了这么多年的前端开发,多少还是知道一些常规的性能优化策略,以下是我对性能优化的一个总结,共13点,如果描述错误,欢迎...

  • 前端优化 - 提高 Web 性能的 9 个技巧

    这篇文章将介绍您可以用来帮助改进前端优化的有用技术。通过专注于干净的代码、压缩图像、最小化外部请求、实施 CDN 和其他一些方法,您可以显着提高网站的速度和整体性能。 1.清理HTML文档 HTML 或超文本标记...

  • 有关前端性能优化的方案—Vue 代码层面性能优化+Webpack 层面的优化+基础的 Web 技术优化+非框架代码优化

    有关前端性能优化的方案—Vue 代码层面性能优化+Webpack 层面的优化+基础的 Web 技术优化+非框架代码优化

  • Web应用前端性能优化浅析

    对于不断发展的Web应用,性能优化如同逆水行舟,不进则退。一般可以从前端和后端的优化来改善Web站点性能。本文侧重通过对前端性能的分析,为Web站点前端性能优化提供了理论依据和一般的优化策略,并讲述了一些用于B/S...

  • 性能优化-网站性能监测与优化

    Web前端性能优化数据 性能优化Web前端性能优化数据 性能优化Web前端性能优化数据 性能优化Web前端性能优化数据 性能优化

  • http/2时代的web性能

    近几年来,Web性能的话题逐渐升温,大家开始认识到它在设计过程中的重要性。今天,随着HTTP/2这个新协议的采用,Web已经进入HTTP/2时代。我们来探讨一下HTTP/2有哪些新特性,带来了哪些优势?

  • 2.前端性能优化-web性能指标

    web性能指标

  • Web性能优化方法总结

    web性能优化 一、页面优化 1、减少HTTP请求数量 1、从设计实现层面简化页面:尽量将页面设置成百度首页那样,保持页面简洁、减少资源的使用。 2、合理设置HTTP缓存:合理设置缓存可以大大地减少HTTP请求,怎么叫...

  • web性能优化技术概览

    摘自书《HTTP/2基础教程》 DNS查询优化 解析域名越快越好 限制域名的数量 保证低限度的解析延迟 主体HTML页面或响应中利用DBS预取指令。在下载并处理主体页面HTML的同时,预取指令能开始解析页面上指定的域名 &lt;...

  • Web项目性能优化之减少HTTP请求次数优化

    本文适合的读者为刚刚毕业,技术实力不是很好,没有考虑过自己编写程序的系统性能的读者。本文简单的介绍了,Web项目中常见的性能问题:请求HTTP次数过多,以及优化的思路。

  • HTTP/1.1的性能问题及可优化方案

    当前HTTP/1.1协议已经走过了有20个年头了,面对现代WEB应用,特别移动应用的挑战,设计缺陷问题日益明显,以下将主要列出主要存在的五大问题队头阻塞:在大多数情况下,浏览器获取资源都是多份的,HTTP/1.1并没有提供机制来...

  • web前端页面性能优化(提升页面加载速度)

    通过查阅相关资料,了解到了一些关于web性能优化的点,其中涉及的知识大致可以分为几类:度量标准、编码优化、静态资源优化、交付优化、构建优化、性能监控。 在详细介绍上边内容之前先来介绍一下常见的性能优化方法...

  • HTTP协议之:HTTP/1.1和HTTP/2

    HTTP的全称是Hypertext Transfer Protocol,是在1989年World Wide Web发展起来之后出现的标准协议,用来在WWW上传输数据。HTTP/1.1是1997年在原始的HTTP协议基础上进行的补充和优化。

  • 关于web前端性能优化总结

    1、从DOM结构和标签上来优化·使用语义化的标签,代码清晰简洁;·减少Dom节点,增加渲染速度;·使用W3C标准书写闭合... 2、从CSS样式上来优化·使用link加载样式而不是@import(是css2提供的一种方式,不兼容,只...

  • 【性能优化】前端 - 性能分析与优化

    主要用于前端项目的性能优化,即Web 应用在浏览器上的加载和显示的速度。

Global site tag (gtag.js) - Google Analytics