HTTP 版本特性
HTTP/2 相比于 HTTP/1.1 大幅度提高了网页的性能,但存在兼容性及如何优雅降级的问题。
虽然 HTTP/2 提高了网页的性能,但不代表它是完美的,HTTP/3 就是为了解决 HTTP/2 存在的一些问题而推出。
一、HTTP/0.9
- 只支持 GET 请求方式;
- 没有请求头;
- 服务端响应后立即关闭 TCP 连接。
HTTP 最早版本的是 1991 年发布的 0.9 版,该版本极其简单,只有一个命令 GET。
GET /index.html
上面命令表示,TCP 连接(connection)建立后,客户端向服务器请求(request)网页 index.html
协议规定,服务器只能回应 HTML 格式的字符串,不能回应其它格式。
<html>
<body>Hello World</body>
</html>
服务器发送完毕就关闭 TCP 连接。
二、HTTP/1.0
- 请求方法新增 POST 和 HEAD;
- 扩充了传输内容格式(图片、音视频资源、二进制等)
- 添加了请求头和响应头;
- ...
1996 年 5 月,HTTP/1.0 版本发布,内容大幅度增加。
首先,除了 GET 方法,还引入了 POST 和 HEAD 方法,丰富了浏览器与服务器的互动手段。
其次,任何格式的内容都可以发送。这使得互联网不仅可以传输文字,还能传输图像、视频、二进制文件。
再次,HTTP 请求和回应的格式也变了,除了数据部分,每次通信都必须包括头信息(HTTP header),用来描述一些元数据。
其他的新增功能还包括状态码(status code)、多字符集支持、多部分发送(multi-part type)、权限(authorization)、缓存(cache)、内容编码(content encoding)等。
三、HTTP/1.1
- 请求方法新增 PUT、DELETE、OPTIONS、CONNECT 和 TRACE;
- 引入持久连接;
- 引入管线化;
- 新增 Host 字段;
- 新增 cache-control 字段;
- 支持断点传输。
1997 年 1 月,HTTP/1.1 版本发布。它进一步完善了 HTTP 协议,一直流行到现在。
1、新增请求方法
HTTP/1.1 的请求方法新增了 PUT、DELETE、OPTIONS、TRACE 和 CONNECT。
2、引入持久连接
HTTP/1.1 的最大变化,就是引入了持久连接(persistent connection),即 TCP 连接默认不关闭,可以被多个请求复用,不用声明 Connection: keep-alive。
客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求时,发送 Connection: close,明确要求服务器关闭 TCP 连接。
Connection: close
目前,对于同一个域名,大多数浏览器允许同时建立 6 个持久连接。
3、引入管线化
HTTP/1.1 还引入了管线化(pipelining),即在同一个 TCP 连接里面,客户端可以同时发送多个请求。这样就进一步改进了 HTTP 协议的效率。
4、新增 Host 字段
HTTP/1.1 客户端请求的头信息新增了 Host 字段,用来指定服务器的域名。
Host: www.example.com
有了 Host 字段,就可以将请求发往同一台服务器上的不同网站,为虚拟主机的兴起打下了基础。
5、新增 cache-control 字段
当浏览器请求资源时,先看是否有缓存的资源,如果有缓存,直接取,不会再发请求,如果没有缓存,则发送请求。
通过设置字段 cache-control 来控制缓存。
6、支持断点传输
在上传/下载资源时,如果资源过大,将其分割为多个部分,分别上传/下载,如果遇到网络故障,可以从已经上传/下载好的地方继续请求,不用从头开始,提高效率。
7、HTTP/1.1 的缺陷
- 队头阻塞导致的高延迟;
- 无状态特性带来的巨大 HTTP 头部;
- 明文传输带来的不安全性;
- 不支持服务器推送消息。
7-1、队头阻塞导致的高延迟
虽然 HTTP/1.1 允许复用 TCP 连接,但同一个 TCP 连接中所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。要是前面的回应特别慢,后面就会有许多请求排队等着。这称为"队头阻塞"(Head-of-line blocking)。
为了避免这个问题,只有两种方法:
- 减少请求数;
- 同时多开持久连接。
这导致了很多额外的网页优化工作,比如引入雪碧图、将小图内联到 CSS、使用多个域名等等,例如:
/* 将小图内联到 CSS */
.icon1 {
background: url(data:image/png;base64,<data>) no-repeat;
}
.icon2 {
background: url(data:image/png;base64,<data>) no-repeat;
}
7-2、无状态特性带来的巨大 HTTP 头部
由于报文 Header 一般会携带 "User Agent"、"Cookie"、"Accept"、"Server" 等许多固定的头字段(如下图),多达几百字节甚至上千字节,但 Body 却经常只有几十字节(比如 GET 请求、204/301/304 响应),成了不折不扣的“大头儿子”。Header 里携带的内容过大,在一定程度上增加了传输的成本。更要命的是,成千上万的请求响应报文里有很多字段值都是重复的,非常浪费。
7-3、明文传输带来的不安全性
HTTP/1.1 在传输数据时,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份,这在一定程度上无法保证数据的安全性。
7-4、不支持服务器推送消息
客户端想要获取更新需要通过轮询的方式,十分浪费带宽,占用资源。
WebSocket 是 HTML5 新增的协议,它在浏览器和服务器之间建立一个不受限的双向通信的通道,比如说,服务器可以在任意时刻发送消息给浏览器。
四、SPDY 协议
2009 年,谷歌公开了自行研发的 SPDY 协议,主要解决 HTTP/1.1 效率不高的问题。
SPDY 位于 HTTP 之下,TCP 和 SSL之上,这样可以轻松兼容老版本的 HTTP 协议(将 HTTP/1.x 的内容封装成一种新的 frame 格式),同时可以使用已有的 SSL 功能。
这个协议在 Chrome 浏览器上证明可行以后,就被当作 HTTP/2 的基础,主要特性都在 HTTP/2 之中得到继承。
五、HTTP/2
- 二进制传输:HTTP 2 头信息和数据体都是二进制;
- Header 压缩:HTTP 2 使用 HPACK 算法对 header 的数据进行压缩;
- 多路复用:在共享 TCP 的基础上同时发送请求和响应;
- 服务器推送:服务器可以额外的向客户端推送资源,而无需客户端明确的请求。
2015年,HTTP/2 发布。它不叫 HTTP/2.0,是因为标准委员会不打算再发布子版本了,下一个新版本将是 HTTP/3。
HTTP/2 基于 SPDY,专注于性能,最大的一个目标是在用户和网站间只用一个连接(connection),使用 HTTP/2 能带来 20%~60% 的效率提升。
HTTP/2 的新特性如下:
1、二进制传输
HTTP/2 传输数据量大幅减少,主要原因是以二进制方式传输和 Header 压缩。
HTTP/2 采用二进制格式传输数据,而非 HTTP/1.x 里纯文本形式的报文,解析起来更高效。而且将请求和响应数据分割为更小的帧,并且采用二进制编码,多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装。
2、Header 压缩
无论是 HTTP/1.1 还是 HTTP/2,它们都有请求头和响应头,这是浏览器和服务器的通信语言。
HTTP/2 引入了头信息压缩机制(header compression),对请求头和响应头进行了压缩,使得传输效率得到大幅提升。
3、多路复用
在 HTTP/2 中引入了多路复用的技术,一个域名只使用一个 TCP 长连接来传输数据,这样整个页面资源的下载过程只需要一次慢启动,同时也解决了浏览器限制同一个域名下的请求数量的问题,更容易实现全速传输。
举例来说,在一个 TCP 连接里面,服务器同时收到了 A 请求和 B 请求,于是先回应 A 请求,结果发现处理过程非常耗时,于是就发送 A 请求已经处理好的部分,接着回应 B 请求,完成后,再发送 A 请求剩下的部分。
上面是 HTTP/2 最核心的多路复用机制,可以看到每个请求都有一个对应的 ID,如 stream1 表示 index.html 的请求,stream2 表示 foo.css 的请求。这是因为 HTTP/2 的数据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。
HTTP/2 将每个请求或回应的所有数据包,称为一个数据流(stream)。每个数据流都有一个独一无二的编号。数据包发送的时候,都必须标记数据流 ID,用来区分它属于哪个数据流。
服务器端接收到这些请求后,会根据自己的喜好来决定优先返回哪些内容,比如服务器可能早就缓存好了 index.html 和 bar.js 的响应头信息,那么当接收到请求的时候就可以立即把 index.html 和 bar.js 的响应头信息返回给浏览器,然后再将 index.html 和 bar.js 的响应体数据返回给浏览器。
之所以可以随意发送,是因为每份数据都有对应的 ID,浏览器接收到之后,会筛选出相同 ID 的内容,将其拼接为完整的 HTTP 响应数据。
客户端还可以指定数据流的优先级。优先级越高,服务器就会越早回应。
HTTP/2 使用了多路复用技术,可以将请求分成一帧一帧的数据去传输,这样带来了一个额外的好处,就是当收到一个优先级高的请求时,比如接收到 JavaScript 或者 CSS 关键资源的请求,服务器可以暂停之前的请求来优先处理关键资源的请求。
4、服务器推送
HTTP/2 允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送(server push)。
场景:当用户请求一个 HTML 页面之后,服务器知道该 HTML 页面会引用几个重要的 JavaScript 文件和 CSS 文件,那么在接收到 HTML 请求之后,附带将要使用的 CSS 文件和 JavaScript 文件一并发送给浏览器,这样当浏览器解析完 HTML 文件之后,就能直接拿到需要的 CSS 文件和 JavaScript 文件,这对首次打开页面的速度起到了至关重要的作用。
另外,服务端可以主动推送,客户端也有权利选择是否接收。如果服务端推送的资源已经被浏览器缓存过,浏览器可以通过发送 RST_STREAM 帧来拒收。主动推送也遵守同源策略,换句话说,服务器不能随便将第三方资源推送给客户端,而必须是经过双方确认才行。
5、HTTP/2 的缺陷
- TCP 以及 TCP+TLS 建立连接的延时;
- TCP 的队头阻塞并没有彻底解决。
5-1、TCP 以及 TCP+TLS 建立连接的延时
HTTP/2 使用 TCP 协议来传输,如果使用 HTTPS 则需要使用 TLS 协议进行安全传输,而使用 TLS 也需要一个握手过程,这样就需要有两个握手延迟过程:
- 建立 TCP 连接时需要和服务器进行三次握手来确认连接成功,也就是说要消耗完 1.5 个 RTT 之后才进行数据传输。
- 进行 TLS 连接,TLS 的两个版本 TLS1.2/TLS1.3 建立连接所花的时间不同,大致需要 1~2 个RTT。
总之,在传输数据之前,需要花掉 3~4 个 RTT。
5-2、TCP 的队头阻塞并没有彻底解决
由于 HTTP/2 中多个请求跑在一个 TCP 管道中,当出现丢包时,TCP 为了保证可靠传输,会进行丢包重传,因此 HTTP/2 出现丢包时整个 TCP 都要开始等待重传,会阻塞该 TCP 连接中的所有请求。
而对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。
六、HTTP/3
Google 在推 SPDY 时就意识到上面 HTTP/2 的这些问题(由 TCP 协议导致),由于 TCP 协议僵化,几乎不可能通过修改 TCP 协议自身来解决这些问题。于是 Google 另起炉灶搞了一个基于 UDP 协议的 QUIC 协议,让 HTTP 跑在 QUIC 上而非 TCP 上。
这个 HTTP over QUIC 就是 HTTP 协议的下一个大版本,HTTP/3,它在 HTTP/2 的基础上又实现了质的飞跃,真正“完美”地解决了“队头阻塞”问题。
QUIC 虽然基于 UDP,但是在原本的基础上新增了很多功能,例如:
- 保证数据可靠性传输;
- 实现了快速握手功能;
- 集成了 TLS 加密功能;
- 彻底解决 TCP 中队头阻塞的问题。
1、保证数据可靠性传输
虽然 UDP 不提供可靠性的传输,但 QUIC 在 UDP 的基础之上增加了一层来保证数据可靠性传输。它提供了数据包重传、拥塞控制以及其他一些 TCP 中存在的特性。
2、实现了快速握手功能
QUIC 基于 UDP,而 UDP 是“无连接”的,不需要“握手”和“挥手”,所以可以用最快的速度来发送和接收数据。
3、集成了 TLS 加密功能
QUIC 目前使用的是 TLS1.3,相较于早期版本 TLS1.3 有更多的优点,其中最重要的一点是减少了握手所花费的 RTT 个数。
4、彻底解决队头阻塞的问题
QUIC 也实现了 HTTP/2 中的多路复用功能,但和 TCP 不同,QUIC 实现了在同一物理连接上可以有多个独立的逻辑数据流,实现了数据流的单独传输,解决了 TCP 中队头阻塞的问题。
七、总结
版本 | 特性 |
---|---|
HTTP/0.9 |
|
HTTP/1.0 |
|
HTTP/1.1 |
|
HTTP/2 |
|
HTTP/3 |
|