HTTP发展史(浏览器工作原理与实战笔记)

124 阅读12分钟

一、HTTP/1

(1)超文本传输协议HTTP/0.9

诞生于1991年,主要用于学术交流,用来在网络之间传输HTML超文本内容,所以被称为超文本传输协议

客户端发起请求,服务端返回数据~

HTTP/0.9的请求流程如下:

db1166c68c22a45c9858e88a234f1a34.webp

  • 因为HTTP都是基于TCP协议的,客户端先通过三次握手建立TCP连接
  • 建立连接之后,发送一个get请求行的信息,如GET /index.html用来获取 index.html
  • 服务端收到请求消息之后,读取对应的HTML文件,并将数据以ASCII字符流返回给客户端
  • 客户端接收完成,四次挥手断开连接

由于当时的需求是传输体积很小的HTML文件,所以当时的协议也比较简单:

  • 只有一个请求行,没有请求头和请求体
  • 响应没有响应头信息
  • 返回的数据是ASCII字符流来出传输

(2)、HTTP/1.0

万维网的高速发展以及浏览器的出现使得http/0.9已经不适应于新兴网络的发展,比如浏览器中展示的不单是HTML文件了,还包括了JavaScript、CSS、图片、音频、视频等文件,因此支持多种类型的文件下载是 HTTP/1.0 的一个核心诉求,而且文件格式不仅仅局限于 ASCII 编码,还有很多其他类型编码的文件,这些就是http/1.0诞生的原因;

时成立了万维网和http工作组,分别致力于HTML的发展和http的改进;

  • 如何支持类型的数据传输

TTP/1.0 的方案是通过请求头和响应头来进行协商,在发起请求时候会通过 HTTP 请求头告诉服务器它期待服务器返回什么类型的文件、采取什么形式的压缩、提供什么语言的文件以及文件的具体编码。比如:

accept: text/html
accept-encoding: gzip, deflate, br
accept-Charset: ISO-8859-1,utf-8
accept-language: zh-CN,zh
  • 引入状态码

  • 为了减轻服务器压力,引入了Cache机制

  • 用户代理User-Agent

User-Agent:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36

(3)http/1.1

随着需求的推进和技术发展,http/1.0也不能满足当时的需求,因此有推出了http/1.1。

  • 改进持久机制

HTTP/1.1 中增加了持久连接的方法,它的特点是在一个 TCP 连接上可以传输多个 HTTP 请求,只要浏览器或者服务器没有明确断开连接,那么该 TCP 连接会一直保持。

HTTP 的持久连接可以有效减少 TCP 建立连接和断开连接的次数,这样的好处是减少了服务器额外的负担,并提升整体 HTTP 的请求时长。

持久连接在 HTTP/1.1 中是默认开启的,所以你不需要专门为了持久连接去 HTTP 请求头设置信息,如果你不想要采用持久连接,可以在 HTTP 请求头中加上Connection: close。目前浏览器中对于同一个域名,默认允许同时建立 6 个 TCP 持久连接。

  • http管线化 队头阻塞: 持久连接虽然能减少 TCP 的建立和断开次数,但是它需要等待前面的请求返回之后,才能进行下一次请求。如果 TCP 通道中的某个请求因为某些原因没有及时返回,那么就会阻塞后面的所有请求。

HTTP/1.1 中试图通过管线化的技术来解决队头阻塞的问题。HTTP/1.1 中的管线化是指将多个 HTTP 请求整批提交给服务器的技术,虽然可以整批发送请求,不过服务器依然需要根据请求顺序来回复浏览器的请求。

FireFox、Chrome 都做过管线化的试验,但是由于各种原因,它们最终都放弃了管线化技术。

  • 提供虚拟主机的支持(HOST字段)

在 HTTP/1.0 中,每个域名绑定了一个唯一的 IP 地址,因此一个服务器只能支持一个域名。但是随着虚拟主机技术的发展,需要实现在一台物理主机上绑定多个虚拟主机,每个虚拟主机都有自己的单独的域名,这些单独的域名都公用同一个 IP 地址。

  • 动态内容的支持(Chunk transfer 机制)

http/1.0需要在响应头中Content-Length设置响应的数据大小,浏览器根据设置的大小来接收数据。但是随着需求的发展,很多内容是动态生成的,因此在传输中并不知道数据的大小,导致浏览器不知道何时才认为接收完数据~

HTTP/1.1 通过引入 Chunk transfer 机制来解决这个问题,服务器会将数据分割成若干个任意大小的数据块,每个数据块发送时会附上上个数据块的长度,最后使用一个零长度的块作为发送数据完成的标志。这样就提供了对动态内容的支持。

  • 客户端 Cookie

  • 安全机制

二、HTTP/2

虽然 HTTP/1.1 已经做了大量的优化,但是依然存在很多性能瓶颈,依然不能满足我们日益变化的新需求,所以就诞生了 HTTP/2。

(1)http/1.1的问题对带宽的利用率却并不理想

原因如下:

  • TCP 的慢启动

一旦一个 TCP 连接建立之后,就进入了发送数据状态,刚开始 TCP 协议会采用一个非常慢的速度去发送数据,然后慢慢加快发送数据的速度,直到发送数据的速度达到一个理想状态,我们把这个过程称为慢启动

慢启动是 TCP 为了减少网络拥塞的一种策略,我们是没有办法改变的。

所以一般会将HTML、CSS、JavaScript脚本,首先加载~

  • 同时开启了多条 TCP 连接,那么这些连接会竞争固定的带宽

有的 TCP 连接下载的是一些关键资源,如 CSS 文件、JavaScript 文件等,而有的 TCP 连接下载的是图片、视频等普通的资源文件,但是多条 TCP 连接之间又不能协商让哪些关键资源优先下载,这样就有可能影响那些关键资源的下载速度了。

  • HTTP/1.1 队头阻塞的问题

我们知道在 HTTP/1.1 中使用持久连接时,虽然能共用一个 TCP 管道,但是在一个管道中同一时刻只能处理一个请求,在当前的请求没有结束之前,其他的请求只能处于阻塞状态。这意味着我们不能随意在一个管道中发送请求和接收内容。

这是一个很严重的问题,因为阻塞请求的因素有很多,并且都是一些不确定性的因素,假如有的请求被阻塞了 5 秒,那么后续排队的请求都要延迟等待 5 秒,在这个等待的过程中,带宽、CPU 都被白白浪费了。

在浏览器处理生成页面的过程中,是非常希望能提前接收到数据的,这样就可以对这些数据做预处理操作,比如提前接收到了图片,那么就可以提前进行编解码操作,等到需要使用该图片的时候,就可以直接给出处理后的数据了,这样能让用户感受到整体速度的提升。

但队头阻塞使得这些数据不能并行请求,所以队头阻塞是很不利于浏览器优化的。

(2)HTTP/2.0的多路复用技术

慢启动和 TCP 连接之间相互竞争带宽是由于 TCP 本身的机制导致的,而队头阻塞是由于 HTTP/1.1 的机制导致的。

虽然 TCP 有问题,但是我们依然没有换掉 TCP 的能力,所以我们就要想办法去规避 TCP 的慢启动和 TCP 连接之间的竞争问题。

基于此,HTTP/2 的思路就是一个域名只使用一个 TCP 长连接来传输数据,这样整个页面资源的下载过程只需要一次慢启动,同时也避免了多个 TCP 连接竞争带宽所带来的问题。

对于队头阻塞的问题,等待请求完成后才能去请求下一个资源,这种方式无疑是最慢的,所以 HTTP/2 需要实现资源的并行请求,也就是任何时候都可以将请求发送给服务器,而并不需要等待其他请求的完成,然后服务器也可以随时返回处理好的请求资源给浏览器

HTTP/2 的解决方案可以总结为:一个域名只使用一个 TCP 长连接和消除队头阻塞问题

HTTP/2 最核心、最重要且最具颠覆性的多路复用机制。

HTTP/2 使用了多路复用技术,可以将请求分成一帧一帧的数据去传输,这样带来了一个额外的好处,就是当收到一个优先级高的请求时,比如接收到 JavaScript 或者 CSS 关键资源的请求,服务器可以暂停之前的请求来优先处理关键资源的请求。

(3)多路复用的实现

HTTP/2 添加了一个二进制分帧层,通过引入二进制分帧层,就实现了 HTTP 的多路复用技术,如下图所示

86cdf01a3af7f4f755d28917e58aae6a.webp

  • 首先,浏览器准备好请求数据,包括了请求行、请求头等信息,如果是 POST 方法,那么还要有请求体。
  • 这些数据经过二进制分帧层处理之后,会被转换为一个个带有请求 ID 编号的帧,通过协议栈将这些帧发送给服务器。
  • 服务器接收到所有帧之后,会将所有相同 ID 的帧合并为一条完整的请求信息。
  • 然后服务器处理该条请求,并将处理的响应行、响应头和响应体分别发送至二进制分帧层。
  • 同样,二进制分帧层会将这些响应数据转换为一个个带有请求 ID 编号的帧,经过协议栈发送给浏览器。
  • 浏览器接收到响应帧之后,会根据 ID 编号将帧的数据提交给对应的请求。

(4)HTTP/2.0的其他特性

  • 设置请求的优先级
  • 服务器推送

提前推送浏览器需要的文件

  • 头部压缩

三、HTTP/3

(1) TCP 的队头阻塞

虽然 HTTP/2 解决了应用层面的队头阻塞问题,不过和 HTTP/1.1 一样,HTTP/2 依然是基于 TCP 协议的,而 TCP 最初就是为了单连接而设计的。你可以把 TCP 连接看成是两台计算机之前的一个虚拟管道,计算机的一端将要传输的数据按照顺序放入管道,最终数据会以相同的顺序出现在管道的另外一头

在 TCP 传输过程中,由于单个数据包的丢失而造成的阻塞称为 TCP 上的队头阻塞。

,我们知道在 HTTP/2 中,多个请求是跑在一个 TCP 管道中的,如果其中任意一路数据流中出现了丢包的情况,那么就会阻塞该 TCP 连接中的所有请求。这不同于 HTTP/1.1,使用 HTTP/1.1 时,浏览器为每个域名开启了 6 个 TCP 连接,如果其中的 1 个 TCP 连接发生了队头阻塞,那么其他的 5 个连接依然可以继续传输数据。

4837434655a6d87f1bf5e3d899a698d1.webp

33d2b4c14a7a2f19ef6677696b67de96.webp

所以随着丢包率的增加,HTTP/2 的传输效率也会越来越差。有测试数据表明,当系统达到了 2% 的丢包率时,HTTP/1.1 的传输效率反而比 HTTP/2 表现得更好。

(2)TCP 建立连接的延时

。网络延迟又称为 RTT(Round Trip Time)。我们把从浏览器发送一个数据包到服务器,再从服务器返回数据包到浏览器的整个往返时间称为 RTT(如下图)。RTT 是反映网络性能的一个重要指标。

e98927e19b20349815fb8f499067cb4f.webp

那建立 TCP 连接时,需要花费多少个 RTT 呢?

下面我们来计算下。我们知道 HTTP/1 和 HTTP/2 都是使用 TCP 协议来传输的,而如果使用 HTTPS 的话,还需要使用 TLS 协议进行安全传输,而使用 TLS 也需要一个握手过程,这样就需要有两个握手延迟过程。

  • 在建立 TCP 连接的时候,需要和服务器进行三次握手来确认连接成功,也就是说需要在消耗完 1.5 个 RTT 之后才能进行数据传输。
  • 进行 TLS 连接,TLS 有两个版本——TLS1.2 和 TLS1.3,每个版本建立连接所花的时间不同,大致是需要 1~2 个 RTT,关于 HTTPS 我们到后面到安全模块再做详细介绍。

总之,在传输数据之前,我们需要花掉 3~4 个 RTT。

(4)、QUIC 协议

HTTP/3 选择了一个折衷的方法——UDP 协议,基于 UDP 实现了类似于 TCP 的多路数据流、传输可靠性等功能,我们把这套功能称为 QUIC 协议

0bae470bb49747b9a59f9f4bb496a9c6.webp

HTTP/3 中的 QUIC 协议集合了以下几点功能:

  • 实现了类似 TCP 的流量控制、传输可靠性的功能
  • 集成了 TLS 加密功能
  • 实现了 HTTP/2 中的多路复用功能

05cc5720989aec75730ee4cb7e7c149a.webp

  • 实现了快速握手功能 由于 QUIC 是基于 UDP 的,所以 QUIC 可以实现使用 0-RTT 或者 1-RTT 来建立连接,这意味着 QUIC 可以用最快的速度来发送和接收数据,这样可以大大提升首次打开页面的速度。