前言
做为一只前端开发🐶,HTTP是咱们知识地图里面必不可少的一部分,也是面试必问知识点。HTTP2号称可让咱们的应用更快、更简单、更稳定,它完美解决了1.1版本的诸多问题,本文和你们一块儿聊聊HTTP2的改进点。css
HTTP发展史
正式讲HTTP2以前咱们先讲一下HTTP的发展史。 html
- HTTP/0.9 - 单行协议
HTTP于1990年问世,那时候HTTP很是简单:只支持GET方法;没有首部;只能获取纯文本。
- HTTP/1.0 - 搭建协议的框架
1996年,HTTP正式被做为标准公布,版本为HTTP/1.0。1.0版本增长了首部、状态码、权限、缓存、长链接(默认短链接)等规范,能够说搭建了协议的基本框架。
- HTTP/1.1 - 进一步完善
1997年,1.1版本接踵而至。1.1版本的重大改进在于默认长链接;强制客户端提供Host首部;管线化;Cache-Control、ETag等缓存的相关扩展。
目前存在的问题
如今咱们先不聊HTTP2, 看一下HTTP发展到1.1存在有哪些问题:前端
- 线头阻塞:TCP链接上只能发送一个请求,前面的请求未完成前,后续的请求都在排队等待。
- 多个TCP链接
虽然HTTP/1.1管线化能够支持请求并发,可是浏览器很难实现,chrome、firefox等都禁用了管线化。因此1.1版本请求并发依赖于多个TCP链接,创建TCP链接成本很高,还会存在慢启动的问题。
- 头部冗余,采用文本格式
HTTP/1.X版本是采用文本格式,首部未压缩,并且每个请求都会带上cookie、user-agent等彻底相同的首部。
- 客户端须要主动请求
HTTP/2.0的时代来了
先来一个demo感觉一下吊炸天的HTTP/2.0,这个demo是加载379张图片,来对比HTTP/1.1和HTTP/2.0的性能。 HTTP/1.1 与2.0 性能比较node
理论上HTTP/2.0会比HTTP/1.1有一倍多的性能提高,弱网环境下,性能提高会更加明显。 下面两张图是我在设置网络在fast 3G 和slow 3G的性能对比。web
是否是被HTTP/2.0的速度亮瞎了双眼?2333,接下来咱们正式开始聊聊2.0。看看2.0 相比与1.1的一些重大改进。
二进制分帧层
HTTP2性能提高的核心就在于二进制分帧层。HTTP2是二进制协议,他采用二进制格式传输数据而不是1.x的文本格式。面试
看图吧!很清晰的表达了HTTP/1.1的响应和2.0的区别。1.1响应是文本格式,而2.0把响应划分红了两个帧,图中的HEADERS(首部)和DATA(消息负载) 是帧的类型。
了解更多帧的类型也就是说一条HTTP响应,划分红了两个帧来传输,而且采用二进制来编码。
这里咱们来提三个概念。chrome
- 流(Stream):已创建的TCP链接上的双向字节流,能够承载一个或多个消息。
- 消息(Message):一个完整的HTTP请求或响应,由一个或多个帧组成。特定消息的帧在同一个流上发送,这意味着一个HTTP请求或响应只能在一个流上发送。
- 帧(Frame):通讯的基本单位。
一个TCP链接上能够有任意数量的流。
多路复用
上面提到HTTP/1.1的线头阻塞和多个TCP链接的问题,HTTP2的多路复用完美解决。HTTP2让全部的通讯都在一个TCP链接上完成,真正实现了请求的并发。咱们来看一下HTTP2具体是怎么实现的:api
HTTP2创建一个TCP链接,一个链接上面能够有任意多个流(stream),消息分割成一个或多个帧在流里面传输。帧传输过去之后,再进行重组,造成一个完整的请求或响应。这使得全部的请求或响应都没法阻塞。 咱们再来回看上面的那个demo:
打开控制台能够看到,HTTP/1.1的方式,后面的图片的加载时间主要耗时在stalled,stalled的意思是从TCP链接创建完成,到真正能够传输数据之间的时间差。这就是队头阻塞,前面的请求没有处理,后面的请求都在排队等待。
这里例子咱们能很直观的看到就是多路复用起到的优化做用。由于HTTP2 实现了请求并发,后面的请求不用再等待,加载时长固然少了不少。截一张HTTP2的图片加载耗时详情来看看(要看比较靠后的请求): 浏览器
咦??什么状况?咱们发现后面的不少请求依旧有在排队哎,只是排队的时间相对1.1少了不少。一个TCP链接能够有任意数量的流,也就是同时能够并发任意数量的请求啊,为啥还会排队呢?缘由就是请求太多时,浏览器或服务器会受不了,这超出了它的处理能力。流控制帮咱们解决了这个问题,流控制会管理数据的传输,容许接收者中止或减小发送的数据量,省得接收方不堪重负。因此请求太多时,仍是会存在排队等待的问题,由于不论是客户端或服务器端,能同时处理请求或响应都是有限的。
头部压缩
头部压缩也是HTTP2的一大亮点。在1.X版本中,首部用文本格式传输,一般会给每一个传输增长500-800字节的开销。如今打开一个网页上百个请求已经是常态,而每一个请求带的一些首部字段都是相同的,例如cookie、user-agent等。HTTP2为此采用HPACK压缩格式来压缩首部。头部压缩须要在浏览器和服务器端之间:缓存
- 维护一份相同的静态字典,包含常见的头部名称,以及常见的头部名称和值的组合
- 维护一份相同的动态字典,能够动态的添加内容
- 经过静态Huffman编码对传输的首部字段进行编码
HTTP2的静态字典是长这个样子的(只截取了部分,完整表格在这里):
因此咱们在传输首部字段的时候,例如要传输method:GET,那咱们只须要传输静态字典里面method:GET对应的索引值就能够了,一个字节搞定。像user-agent、cookie这种静态字典里面只有首部名称而没有值的首部,第一次传输须要user-agent在静态字典中的索引以及他的值,值会采用静态Huffman编码来减少体积。
第一次传输过user-agent 以后呢,浏览器和服务器端就会把它添加到本身的动态字典中。后续传输就能够传输索引了,一个字节搞定。
咱们用WireShark来抓包验证一下:
HTTP2目前都是HTTPS的请求,WireShark对HTTPS网站抓包解密请参考这里。
- 首次传输user-agent和第二次传输user-agent
因为第一次传输的时候,字典里面并无user-agent的值,这时候user-agent是63字节,第二次传输时,他已经在动态字典里面了,只传索引,一个字节搞定。
Header解码后的长度有471个字节,而HEADERS流只有246个字节。这只是第一个请求,后续的请求压缩力度会更大,由于前面请求用到的首部(静态字典中没有的)会添加到动态字典中,使得后续请求只须要传输字典里面的索引。
服务器端推送
服务器端推送使得服务器能够预测客户端须要的资源,主动推送到客户端。
例如:客户端请求index.html,服务器端可以额外推送script.js和style.css。 实现原理就是客户端发出页面请求时,服务器端可以分析这个页面所依赖的其余资源,主动推送到客户端的缓存,当客户端收到原始网页的请求时,它须要的资源已经位于缓存。
针对每个但愿发送的资源,服务器会发送一个PUSH_PROMISE帧,客户端能够经过发送RST_STREAM帧来拒绝推送(当资源已经位于缓存)。这一步的操做先于父响应(index.html),客户端了解到服务器端打算推送哪些资源,就不会再为这些资源建立重复请求。当客户端收到index.html的响应时,script.js和style.css已经位于缓存。
想要搭一个HTTP2服务器的话推荐node,很简单。连接
参考文章
结语
简单讲了HTTP2相比1.1版本的重要改进点,感觉了一下h2的强大。还有一些流优先化等特性文中未涉及,感兴趣的能够在参考文章中看看。若有错误,恳请指正!
@Author:小夭yao爱吃糖糖糖