HTTP/0.9
HTTP/0.9 诞生于 1991年
最开始用来在网络之间传递 HTML 超文本内容,所以也被称为 超文本传输协议
它的实现也很简单,基于请求响应的模式,从客户端发出请求,服务端返回数据
一个完成的 HTTP/0.9请求流程如下
它拥有如下特点
- 只有一个请求行,并没有 请求头和请求体,因为只需要一个请求行就可以完整表达客户端的需求了
- 服务器也没有返回头信息,只需要返回数据就行
- 返回的文件内容是以 ASCII 字符流来传输的
HTTP/1.0
JavHTTP/0.9诞生的时候,已经能满足当下超文本传输的需求了
但是伴随着 拨号上网服务和浏览器的发展,互联网进入了高速发展的阶段
在浏览器中仅仅只有 HTML 已经不能满足需求了,还需要包括 JavaScript 、CSS、图片、音频、视频等不同类型的文件,因此 支持多种类型的文件下载成了HTTP/1.0和核心诉求
为了让客户端和服务器能有更深入的交流,HTTP/1.0 增加了请求行、请求头、响应头、响应体
客户端通过请求头告诉服务器,它需要什么类型的数据,采用什么编码,采用什么形式压缩,以及文件的语言等
accept: text/html
accept-encoding: gzip, deflate, br
accept-Charset: ISO-8859-1,utf-8
accept-language: zh-CN,zh
HTTP/1.0除了对多文件类型提供了支持之外,还引入了一些其他特性
- 状态码: 告诉客户端服务器等处理状态
- Cache机制: 缓存已经下载过的数据,减缓服务器的压力
- 用户代理: Hppt/1.0的请求头还增加了用户代理的字段,用来统计客户端的基础信息,比如 window 和 Mac的用户量是多少
HTTP/1.1
伴随着技术的发展,HTTP的需求不断在迭代更新
HTTP/1.0 每进行一次通信,都需要经历建立TCP连接,传输HTTP数据,断开TCP连接三个阶段
在最开始,文件比较小,所以传输形式没什么大问题
但是随着浏览器的普及,该通信方式已经捉襟见肘了,数次的连接无疑会大量增加通信开销
为解决该问题,HTTP/1.1中增加了持久连接的方法,它允许一个TCP连接上传输多个HTTP请求,只要浏览器或者服务器没有明确断开连接,那么该TCP连接会一直保持
除了持久化连接之外,HTTP/1.1还增加了如下特性
- HTTP管线化尝试: 为解决 TCP一直以来的队头阻塞问题,尝试将请求批量发送给服务器
- 提供虚拟主机支持: 请求头增加 Host 字段
- 动态内容支持: Chunk transfer 机制解决动态包长度判断问题
- 客户端Cookie、安全机制
HTTP/2
即使HTTP/1.1 采取了很多优化资源加载速度的策略,但是对 带宽的利用率并不理想, 这也是HTTP/1.1的核心问题
带宽: 带宽是指每秒最大能发送或者接收的字节数
上行带宽: 每秒能够发送的最大字节数
下行带宽: 每秒能够接收的最大字节数
三个问题
主要是以下三个问题导致的
问题1: TCP的慢启动
TCP进入连接状态之后,发送数据的速度会逐步增加,就像一辆汽车的启动过程一样,逐渐加速到一个理想状态
慢启动是TCP为了减少网络拥挤的一种策略,我们是没有办法改变的
问题2: 同时开启的多条TCP连接,会竞争固定的带宽
HTTP/1.1 最多可同时建立6条持久连接,那么在带宽不足的情况下,连接之间就会减缓数据接收速度。但是页面中的数据是有优先级的,可能就会影响到关键数据的加载了
问题3: HTTP/1.1 队头阻塞问题
在一个HTTP持久化管道中,通信的处理是串行的,同一时刻只能处理一个请求,在当前请求没有结束之前,其他请求都只能处于阻塞状态,在这个等待过程中,带宽都被白白浪费掉了
多路复用
定义: 在单条 TCP 连接中,客户端和服务器可以并行发送和接收多个 HTTP 请求/响应,无需按顺序等待
针对上面三个问题,HTTP/2 是怎么去解决的呢
HTTP/2的思路就是 一个域名只使用一个长连接来传输数据,这样整个页面的资源传输只需要一次慢启动,同时避免了TCP连接之间带宽的竞争
同时,为了解决队头阻塞的问题,HTTP/2任何时候都可以将请求发送给服务器,并不需要等待其他请求的完成,然后服务器也可以随时反悔处理好的请求资源给浏览器
该图就是HTTP/2 最核心、最重要且具有颠覆性的 多路复用机制
每一个请求都会有一个 ID属性,通过ID去获取完整的请求以及接收完整的数据
在 多路复用机制中, 最重要的是 二进制分桢层
- 首先,浏览器准备好请求数据,包括了请求行、请求头等信息,如果是POST方法,那么还要有请求体。
- 这些数据经过二进制分帧层处理之后,会被转换为一个个带有请求ID编号的帧,通过协议栈将这些帧发送给服务器。
- 服务器接收到所有帧之后,会将所有相同ID的帧合并为一条完整的请求信息。
- 然后服务器处理该条请求,并将处理的响应行、响应头和响应体分别发送至二进制分帧层。
- 同样,二进制分帧层会将这些响应数据转换为一个个带有请求ID编号的帧,经过协议栈发送给浏览器。
- 浏览器接收到响应帧之后,会根据ID编号将帧的数据提交给对应的请求。
从上面的流程可以看出,通过引入二进制分帧层,就实现了HTTP的多路复用技术
其他特性
除了多路复用之外,HTTP/2 还增加了如下特性
设置请求优先级: 按照客户端要求优先级传输数据
服务器推送: 将数据提前推送到浏览器(预渲染场景)
头部压缩: 针对大量请求提升传输效率
应用现状
浏览器支持:所有现代浏览器均支持 HTTP/2(需 HTTPS)。
服务器要求:Nginx/Apache 等主流服务器需开启 HTTP/2 模块。
性能提升:根据 Google 统计,HTTP/2 平均减少 30%~50% 的页面加载时间(高延迟场景更明显)
HTTP/3
从上述的介绍来看,似乎 HTTP/2 已经完美取代 HTTP/1
但其实 HTTP/2 依然存在一些缺陷,所以才有了 HTTP/3
HTTP/2的缺陷
1. TCP的队头阻塞
HTTP/2虽然从应用层面上解决了队头阻塞的问题,但是其本质上使用的还是TCP连接
单数据在传输过程中,有一个数据包因为网络故障或者其他原因丢失了,整个TCP连接就会处于暂停的状态
我们先分别来看下 HTTP/1.1 和 HTTP/2 的通信传输方式
HTTP/1.1: 一个TCP连接同一时间只能发送一个请求
HTTP/2: 一个TCP连接同一时间可以发送多个请求
队头阻塞:在TCP传输过程中,由于单个数据包丢失而造成的阻塞称为TCP上的队头阻塞
所以随着丢包率的增加,HTTP/2的传输效率也会越来越差。有测试数据表明,当系统达到了2%的丢包率时,HTTP/1.1的传输效率反而比HTTP/2表现得更好
2. TCP建立连接的延时
TCP握手的过程也是影响传输效率的一个因素
我们先来看下网络延迟的概念
网络延迟又称为RTT(Round Trip Time)。
我们把从浏览器发送一个数据包到服务器,再从服务器返回数据包到浏览器的整个往返时间称为RTT(如下图)。RTT是反映网络性能的一个重要指标
那么我们再来看下,TCP连接需要花费多少RTT
我们都知道,TCP连接需要进行三次握手,那么一共就是 1.5RTT
进行 TLS连接的时候,大概是需要1~2个RTT
那么就是说,光是整个连接的过程,都需要花费 3~4RTT
3. TCP协议僵化
上面说了这么多 TCP 的问题,那么是不是可以通过优化 TCP 来解决问题呢?
答案是:非常难
第一个原因是中间设备的僵化,几乎所有的网络中间件还有软件,都使用了大量的 TCP 特性,如果贸然更新,旧设备可能出现大量无法识别的数据
第二个原因是操作系统,因为TCP协议都是通过操作系统内核来实现的,应用系统只能用,不能修改
QUIC协议
基于以上TCP的问题,我们基本上不可能通过修改 TCP协议来解决问题
那么就只能发明一种新的协议,但是这也同样面临着巨大的挑战,以及TCP协议设备之间的僵化
于是HTTP/3 选择了一个折衷的方法,通过改造 UDP 协议实现类似 TCP的多路复用,传输可靠性,我们把这套协议叫做 QUIC协议
HTTP/3的QUIC集合了以下功能
- 实现了类似TCP的流量控制,传输可靠性功能
- 集成了TLS加密,减少了握手所花费的 RTT个数
- 实现了 HTTP/2中的多路复用,实现了数据流的单独传输,也就解决了TCP中的队头阻塞问题
- 实现了快速握手功能,由于QUIC是基于UDP的,所以可以实现 0RTT或者1RTT建立连接,这意味着QUIC能以最快的速度发送和接收数据,能够大大提升页面的打开速度
HTTP/3的挑战
通过上面的分析,我们相信,从技术层面上看,HTTP/3确实是个完美的协议
不过要将HTTP/3在互联网中迅速推广和应用,还面临着不少的挑战
- 从目前来看,服务器端和浏览器端都没有对 HTTP/3提供比较完整的支持
- 部署存在较大问题,因为系统内核对UDP的优化远远没有达到TCP的地步,这也是阻碍QUIC的一个重要原因
- 中间设备僵化的问题,中间设备对UDP的优化远低于TCP,当前设备使用QUIC协议会产生大量的丢包率
从标准制定到实践再到协议优化还需要走很长一段路,任重而道远
各版本核心迭代对比
版本 | 核心突破 | 性能瓶颈 |
---|---|---|
0.9 -> 1.0 | 功能扩展(Header/MIME 类型) | TCP连接效率 |
1.0 -> 1.1 | 持久化连接/缓存优化 | 队头阻塞/头部冗余 |
1.1 -> 2 | 二进制协议/多路复用 | TCP层队头阻塞 |
2 -> 3 | QUIC协议/UDP传输 | 协议兼容/基础设置支持度 |
总结
HTTP从诞生以来,一共经历了 0.9、1.0、1.1、2、3 这五个大版本
HTTP/0.9
最初HTTP的发明,仅仅是为传输基本的超文本HTML文件
HTTP/1.0
伴随着互联网拨号上网的出现,浏览器的更迭,基础的HTML文件已经不能满足互联网的需求,更多的资源类型都需要能够从HTML上面下载,HTTP/1.0应运而生,在HTTP/0.9的基础上支持了多类型文件,同时支持了请求头和状态码的配置
HTTP/1.1
解决完多类型文件下载的需求,HTTP的传输速度又开始面临着挑战
其中最大的问题莫过于每一次通信,都需要经历TCP 的三次握手,四次挥手
在HTTP/1.1中为解决这个问题,诞生了 持久化连接,默认允许最多支持6个长连接
在连接明确结束之前,可以进行多次通信,一定程度解决了传输速度的问题
同时在1.1版本中,支持了 Cookie
HTTP/2
HTTP/1.1在引入持久化连接的过程中,同时带来了带宽竞争和慢启动的问题,以及基于TCP协议无法避免的队头阻塞问题。为了解决带宽竞争和慢启动,HTTP/2将默认的6个长连接减少为1个,成功解决了带宽竞争的问题,也使得慢启动只需要经历一次。同时提出并落地了多路复用的技术方案,在通信过程中,无需等待上一个结果返回,请求可以批量发送到服务器
同时在2版本中支持了服务器推送,头部压缩
HTTP/3
HTTP/2虽然从应用层解决了TCP的队头阻塞问题,但是一旦有丢包问题的出现,TCP的传输还是会受到阻塞,传输效率会受到极大影响。
同时TCP三次握手造成的性能影响,也愈发明显。
但是碍于TCP协议,设备僵化,从底层去优化TCP协议是一个不可能完成的事情
于是基于 UDP的QUIC协议应运而生,QUIC在基于UPD的基础上,支持了多路复用,集成了TLS,实现了快速握手,多路数据流等功能,有效的解决了上面提到的一系列问题
HTTP/3挑战
HTTP/3虽然在现在看来是一个比较完美的协议
但是由于上面提到过的 TCP协议和设备僵化,各个网络中间设备对 TCP和UPD的支持和兼容也存在较大的差异,HTTP/3的推广还面临着不少的挑战