欢迎你们前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~html
做者:yangchunwenweb
HTTP协议是前端性能乃至安全中一个很是重要的话题,最近在看《web性能权威指南(High Performance Browser Networking)》,把其中关于HTTP部分的内容拿出来分享一下,加了一点本身的想法,固然没有《HTTP权威指南》讲得详细,但对于理解咱们日常作的事情颇有启发。预计会有两三篇文章,重点分别会涉及到HTTP 1.一、HTTPS、HTTP 2.0等内容,本篇主要涉及HTTP 1.1及其应用。浏览器
HTTP的第一个版本被官方称为HTTP0.9,这是个只有一行的协议,例如:缓存
GET /about/ (超文本响应……) (链接关闭……)
HTTP 0.9有几个要点:安全
这个版本的HTTP主要用来传输文本,而且没有共用TCP链接。性能优化
一个典型的HTTP 1.0请求过程以下:服务器
GET /rfc/rfc1945.txt HTTP/1.0 User-Agent: CERN-LineMode/2.15 libwww/2.17b3 Accept: */* HTTP/1.0 200 OK Content-Type: text/plain Content-Length: 137582 Expires: Thu, 01 Dec 1997 16:00:00 GMT Last-Modified: Wed, 1 May 1996 12:45:26 GMT Server: Apache 0.84 (超文本响应……) (链接关闭……)
相对前一个版本,HTTP 1.0主要有如下几点变化:cookie
这时候开始有了请求及返回首部的概念,开始传输不限于文本(其余二进制内容)网络
HTTP 1.1是当前大部分应用所使用的协议版本。相对前面的1.0版本,HTTP 1.1语义格式基本保持不变,可是它加入了不少重要的性能优化:持久链接、分块编码传输、字节范围请求、加强的缓存机制、传输编码及请求管道。
实际上,持久连接在后来被反向移植到了HTTP1.0上
HTTP 2.0 的主要目标是改进传输性能,实现低延迟和高吞吐量。HTTP 2.0做了不少性能角度的优化,另外一方面,HTTP的高层协议语义并不会由于此次版本升级而受影响。全部HTTP首部、值,以及它们的使用场景都不会变。现有的任何网站和应用,无需作任何修改均可以在 HTTP 2.0 上跑起来。换句话说, 等之后咱们的服务器、客户端(如浏览器)都支持HTTP 2.0的时候,咱们不用为了利用 HTTP 2.0 的好处而修改标记,做不少额外的编码,却能享受到它带来的更低的延迟和更高的网络链接利用率交付!
HTTP 2.0的内容将在下篇或下下篇放出,本文不对其作过多润色
前面讲到,HTTP 1.1这个版本引入了大量加强性能的重要特性,其中包括:
这里重点讲一下持久化、管道在前端性能优化中的一些应用
所谓持久链接,就是重用 TCP链接,多个HTTP请求公用一个TCP链接。
HTTP 1.1 改变了 HTTP 协议的语义,默认使用持久链接。换句话说,除非明确告知(经过 Connection: close
首部),不然服务器默认会保持TCP链接打开。若是你使用的是 HTTP 1.1,从技术上说不须要 Connection: Keep-Alive
首部,但不少客户端仍是选择加上它,好比咱们的浏览器在发起请求的时候,通常会默认帮咱们带上 Connection: Keep-Alive
首部。
咱们来看一下为何持久链接对咱们来讲这么重要。
假设一个网页仅包含一个HTML文档及一个CSS样式文件,服务器响应这两个文件的时间分别为40ms及20ms,服务器和浏览者分别在哈尔滨和深圳,二者之间单向光纤延迟为28ms(假设的理想状态,实际会比这个要大)。
HTML下载完毕后,TCP链接关闭。
能够看到,两个HTTP请求都分别须要经历一次TCP的三次握手时间,另外,图中没有体现到的是,每一次TCP请求都有可能会经历一次TCP慢启动 过程,这是影响传播性能的一个不可忽视的重要因素。
假如咱们底层的TCP链接获得重用,这时候的状况会是这样子:
很明显,在获取CSS的请求中,减小了一次握手往返。
一开始,每一个请求要用两个TCP链接,总延迟为284ms。在使用持久链接后,避免了一次握手往返,总延迟减小为228ms。这里面两次请求节省了56ms(一个RTT,Round-Trip Time)的时间
上面的例子还只是只有一个HTML和一个CSS的简单假设状况,而现实世界的web的HTTP请求数量比这个要多得多,在启用持久链接的状况下,N次请求节省的总延迟时间就是(N-1)×RTT。
现实状况中,延迟更高、请求更多,性能提高效果比这里还要高得多。事实上,网络延迟越高,请求越多,节省的时间就越多。实际应用中,这个节省的总时间可按秒来算了。若是每个HTTP都重启一个TCP链接,可想而知要浪费多少时间!
持久 HTTP 可让咱们重用已有的链接来完成屡次应用请求,但屡次请求必须严格知足先进先出(FIFO,first in first out)的队列顺序:发送请求,等待响应完成,再发送客户端队列中的下一个请求。
举一下上一节持久链接的那个例子,首先,服务器处理完第一次请求后,会发生了一次完整的往返:先是响应回传,接着是第二次请求,在第二次请求到达服务器之间的这段时间里,服务器空闲。
若是服务器能在处理完第一次请求后,当即开始处理第二次请求呢?甚至,若是服务器能够并行处理两个请求呢?
这时候HTTP管道就派上用场了,HTTP管道是一个很小但对上述工做流很是重要的一次优化。
有了HTTP管道,咱们的HTTP请求在必定程度上不用再一个一个地串行请求,而是能够多个并行了,看起来好像很理想:
如上图,HTML和CSS的请求同时到达服务器,服务器同时处理,而后返回。
这一次,经过使用HTTP管道,又减小了两次请求之间的一次往返,总延迟减小为 172 ms。从一开始没有持久链接、没有管道的284ms,到优化后的172ms,这40%的性能提高彻底拜简单的协议优化所赐。
等一下,刚刚那个例子好像哪里还不够好:既然请求同时到达,同时处理,为何后面要先返回HTML,而后再返回CSS?二者不能同时返回吗?
理想很丰满,现实却有点骨感,这就是HTTP 1.1管道的一个很大的局限性:HTTP请求没法很好地利用多路复用,不容许一个链接上的多个响应数据交错返回(多路复用)。于是一个响应必须彻底返回后,下一个响应才会开始传输。
这个管道只是让咱们把FIFO队列从客户端迁移到了服务器。也就是说,请求能够同时到达服务器,服务器也能够同时处理两个文件,可是,两个文件仍是得按顺序返回给用户,以下图:
能够看到,即便客户端同时发送了两个请求,并且CSS资源先准备就绪,可是服务器也会先发送 HTML 响应,而后再交付 CSS。
题外话 上面两节举的例子,说到了HTML和CSS请求同时到达,这是书中的例子,实际上,我的以为这个例子举得不是很恰当。 实际的web中,HTML及其包含的CSS通常不会同时到达服务器,正常的瀑布图也不是这样的,每每是要先获取HTML内容后浏览器才能发起其中的CSS等资源请求。我想做者只是为了阐述原理吧,我的认为换成同一个HTML文档中CSS和JS可能更加恰当。
这个问题的原理在于TCP层面的“队首阻塞”,感兴趣能够去复习下计算机网络的课程。其代价每每是:不能充分利用网络链接,形成服务器缓冲开销,有可能致使客户端更大的延迟。更严重的时,假如前面的请求无限期挂起,或者要花很长时间才能处理完,全部后续的请求都将被阻塞,等待它完成。
因此,在HTTP 1.1中,管道技术的应用很是有限,尽管其优势毋庸置疑。实际上,一些支持管道的浏览器,一般都将其做为一个高级配置选项,但大多数浏览器都会禁用它。换句话说,做为前端工程师,开发的应用是面向普通浏览器应用的话,仍是不要过多的期望HTTP管道,看来仍是期待一下HTTP 2.0中对管道的优化吧。
不过,实际上仍是有很好地利用HTTP管道的一些应用,例如在WWDC 2012上,有苹果的工程师分享了一个针对HTTP优化取得巨大成效的案例:经过使用HTTP的持久链接和管道,重用iTunes中既有的TCP链接,使得低网速用户的性能提高到原来的3倍!
实际上假如你想充分利用管道的好处,必需要保证下面这几点条件:
由于iTunes的服务器和客户端都受开发者控制的应用,因此他们能知足以上的条件。这也许能给开发hybrid应用或者开发浏览器以外的web应用的前端工程师们一些启发,若是你开发的网站面向的用户是使用五花八门的浏览器,你可能就没辙了。
由于HTTP 1.1管道存在上面的缺点,因此利用率不高。那么问题来了:假设没有使用HTTP管道,咱们的全部HTTP请求都只能经过持久链接,一个接一个地串行返回,这得有多慢?
实际上,现阶段的浏览器厂商采起了另外的办法来解决HTTP 1.1管道的缺陷:容许咱们并行打开多个TCP会话。至因而多少个,你们可能已经似曾相识:4到8个不等。这就是前端工程师很是熟悉的浏览器只容许从同一个服务器并行加载4到8个资源
这一认识的真正来历。
HTTP持久链接虽然帮咱们解决了TCP链接复用的问题,可是现阶段的HTTP管道却没法实现多个请求结果的交错返回,因此浏览器只能开启多个TCP链接,以达到并行地加载资源的目的。
只能说,这是做为绕过应用协议(HTTP)限制的一个权宜之计。能够这样打一个比喻,一个水管没法同时运输多种液体,那就只能给每一种液体开通一条运输管了,至于这个水管何时能够智能化到同时运输不一样的液体,又能保证各自完整不受干扰到达目的地并在目的地自行分类?仍是那一句,期待HTTP 2.0吧。
这里的链接数为何是4到8个,是多方平衡的结果:这个数字越大,客户端和服务器的资源占用越多(在高并发访问的服务器中由于TCP链接形成的系统开销不可忽视),每一个主机4到8个链接只不过是你们都以为比较安全的一个数字。
前面说到,浏览器和服务器之间只能并发4到8个TCP链接,也就是同时下载4到8个资源,够吗?
看看咱们如今的大部分网站,动不动就几十个JS、CSS,一次六个,会形成后面大量的资源排队等待;另外,只下载6个资源,对带宽的利用率也是很低的。
打个比喻,一个工厂装了100根水管,每次却只能用其中6根接水,既慢,又浪费水管!
因此,咱们前端性能优化中有一个最佳实践:使用域名分区!
对啊,何须把本身只限制在一个主机上呢?咱们能够手工将全部资源分散到多个子域名,因为主机名称不同了,就能够突破浏览器的链接限制,实现更高的并行能力。
经过这种方式“欺骗”浏览器,这样浏览器和服务器之间的并行传输数量就变多了。
域名分区使用得越多,并行能力就越强!
可是,域名分区也是有代价的!
实践中,域名分区常常会被滥用。
例如,假设你的应用面向的是2G网络的手机用户,你分配了好几个域名,同时加载十几二十多个CSS、JS,这里的问题在于:
因此在一些低带宽高延时的场景,例如2G手机网络,域名分区作过了的话,不光不会带来前端性能的提高,反而会变成性能杀手。
域名分区是一种合理但又不完美的优化手段,最合适的办法就是,从最小分区数目(不分区)开始,而后逐个增长分区并度量分区后对应用的影响,从而获得一个最优的域名数。
咱们前端性能优化中有这么一个所谓的最佳实践原则:合并打包JS、CSS文件,以及作CSS sprite。
如今咱们应该知道为何要这样作了,实际上就是由于如今HTTP 1.1的管道太弱了,这两种技术的效果就好像是隐式地启用了HTTP 管道:来自多个响应的数据先后相继地链接在一块儿,消除了额外的网络延迟。
实际上,就是把管道提升了一层,置入了应用中,也许到了HTTP 2.0时代,前端工程师就不用干这样的活了吧?(HTTP 2.0的内容下篇讲)
固然,链接拼合技术一样有代价的。
打包文件到底多大合适呢?惋惜的是,没有理想的大小。然而,谷歌PageSpeed团队的测试代表,30~50 KB(压缩后)是每一个JavaScript 文件大小的合适范围:既大到了可以减小小文件带来的网络延迟,还能确保递增及分层式的执行。具体的结果可能会因为应用类型和脚本数量而有所不一样。
JavaScript 和CSS 代码, 经过适当的script 和style 块能够直接放在页面中,而图片甚至音频或PDF 文件,均可以经过数据URI(data:[mediatype][;base64],data)的方式嵌入到页面中。
上面的这种方式咱们称为资源内嵌。
嵌入资源是另外一种很是流行的优化方法, 把资源嵌入文档能够减小请求的次数。尤为在2G网络等状况中,内嵌资源能够有效地减小屡次请求带来的时延。能够参考这篇文章在2G中的一些实践。
固然,有缺点:
Google的砖家给出一些经验:
本文介绍了HTTP 1.1在前端性能优化中的一些应用,有些是为了绕过HTTP 1.1局限性的一些不得不作的事情,好比资源合并、压缩、内嵌等,这些均可以说是HTTP 2.0来临前的一些解决问题的“黑魔法”。
HTTP 1.1及其利用固然远远没有本文说得那么简单,我只是浓缩了一部份内容,有兴趣能够去研究《HTTP权威指南》。
问答
BDD框架的前端如何搭建?
相关阅读
全面进阶 H5 直播(上)
NodeJs内存管理
WebGL 纹理颜色原理
【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识
此文已由做者受权腾讯云+社区发布,更多原文请点击
搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!
海量技术实践经验,尽在云加社区!