HTTP/2 新特性浅析
HTTP/2 源自 SPDY/2
SPDY 系列协议由谷歌开发,于 2009 年公开。它的设计目标是降低 50% 的页面加载时间。当下很多著名的互联网公司都在自己的网站或 APP 中采用了 SPDY 系列协议(当前最新版本是 SPDY/3.1),因为它对性能的提升是显而易见的。主流的浏览器(谷歌、火狐、Opera)也都早已经支持 SPDY,它已经成为了工业标准,HTTP Working-Group 最终决定以 SPDY/2 为基础,开发 HTTP/2。
但是,HTTP/2 跟 SPDY 仍有不同的地方,主要是以下两点:
HTTP/2 的优势
相比 HTTP/1.x,HTTP/2 在底层传输做了很大的改动和优化:
- HTTP/2 采用二进制格式传输数据,而非 HTTP/1.x 的文本格式。二进制格式在协议的解析和优化扩展上带来更多的优势和可能。
- HTTP/2 对消息头采用 HPACK 进行压缩传输,能够节省消息头占用的网络的流量。而 HTTP/1.x 每次请求,都会携带大量冗余头信息,浪费了很多带宽资源。头压缩能够很好的解决该问题。
- 多路复用,直白的说就是所有的请求都是通过一个 TCP 连接并发完成。HTTP/1.x 虽然能利用一个连接完成多次请求,但是多个请求之间是有先后顺序的,后面发送的请求必须等待上一个请求返回才能发送响应。这会很容易导致后面的请求被阻塞,而 HTTP/2 做到了真正的并发请求。同时, 流还支持优先级和流量控制。
- Server Push:服务端能够更快的把资源推送给客户端。例如服务端可以主动把 JS 和 CSS 文件推送给客户端,而不需要客户端解析 HTML 再发送这些请求。当客户端需要的时候,它已经在客户端了。
HTTP/2 主要是 HTTP/1.x 在底层传输机制上的完全重构,HTTP/2 是基本兼容 HTTP/1.x 的语义的(详细兼容性说明请戳 这里 )。 Content-Type
仍然是 Content-Type
,只不过它不再是文本传输了。那么 HTTP/2 的这些新特性又是如何实现的呢?
HTTP/2 的基石 - Frame
Frame 是 HTTP/2 二进制格式的基础,基本可以把它理解为它 TCP 里面的数据包一样。HTTP/2 之所以能够有如此多的新特性,正是因为底层数据格式的改变。 Frame 的基本格式如下(图中的数字表示所占位数,内容摘自 http2-draft-17 ):
+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------+
|R| Stream Identifier (31) |
+=+=================================================+
| Frame Payload (0...) ...
+---------------------------------------------------+
Length: 表示 Frame Payload 部分的长度,另外 Frame Header 的长度是固定的 9 字节(Length + Type + Flags + R + Stream Identifier = 72 bit)。
Type: 区分这个 Frame Payload 存储的数据是属于 HTTP Header 还是 HTTP Body;另外 HTTP/2 新定义了一些其他的 Frame Type,例如,这个字段为 0 时,表示 DATA 类型(即 HTTP/1.x 里的 Body 部分数据)
Flags: 共 8 位, 每位都起标记作用。每种不同的 Frame Type 都有不同的 Frame Flags。例如发送最后一个 DATA 类型的 Frame 时,就会将 Flags 最后一位设置 1( flags &= 0x01
),表示 END_STREAM,说明这个 Frame 是流的最后一个数据包。
R: 保留位。
Stream Identifier: 流 ID,当客户端和服务端建立 TCP 链接时,就会先发送一个 Stream ID = 0 的流,用来做些初始化工作。之后客户端和服务端从 1 开始发送请求/响应。
Frame 由 Frame Header 和 Frame Payload 两部分组成。不论是原来的 HTTP Header 还是 HTTP Body,在 HTTP/2 中,都将这些数据存储到 Frame Payload,组成一个个 Frame,再发送响应/请求。通过 Frame Header 中的 Type 区分这个 Frame 的类型。由此可见语义并没有太大变化,而是数据的格式变成二进制的 Frame。二者的转换和关系如下图:
为 HTTP/2 头压缩专门设计的 HPACK
如果我们约定将常用的请求比如 GET /index.html
用一个 1 来表示, POST /index.html
用 2 来表示。那么是不是可以节省很多字节?
为 HTTP/2 的专门量身打造的 HAPCK 便是类似这样的思路延伸。它使用一份索引表来定义常用的 HTTP Header。把常用的 HTTP Header 存放在表里。请求的时候便只需要发送在表里的索引位置即可。例如 :method=GET
使用索引值 2 表示, :path=/index.html
使用索引值 5 表示(完整的列表参考: HPACK Static Table )。只要给服务端发送一个 Frame,该 Frame 的 Payload 部分存储 0x8285
,Frame 的 Type 设置为 Header 类型,便可表示这个 Frame 属于 HTTP Header,请求的内容是:
GET /index.html
为什么是 0x8285
,而不是 0x0205
? 这是因为高位设置为 1 表示这个字节是一个完全索引值(key 和 value 都在索引中)。类似的,通过高位的标志位可以区分出这个字节是属于一个完全索引值,还是仅索引了 key,还是 key 和 value 都没有索引。因为索引表的大小的是有限的,它仅保存了一些常用的 HTTP Header,同时每次请求还可以在表的末尾动态追加新的 HTTP Header 缓存。动态部分称之为 Dynamic Table。Static Table 和 Dynamic Table 在一起组合成了索引表:
<---------- Index Address Space ---------->
<-- Static Table --> <-- Dynamic Table -->
+---+-----------+---+ +---+-----------+---+
| 1 | ... | s | |s+1| ... |s+k|
+---+-----------+---+ +---+-----------+---+
^ |
| V
Insertion Point Dropping Point
HPACK 不仅仅通过索引键值对来降低数据量,同时还会将字符串进行霍夫曼编码来压缩字符串大小。
以常用的 User-Agent
为例,它在静态表中的索引值是 58,它的值是不存在表中的,因为它的值是多变的。第一次请求的时候它的 key 用 58 表示,表示这是一个 User-Agent
,它的值部分会进行霍夫曼编码(如果编码后的字符串变更长了,则不采用霍夫曼编码)。服务端收到请求后,会将这个 User-Agent
添加到 Dynamic Table 缓存起来,分配一个新的索引值。客户端下一次请求时,假设上次请求 User-Agent
的在表中的索引位置是 62, 此时只需要发送 0xBE
(同样的,高位置 1),便可以代表: User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36
。其过程如下图所示:
最终,相同的 Header 只需要发送索引值,新的 Header 会重新加入 Dynamic Table。
Multipexing 多路复用
每个 Frame Header 都有一个 Stream ID 就是被用于实现该特性。每次请求/响应使用不同的 Stream ID。就像同一个 TCP 链接上的数据包通过 IP:PORT
来区分出数据包去往哪里一样。通过 Stream ID 标识,所有的请求和响应都可以欢快的同时跑在一条 TCP 链接上了。 下图是 http 和 spdy(http2 的模型和 spdy 是类似的) 的并发模型对比:
当流并发时,就会涉及到流的优先级和依赖。优先级高的流会被优先发送。图片请求的优先级要低于 CSS 和 SCRIPT,这个设计可以确保重要的东西可以被优先加载完。
Server Push
当服务端需要主动推送某个资源时,便会发送一个 Frame Type 为 PUSH_PROMISE 的 Frame,里面带了 PUSH 需要新建的 Stream ID。意思是告诉客户端:接下来我要用这个 ID 向你发送东西,客户端准备好接着。客户端解析 Frame 时,发现它是一个 PUSH_PROMISE 类型,便会准备接收服务端要推送的流。
结束语
本文简化了很多 HTTP/2 协议中的具体细节,只描述了 HTTP/2 中主要特性实现的基本过程。
如果你想实现一个支持 HTTP/2 的服务器,那么你可以移步 HTTP/2 官网 做更多了解,它还提供了一份已经实现 HTTP/2 的项目列表: https://github.com/http2/http2-spec/wiki/Implementations 。
另外,关于 HTTP/2 性能如何,可以参考官方小组给出的例子: https://http2.akamai.com/demo
相关推荐
### WP7交互特性浅析及APP设计探究 #### 涅磐重生的WP7(以及它的历史简介) ##### WP7的起源与转型 **Windows Phone 7 (WP7)** 是微软在移动操作系统领域的一次大胆尝试,标志着从Windows Mobile时代到Windows ...
传统方式对JavaScript的应用基本上是基于过程模型的,若在JavaScript中利用面向对象的思想进行代码编写,将会使得代码具有良好的结构和逻辑性,更便于管理和维护。本文让读者看到JavaScript如何实现面向对象编程并...
"JavaScript面向对象特性浅析与范例" JavaScript是一种基于原型的解释型语言,它允许在任意时刻给一个对象添加任意多的属性和方法。 JavaScript的面向对象特性是指它可以实现面向对象编程的部分特性,而不是完全...
在人工智能的视域下,未来课堂教学设计的特性分析为我们揭示了教育领域即将面临的一系列变革。人工智能技术的应用,不仅在教育中,而且在社会的各个层面都引发了广泛的讨论和深入的探究。尤其对于教育,人工智能与...
TD-LTE无线网络覆盖特性浅析.pdf
"电力系统频率问题浅析与频率特性研究综述" 在现代电力系统中,频率问题日益显著,系统安全稳定经济运行对频率稳定提出了更高的要求。电力系统频率特性的研究涌现并被应用于保障系统安全稳定运行。本文对电力系统...
《高速高功率半导体开关DSRD特性浅析》这篇论文主要探讨了一种先进的全固态高速高功率半导体开关——漂移阶跃恢复二极管(DSRD)。DSRD是超宽带、高功率脉冲信号源的核心组件,尤其适用于超宽带和高功率微波领域的...
浅析基于Python爬虫技术的特性及应用
这篇名为“浅析传统媒体与新媒体的关系”的论文深入探讨了这两个领域之间的相互作用、融合以及挑战。以下是对这一主题的详细解读: 一、传统媒体的定义与特点 传统媒体通常指的是报纸、广播、电视等在数字时代之前...
《EDA/PLD中的浅析C#中异步和多线程的区别》 在软件开发领域,C#语言提供了两种重要的编程模型,即异步和多线程,它们都能提高程序的并发性和响应性,但二者有着本质的区别。本文将深入探讨这两种技术的差异,并...
Linux系统特性及安全问题浅析.pdf
摘要:在嵌入式系统设计中我们...外部特性 A D 9 5 2 2是一个多路时钟输出和分配功能的芯片,本身支持亚皮秒抖动性能,在芯片内部还集成了PLL(PhaseLockedLoop)和VCO(压控振荡器)。VCO的调谐范围是 2.02GHz~2.
导读:动态特性是指传感器对于随时间变化的输入量的响应特性,只要输入量是时间的函数,则其输出量必将是时间的函数。 研究动态特性的标准输入形式有三种,即正弦、阶跃和线性,而经常使用的是前两种。 零阶...
虽然有传言JavaScript 2.0将加入类继承机制,但考虑到要让所有浏览器支持新特性可能需要很长时间,因此目前只能依靠现有的机制来实现对象的继承。 在JavaScript中,继承主要通过原型链实现。首先,我们需要区分几个...
新能源,如太阳能、风能等,因其环保、可持续的特性,已成为全球发展的重要方向。然而,新能源项目在开发和应用中往往伴随着高风险,这需要保险业提供相应的风险管理服务,以促进新能源的健康发展。 光伏发电保险...
浅析旅游微博营销特性及营销策略.zip
- **开源特性**:ROS的成功得益于其开源模式,吸引了全球各地的研究者共同贡献,形成了一套完整且强大的开发工具集。 - **应用场景**:ROS广泛应用于科研、教育、工业等领域,包括但不限于无人机、自动驾驶汽车、...
Spring 4.1是Spring框架的一个重要更新,虽然没有引入革命性的新特性,但它的主要改进集中在增强现有功能和依赖库的升级。以下是对这些新特性的详细分析: 1. **核心部分增强**: - **DirectFieldAccessor**:允许...