概述

HTTP 协议一共有四个阶段比较重要的阶段,分别是 0.9/1/2/3,其中1又分为1.01.1

协议规定了什么

一、HTTP/0.9 服务于简单的 HTML 文件传输

HTTP/0.9 只能用来传输体积很小的 HTML 文件,采用的是ASCII 字节码来编码

请求和相应的格式都很简单,请求只有一个请求行,相应部分也只有一个相应体

二、HTTP/1.0 能支持多种文件类型

因为 HTTP/1.0 支持了更多样的文件格式,在发送请求时需要标注出来“我需要的文件类型、文件编码...”,请求头应运而生;服务器收到请求后不一定可以按照指定的要求准备数据,所以会在响应里标注数据最终的组织形式,于是有了响应头

请求结构

// 请求行
GET /4848 HTTP/1.0
// 请求头
Connection: Keep-Alive
User-Agent: Mozilla/3.01 (X11; I; SunOS 5.4 sun4m)
Pragma: no-cache
Host: tecfa.unige.ch:7778
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*

相应结构

// 响应行——通过其中的的状态码,报告服务器的最终处理情况
HTTP/1.0 200 ok
// 响应头
Date: Wed, 16 Apr 97 22:54:42 GMT
Server: E_WEB (E_MOO-Web-Server/1.2d - Not WOO) (LambdaMOO 1.8.0p5)
Content-type: text/html
// 响应体
<title>Query results</title>
<h1>Query results</h1>
...

常见请求头、响应头的具体含义

浏览器会用请求头告诉服务器它想要的文件类型、文件编码、压缩形式、文件语言...

服务器会用响应头告诉浏览器最终的处理结果

  1. 文件类型

除了 HTML,还有 Javascript/CSS/图片/音频/视频

// 请求头
accept: application/json, text/plain, */*
// 响应头
content-type: application/json
  1. 文件压缩格式

为了减轻传输压力,服务器会对数据进行压缩后再传输,所以浏览器需要知道服务器压缩的方法,通常针对 CSS/JS 文件

// 请求头
accept-encoding: gzip, deflate, br
// 响应头
content-encoding: gzip
  1. 文件编码格式

不同类型的文件,编码形式会不一样,为了能够准确地读取文件,浏览器需要知道文件的编码类型

// 请求头
accept:text/plain; charset=utf-8
// 响应头
content-type: application/json; charset=utf-8
  1. 缓存

HTTP/1.0 提供了缓存机制,用来缓存已经下载过的数据

// 请求头 响应头
cache-control: no-store, no-cache, must-revalidate
  1. 客户端的基础信息

通过UA,可以知道浏览器的版本、操作系统等信息

// 请求头
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)

三、HTTP/1.1 开始支持持久连接

HTTP 请求是建立在 TCP 请求之上的,所以需要经历 TCP 连接的建立和断开。

在 HTTP1.1 之前,每一个 HTTP 请求都建立在一个 TCP 连接之上,増加大量无谓的开销。

同样发起三个请求,两者的时间对比:

持久连接通过请求头控制。不做特殊处理时,会默认保持持久连接。

// 请求头
connection:keep-alive

可以通过以下方式关闭持久连接

// 请求头
connection:close

此外 HTTP/1.1 还引入了其他特性

  1. 虚拟主机

随着虚拟主机技术的发展,我们希望在用一台物理主机上去映射多个虚拟主机:每个虚拟主机都有自己的域名,但这些单独的域名都指向同一个 IP 地址。

HTTP/1.1 在请求头中增加了 Host 字段,用来表示当前请求的域名地址,这样服务器就可以根据不同的域名做不同的处理

// 请求头
Host: nian.frontend.com
  1. 支持动态生成内容

随着服务器端的技术发展,很多页面的内容都是动态生成的,因此在传输数据之前并不知道最终的数据大小,这就导致了浏览器不知道何时会接收完所有的文件数据。此前响应头会通过Content-Length去设置完整的数据大小。

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

// 响应头
Transfer-Encodeing: chunked
  1. 管线化尝试

HTTP/1.1 中试图通过管线化技术来解決队头阻塞。

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

  1. 客户端 Cookie

Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,浏览器之后向同一服务器再次发起请求时会携带上该cookie,用于告知服务端两个请求是否来自同一浏览器。

// 响应头
Set-Cookie: yummy_cookie=choco
// 请求头
Cookie: yummy_cookie=choco;

四、HTTP/2.0 实现多路复用

HTTP/1.1 实现了持久连接,但每一个长连接,在一个时间点下,只有一个 HTTP 请求在传输。这就导致带宽利用很不充分。比如我们常说的 100M 带宽,实际的下载速度理论上能达到 12.5M/S,但实际在加载页面资源时可能只到 2.5M/S。

这种带宽利用不充分的原因有三个,TCP 的慢启动,TCP 竞争和队头阻塞

HTTP/2.0 采用多路复用去解决 HTTP 队头阻塞,使请求并行发出。

多路复用的实现:二进制分帧

大家使用同一个 TCP 连接,但每个HTTP请求都有一个唯一的请求编号用于互相区分。

所有发送的信息,会经过二进制分帧层处理,被转換为一个个带有对应请求编号的帧

接收方接收到所有帧之后,会将编号相同的帧合并,成为一条完整的信息

此外 HTTP/2.0 还引入了其他特性

  1. 资源优先级

多路复用技术把请求分成一帧一帧的传输,带来的额外好处就是:收到高优先级请求时,可以暂停其他的,优先处理关键资源

  1. 服务器推送

服务器推送,对首次打开页面的速度,起到了至关重要的作用。

当浏览器请求ー个 HTML 页面之后,服务器知道该它会引用几个重要的 JS/CSS 文件。所以会一并发送给浏览器,这样当浏览器解析完 HTML 文件之后,就能直接拿到需要的 CSS 文件和 Javascript 文件,

  1. 头部压缩

浏览器和服务器将反复的头部字段维护起来,成为一个字典,实际传输时,只用带上简短的键值。

五、HTTP/3.0 采用UDP协议

在开始了解3.0之前,先详细看一看2.0存在的问题:TCP 的队头阻塞和 TCP 握手时间过长

什么是队头阻塞

对头阻塞分为http队头阻塞和TCP队头阻塞

  1. http 的队头阻塞是指,客户端收到上一个 HTTP 请求的响应,才能发送下一个请求。在上文讲述的 HTTP/2.0已经解决。
  2. TCP 的队头阻塞是指,因为数据包是有序传输,中间任何一个数据包丢失,都要等待重传,造成后面的数据包的阻塞。

随着丟包率的增加,HTTP/2.0 的传输效率也会越来越差。

有测试数据表明,当系统达到了 2%的丢包率时,HTTP/1 的传输效率反而比 HTTP/2 表现得更好

TCP 握手时间过长

在建立 TCP 连接的时候,需要和服务器进行三次握手来确认连接成功

如果使用 HTTPS 的话,还需要 TLS 握手过程,这样就需要有两个握手延迟过程。

在这样的背景下,提出了HTTP/3.0。它基于 QUIC 协议。

网络性能优化 还需要吗?

HTTP/2.0 的多路复用带来的影响是巨大的,从网络开发者工具的网络请求可以直观看出。

  • 在 1.1 版本下,请求被阻塞的时间很长(前面灰色部分),正是因为 TCP

    连接一次只能服务于一个 HTTP/1.1 的请求。
  • 而在在 2.0 版本时,请求可以被及时响应

在 2.0 时代,一些曾经的优化手段会适得其反。

  1. 文件合并

之前,我们会采用 JS 文件合并、雪碧图等方式,减少 HTTP 请求的数量,达到优化目的。因为在 1.1 时代的 TCP 只能串行复用,但现在可以并行传输。

假如文件 a,b,c 合并,资源 a 的单独更新,会导致本地缓存合并文件 需要整体更新。而不压缩文件,能以更小的颗粒度更新资源,反而是更好的选择。

  1. 域名分片技术

之前,由于浏览器对同域名的 TCP 连接数量有限制,我们通常会把文件放在几个不同的域名下,防止超过连接数量的 HTTP 请求被阻塞。

但有了多路复用,放在同一个域名下文件可以被同时下载,并且只有一个域名,我们只要建立一个 TCP 连接。

03-05 22:24