HTTP协议发展过程
HTTP(Hypertext Transfer Protocol)是用于在网络上传输超文本的协议,它经历了多个版本的演进,每个版本都带来了改进和优化,以解决不同阶段出现的问题。
# HTTP/1.0
发布年份:1996 年。
特点和问题: HTTP/1.0
是 HTTP 的最初版本,它是一个简单的协议,用于在客户端和服务器之间传输 HTML 页面。然而,它存在一些性能和效率问题,例如每次请求都需要建立新的 TCP 连接,这增加了延迟和资源浪费。
优化和解决方案: HTTP/1.1
引入了持久连接,允许在单个 TCP 连接上发送多个请求和响应,减少了连接建立的开销。此外,HTTP/1.1
还引入了虚拟主机支持,缓存管理等改进。
# HTTP/1.1
发布年份:1999 年。
HTTP/1.1
解决了HTTP/1.0
的一些性能问题,但它仍然存在一些限制。例如可以复用同一条 TCP 连接,可以减少 TCP 连接握手所耗费的时间。
但是当多个并行请求在同一个连接上排队时,可能会导致"队头阻塞"问题。
队头阻塞: 现实例子是只有单行道的高速公路。在这条路上发生一起车祸,可能会使整个通道堵塞很长一段时间。队头阻塞一直是 Web 传输中最难解决的问题之一,后续 HTTP 版本中主要为了解决这个问题而提出新的概念。
回到 Web 概念中,“队头阻塞”会导致某文件在传输过程中延迟或者丢包的问题,导致后续请求中需要延迟等待。例如在请求 js 文件过程中,因为js
的文件过大,导致后续的css
请求需要等待。但有人就说,可以优先请求较小css
,然后再请求较大的js
文件。但很可惜,浏览器无法预先知道这两个文件中的哪一个在请求时会成为更大的文件。因为在HTML
中无法指定这个文件有多大。
那么当时聪明的开发者为了优化网页的加载速度,利用HTTP /1.1
可以为不同的域名建立多个TCP连接
(一般是 6 个),将请求分布到不同的连接上,就可以“暂时”解决队头阻塞的问题。但这不是长久之计,因为建立TCP连接
也是昂贵的操作(加上TLS
的加密计算和握手,将更恐怖),每个连接又回到了HTTP/1.0
的时代。😭😭
本质上HTTP/1.1
的队头阻塞,是因为HTTP/1.1
协议无法将文件分拆成更小的块或者片所导致的。
优化和解决方案:HTTP/2.0
引入了二进制分帧,头部压缩,多路复用等技术,以解决队头阻塞问题,提高了性能和效率。
# HTTP/2.0
发布年份:2015 年。
目标:HTTP/2.0
的出现就是要复用一条TCP
连接,并解决队头阻塞的问题。
在HTTP/1.1
因为无法将文件分拆成更小的块,因为它不知道这个块属于哪个资源,从哪里开始,哪里结束。但HTTP/2.0
可以,HTTP/2.0
在每个资源块上添加了帧(frames),这个帧被放置在每个块的前面,它记录了两个信息:
- 这个块属于哪个资源,也就是流 id
- 块的大小是多少
那么后续通过流 id 将这些块组合起来,根据每个块的大小和请求的Content-length
就知道该请求是否完成。
根据以上的特性,HTTP/2.0
就能在一条 TCP 连接上解决 HTTP 得队头阻塞的问题。
你以为到这里就结束了吗? HTTP/3.0
为什么要出现。
其实确实已经结束了,但是是在HTTP协议
的层面,HTTP
的队头阻塞得以解决,但是 HTTP 是基于TCP协议
的,TCP
也有自己的队头阻塞,以及TLS
也有!
优化和解决方案: HTTP/3.0
采用了基于UDP
的传输协议QUIC
,以解决握手延迟和其他性能问题,同时提供了更好的安全性。
# HTTP/3.0
发布年份:预计在 HTTP/2.0 之后的几年内发布。目前接近正式标准化。
**目标:**解决 TCP 队头阻塞。
首先,我们了解一下TCP
的特点: TCP
是点对点,可靠的(丢失重传,按序到达)
正因为TCP
是可靠的,按序到达的,当传输过来的字符是 1, 2, 3,如果 2 丢失了,那么 3 就会被TCP
放入缓冲区中,等待 2 的传输再传递给HTTP
。HTTP/2.0
是基于TCP
协议的,没办法改变这个特性。
那么在HTTP/3.0中
另辟蹊径,在UDP
中实现一个新的协议—QUIC
,这个协议虽然在UDP
上运行,但需要解决UDP
不可靠的特性,所以它自己实现了一个类TCP
,所以也可以叫QUIC
为TCP2.0
,它包括 TCP
的所有特性(可靠性、拥塞控制、流量控制、排序等),同时它也集成了TLS
协议。
那它如何解决 TCP的
队头阻塞呢? 其实主要还是使用了HTTP/2.0
数据帧的算法,把HTTP/2.0
的数据帧下移到传输层的QUIC
协议中。然后把上层的HTTP
协议给简化了,把数据帧的算法全部删除,直接交给底层的QUIC
来控制。
让我们考虑一下如果 QUIC
数据包 2 丢失,而 1 和 3 到达会发生什么。与 TCP 类似,数据包 1 中流 1 的数据可以直接传递到浏览器。然而,对于数据包 3,QUIC
可以比 TCP
更聪明。它查看流 1 的字节范围,发现这个流帧完全遵循流 id 1 的第一个流帧。它可以立即将这些数据提供给浏览器进行处理。然而,对于流 id 2,QUIC
确实看到了一个缺口。它将保存该流帧,直到 QUIC
数据包 2 的重传到达。
再来一个例子
数据包 1 丢失,但 2 和 3 到达。QUIC
知道它已经接收到流 2 的所有预期数据,并将其传递给浏览器,只保留流 1。对于这个例子,QUIC
确实解决了 TCP
的队头阻塞。
但这样会造成这样的后果:QUIC
数据可能不再以与发送时完全相同的顺序发送到浏览器。
# 其他更多问题
# HTTP 3.0 真的比 HTTP2.0 快吗?
其实不一定,其实HTTP3.0
主要是解决TCP
的队头阻塞的问题,但这种在实际情况中,丢包率十分频繁的时候,HTTP3.0
的效果才能展现出来,其实这种情况已经很极端了。
# TLS 的队头阻塞
在TCP
中,TLS
是对整个数据去解密,当最后一个TCP包
丢失的时候,就要等最后一个TCP包
回来的时候才能解密,那这个时候TLS
就被阻塞住了。
QUIC
已经集成了TLS
功能,每个数据帧的时候就会通过TLS
加密。但是加密算法对于CPU
来说是非常耗时的,这也是为什么 QUIC
在当前实现中仍然比 TCP
慢的主要原因之一。