共计 3972 个字符,预计需要花费 10 分钟才能阅读完成。
HTTP/ 2 是 HTTP 协定自 1999 年 HTTP 1.1 的改进版 RFC 2616 公布后的首个更新,前身是 SPDY 协定(Google),于 2015 年 2 月 17 日被批准。
HTTP/ 2 规范于 2015 年 5 月以 RFC 7540 正式发表,少数支流浏览器曾经在 2015 年底反对了该协定。目前国内外大多数网站也都曾经反对了 HTTP/2,比方 Google/Stackoverflow/Reddit,国内的 淘宝 /Segmentfault/ 掘金 /CSDN/ 博客园 /36Kr 等等都曾经全面反对了 HTTP/ 2 协定
HTTP/2 相比 HTTP/1 来说,次要是性能上的大幅晋升,而且齐全没有没有改变 HTTP/ 1 协定中的利用语义。Method、State Code、URI 和 Header 等外围概念齐全没有变动
上面具体介绍 HTTP/2 中的一些要害降级点
二进制的分层(Binary Framing Layer)
Binary Framing Layer 的设计,算是 HTTP/ 2 性能晋升的外围了。HTTP/ 2 中在应用层又设计了一套 BinaryFrame Layer, 它定义了 HTTP 音讯在传输过程中的封装形式。不过这个 Frame Layer 和 TCP 的 Packet 可不是一回事,这个 Frame Layer 只是逻辑上的分层,在 HTTP 和 TCP 层之间,相似于 Http Chunk
如上图所示,HTTP/ 2 中的报文,在传输前都会被先构建成一个个的帧(Frame),每次 Socket 发送的最小单位是一个帧,每个帧都以二进制格局进行编码
二进制格局编码(Binary format encode)
在 HTTP/ 1 中,数据都是以文本编码的模式进行传输的。那么什么叫文本编码,什么叫二进制编码呢?
举个例子,协定中有一个长度的首部值为 11
,这个数字在文本编码中(用字符串来示意),它会占用 2 个 Byte,对应的字节为 [49, 49]
,那么在二进制编码下,11
如果是 Unsigned Int 类型,那么它会占用 4 个 Byte,对应的字节为[0, 0, 0, 11]
。
下面这个例子,看起来二进制编码下占用更大了;其实大多数状况下,二进制编码的占用会更低。如果换个大点的数字2147483647
,在文本编码下须要占用 10 个 Byte,可二进制编码下还是只须要占用 4 个 Byte
文本编码(Byte Array) | 二进制编码(Byte Array) | |
---|---|---|
11 | [49, 49] | [0, 0, 0, 11] |
2147483647 | [50, 49, 52, 55, 52, 56, 51, 54, 52, 55] | [127, -1, -1, -1] |
二进制格局这个叫法尽管比拟容易引起歧义,不过大家都这么叫,那就是对的
不过既然都用二进制编码了,那么还能叫 超文本传输(HyperText Transfer)吗……
比方在 HTTP/ 1 中,有一个 Chunk 编码,和下面提到的 Binary Framing Layer 有些类似,都是在 TCP 之上加了一层逻辑层。Chunk 编码中有一个 Length 字段,就是用文本编码的,但 Binary Framing 中的长度和其余字段都是用二进制编码,所以这也是 HTTP/ 2 新增的逻辑层叫 Binary Framing 的起因吧
二进制格局尽管占用更小,但不像文本编码那种直观,易于调试,肉眼很难间接看出数据的内容
流 / 音讯 / 帧(Stream/Message/Frame)
- Stream – 流,已建设的双向字节流,是一条逻辑的链路,对应一次 HTTP 交互的残缺申请 和响应
- Message – 音讯,一次 HTTP 的申请音讯,或者响应音讯;一次申请 - 响应的交互报文,属于一个 Stream
- Frame – 帧,HTTP/2 传输的最小单位,每个帧蕴含帧首部,首部中会蕴含所属流的 ID,一个 Message 会被分成多个 Frame 进行传输,所属不同 Stream 的 Frame 能够交织发送,对端收到交织的 Frame 后再依据 Stream 进行组装
在 HTTP/ 2 中,(同源的)所有的通信都会在一个繁多的 TCP 连贯上执行,该连贯能够传输任意数量的 Stream
如上图所示:一条 TCP 连贯,能够承载多条 Stream,一条 Stream 蕴含一次残缺的申请和响应音讯,申请和响应音讯又会被拆分为多个 Binary Frame 进行传输
多路复用
HTTP/ 2 中的多路复用,和 I / O 多路复用(I/O Multiplexing)可不是一回事。I/ O 多路复用指的是同一个过程监听多个 Sock 事件,而 HTTP/ 2 中的多路复用是指在一条 TCP 连贯上同时进行多个申请和响应音讯的传输。
**
在 HTTP/ 1 中,如果想并行的发送多个申请,那么须要建设多条 TCP 连贯,每个连贯同时只能解决一个申请;而且当 TCP 连贯过多时,还会造成 TCP 的排队
HTTP/ 2 中新的 Binary Frame Layer 的设计解决了这个弊病,客户端和 服务器能够把 多条并行的 HTTP 音讯合成为互不依赖的 Frame(如下图所示),而后交织发送,最初对端会把这些交织的 Frame 按 Stream 分组重新组合起来
上图中蕴含了一个连贯上多个传输中的 Stream:Client 正在向服务端发送 Stream5 的 Data Frame,同时 Server 也在向 Client 交织发送 Stream1 和 Stream3 的 Frame,在这条 TCP 连贯上有 3 个申请 / 响应的数据进行交互
多路复用是 HTTP/ 2 性能晋升的外围,这种计划的劣势次要在于以下几点:
- 并行处理多个申请 / 响应,不会产生利用层面的阻塞
- 应用单个 TCP 连贯,大幅缩小资源占用
这里可能会有一个疑难,换成单个 TCP 连贯真的会更快吗?
其实不相对,HTTP/ 1 中浏览器个别会限度单个域名的连接数。比方 Chrome 会限度单个域名连接数下限为 6 个(历史数据,不保障精确,可能不同版本有所差别),如果在带宽足够,负载也不高时,同时下载的文件在 6 个以内时,单连贯和多连贯速度没什么区别。
不过如果同时下载的文件超过 6 个时,超出 6 个的那些下载就须要排队期待,这个时候就有区别了,HTTP/ 1 中的连贯模型会导致排队,而 HTTP/ 2 不会
针对上述问题也有一些解决方案,比如说 Domain Sharding,将动态资源别离部署至多个域名站点,这样就能够肯定水平上绕过浏览器的连贯限度;或者时拼接动态资源,将多个动态资源文件合并至一个,升高连接数的占用
而 HTTP/ 2 中,基本就不必思考连接数的限度,因为它对同源的下载只会应用一个 TCP 连贯,天然也就没什么连贯限度的概念了。
不过这里可能会思考一个问题,多个申请的数据在单个 TCP 连贯中传输时,会产生阻塞或者排队吗?
其实不会,TCP 滑动窗口大小的下限是 65535,足够跑满你的带宽。不过在网络环境极其差的时候,还是会有些影响的,毕竟收到 ACK 太慢或丢包,不过这种状况下单连贯和多连贯都一样有问题;但在网络状况良好时,单连贯省去了额定的建设连贯的开销,而且在 TCP 的 Slow Start 的机制下,连贯刚建设时滑动窗口的大小是比拟小的,传输报文的速率会有所升高;不过在 HTTP/ 2 下,因为只有一条连贯,且多个申请的报文能够交织发送不必期待,上述的毛病都能够防止了。
总结一下,HTTP/ 2 的多路复用机制并不是在所有场景下都可能晋升性能,只是在 浏览器 同时加载过多文件时才会大幅晋升性能,并行的申请越多 HTTP/ 2 和 HTTP/ 1 的差别会越显著,如果只是下载单个文件,这个多路复用并不能带来什么晋升
这个网站,提供了一个 HTTP/ 2 和 HTTP/ 1 在同时加载多个资源时的速度差别:https://http2.akamai.com/demo,这个网页中同时下载 400 个小图片,能够看到 HTTP2 的加载速度远超 HTTP/1
不过 HTTP/ 2 的优化可不止连贯复用,还有首部压缩,流量管制等
首部压缩(Header Compression)
在 HTTP/ 1 中,报文中的 Header 时通过文本模式编码的,就像上面这样:
每个 header name 都会跟一个冒号,而后时一个可选的空格,每个 header 以 CRLF 结尾,最初还会保留一个空行作为 header 局部的完结标识
这些 header 每次传输会减少 500-800 字节的开销,如果算上 HTTP cookie 的化,甚至会减少上千个字节开销。为了缩小此开销,HTTP/ 2 应用 HPACK 压缩格局压缩申请和响应 Header 数据:
- 通过 动态霍夫曼编码(Huffman code)对发送的 Header 进行编码,升高 Header 的大小
- 要求客户端和服务器都保护和更新以前看到的 Header 的索引列表,而后将该列表用作无效编码先前传输的值的参考
客户端和服务端都会建设一个 Header 索引表,外面蕴含曾经发送或接管的 header,当再次发送 header 数据时会先从这个 header 索引表中查找,如果找到就用非凡值替换这个反复 header,这样升高了反复值的占用,减小了报文大小
如上图所示,Request 1 和 Request 2 的 header 只有一个:path 不同,那么在发送 Request 2 时其余只有:path 须要残缺编码发送,其余 header 替换成对应反复的索引即可
总结
HTTP/ 2 性能晋升的要害,次要在于新的二进制分层配合多路复用设计,升高了连接数的占用,浏览器环境下晋升会比拟显著
晚期的域名域名分片(Domain Sharding),资源文件合并(Bundle resources)这种针对 HTTP/ 1 的优化策略,在 HTTP/ 2 下也齐全不须要了;如果降级到 HTTP/2,前端的一些构建工具最好也将构建策略调整一下,单域名 + 多文件会更适宜 HTTP/2。
不过对于一些纯服务端环境下,比方服务端调用三方零碎的 HTTP 接口,不会有什么连接数的限度,然而单连贯下还是能够升高建设连贯的开销
加上 HTTP/ 2 的 Header 压缩,升高了 HTTP 的报文大小,进一步的晋升了 HTTP/ 2 的性能
参考
- https://hpbn.co/http2/