Skip to content

HTTP的历史

超文本传输协议(英语:HyperText Transfer Protocol,缩写:HTTP)是万维网(World Wide Web)的基础协议。自蒂姆·伯纳斯-李 (Tim BernersLee)博士和他的团队在 1989-1991 年间创造出它以来,HTTP 已经发生了太多的变化,在保持协议简单性的同时,不断扩展其灵活性。如今,HTTP 已经从一个只在实验室之间交换文档的早期协议进化到了可以传输图片,高分辨率视频和 3D 效果的现代复杂互联网协议。

HTTP的诞生

1989 年 3 月欧洲核子研究组织(CERN)的蒂姆·伯纳斯-李 (Tim BernersLee)博士提出了一种能让远隔两地的研究者们共享知识的设想。 最初设想的基本理念是:借助多文档之间相互关联形成的超文本(HyperText),连成可相互参阅的 WWW(World Wide Web,万维网)。

proposal

到 1990 年 10 月,Tim 编写了三项基本技术来实现设想,这些技术仍然是当今网络的基础(您可能已经在网络浏览器的某些部分上看到过这些技术):

  • HTML(HyperText Markup Language):超文本标记语言,作为编写文档的语言。
  • HTTP(HyperText Transfer Protocol):超文本传输协议,作为传递文档的协议。
  • URL(Uniform Resource Locator):统一资源标识符,一种唯一的“地址”,用于标识文档在网络上的位置。

此外 Tim 还编写了第一个网页编辑器/浏览器(“WorldWideWeb.app”)和第一个 Web 服务器(“httpd”)。至此 Tim 初步完成了他的设想的所有技术实现,且第一批服务器已经在 1991 年初在 CERN 以外的地方运行了,1991 年 8 月 16 日,Tim Berners-Lee 在公开的超文本新闻组上发表的文章被视为是万维网公共项目的开始。

对于HTTP而言, 在应用的早期阶段它是非常简单的,后来它也被称为 HTTP/0.9,有时也叫做单行hang(one-line)协议。

HTTP/0.9——单行协议(1991)

最初版本的 HTTP 协议并没有版本号,后来它的版本号被定位在 0.9 以区分后来的版本。HTTP/0.9于 1991 年提出。它是有史以来最简单的协议;它的请求由单行指令构成(因此也称为单行协议),以唯一可用方法 GET 开头,其后跟目标资源的路径(一旦连接到服务器,协议、服务器、端口号这些都不是必须的)。

http
GET /index.html

响应也极其简单的:只包含HTML文档本身。

html
<HTML>
这是一个非常简单的 HTML 页面
</HTML>

跟后来的版本不同,HTTP/0.9 的响应内容并不包含 HTTP 头。这意味着只有 HTML 文件可以传送,无法传输其他类型的文件。也没有状态码或错误代码。一旦出现问题,一个特殊的包含问题描述信息的 HTML 文件将被发回,供人们查看。

特性

  • 它是 ASCII 协议,请求/响应都是由ASCII字符组成字符串。

  • 它在TCP/IP 链路上运行。

  • 请求以回车符 (CRLF) 终止。

  • 响应只包含HTML文档。

  • 文档传输完成后,TCP连接将终止。

缺陷

  • 只支持GET请求: HTTP/0.9仅支持GET方法,意味着只能用于获取资源,不能用于其他类型的请求,如POST、PUT、DELETE等。这导致在处理复杂的应用逻辑和实现数据更新等操作时,HTTP/0.9显得非常有限。
  • 只能传输HTML: HTTP/0.9的响应只能包含HTML文档,无法处理其他类型的数据,如JSON、XML、图像等。这限制了其在处理现代Web应用程序中的数据传输和交互能力。
  • 无法进行内容协商: HTTP/0.9没有头部信息,无法携带元数据,如Content-Type、Content-Length等,这使得它无法识别并正确解析其他响应内容。
  • 没有状态码或错误代码: 也是由于HTTP/0.9没有头部信息,无法携带元数据,因此响应成功与失败都是返回HTML文档,这使得浏览器不能知晓请求执行成功或失败,并相应调整行为。
  • 不支持持久连接: 在HTTP/0.9中,每次请求都会建立一个新的TCP连接,请求完成后立即关闭连接。这导致在处理大量请求时,频繁地建立和关闭连接会带来较大的开销,影响性能。
  • 安全性问题: HTTP/0.9没有提供任何加密和安全机制,所有的数据都是明文传输。这使得数据容易受到窃听和篡改,缺乏对隐私和数据完整性的保护。
  • 只能传输英文文本数据: HTTP/0.9默认采用的字符集是ASCII字符集,这意味着HTTP只能传输英文文本数据,无法支持其他语言的文本数据,比如包含非英文字符的文本(如中文、日文、俄文等)。

正如你们所看到的,HTTP/0.9仅适用于简单的、仅需要获取HTML文档的场景。新兴 Web 所需功能及其在公共 Web 上的用例不断增长,很快就强化了 HTTP 0.9 的许多缺陷:我们需要一个协议,该协议不仅可以服务于超文本文档,还可以提供有关请求和响应的更丰富的元数据。

很快,HTTP 的下一个版本(即 HTTP/1.0)被推出,它解决了HTTP/0.9的缺陷,并提供更多强大的功能和性能优化。

HTTP/1.0——构建可扩展性(1996)

1996 年 5 月,HTTP 工作组 (HTTP-WG) 发布了 RFC 1945,文档 RFC 1945 定义了 HTTP/1.0,但它是狭义的,并不是官方标准。

HTTP/1.0 通过定义了HTTP请求/响应的结构,加入许多头部信息,现在可以处理其他响应格式,即图像、视频文件、纯文本或任何其他内容类型。它添加了更多方法(即 POST 和 HEAD)、添加了状态代码来标识响应、引入了字符集、类型、授权、缓存、内容编码等内容。

以下为 HTTP/1.0 的请求示例:

http
GET / HTTP/1.0
Host: cs.fyi
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5)
Accept: */*

以下为 HTTP/1.0 的响应示例:

http
HTTP/1.0 200 OK 
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84

(response body)
(connection closed)

HTTP/1.0 的请求与响应现在看起来也是非常熟悉的,因为它首次定义了请求/响应的格式我们沿用至今,并且由于加入头信息,使得内容协商变得容易起来,这也极大的丰富了它的扩展性,虽然请求和响应标头仍保留为 ASCII 编码,但响应正文可以是任何类型,即图像、视频、HTML、纯文本或任何其他内容类型。因此,现在服务器不仅仅只能向客户端发送HTML文档,还可以发送其他任何内容类型;推出后不久,HTTP 中的“超文本”一词就变得用词不当。HMTP 或超媒体传输协议可能更有意义,但我想,我们终生都坚持这个名字。

请求与响应头仍是ASCII编码,那么其中的中文是如何处理的呢?

我们都知道ASCII编码仅表示了英文小大写、数字以及一些标点符号,对于其他国家的语言是并不支持的,那么请求头中的其他国家的语言的信息如何处理呢?

我尝试使用浏览器访问以中文命名的HTML,URL:wangjunliang.com/我想看看.html

http
请求网址:
https://www.wangjunliang.com/%E6%88%91%E6%83%B3%E7%9C%8B%E7%9C%8B.html
请求方法:
GET
状态代码:
200
远程地址:
127.0.0.1:7890
引荐来源网址政策:
strict-origin-when-cross-origin

惊奇的发现在请求头中我想看看转换为了%E6%88%91%E6%83%B3%E7%9C%8B%E7%9C%8B,这就是传说中的URL编码。

URL编码: URL 编码是一种将特殊字符和非 ASCII 字符转换为 ASCII 字符的方法,使它们能够在 HTTP 请求的 URL 中进行传输。具体而言,URL 编码使用 "%" 符号后跟两个十六进制数字来表示一个字符。例如,中文字符 "中" 的 URL 编码表示为 "%E4%B8%AD"。这个编码表示了字符 "中" 在 ASCII 字符集中的十六进制表示。当 HTTP/1.0 的请求或响应中包含中文字符时,这些字符会被转换成对应的 URL 编码形式。服务器收到 URL 编码后的请求后,会将其还原为原始的中文字符进行处理。需要注意的是,HTTP/1.0 的 URL 编码仅适用于 URL 中的字符编码。

神奇的base64——在ASCII上传输任何内容

在HTTP的早期版本,如HTTP/0.9和HTTP/1.0,由于对字符集的限制以及内容协商机制缺失与不完善,直接传输二进制数据或非ASCII字符是有挑战的。为了在这些早期版本的HTTP中传输图片或其他二进制数据,人们使用了将二进制数据转换为ASCII字符的编码方式——base64。

base64编码: 是一种将二进制数据转换成ASCII码中64个字符(A-Z、a-z、0-9和+、/两个符号)的文本编码方式。

在Base64编码中,原始二进制数据每3个字节的二进制数据被分成4组,每组6位,然后分别转换成4个ASCII字符。也就是说原始数据每6位将会被转换成一个ASCII字符,而一个ASCII字符占用空间则是8位,因此编码后的数据大小会增加约1/3

原始数据(占用空间:24位)base64编码编码后二进制数据(占用空间:32位)
100100 101001 100100 101001kpkp01101011 01110000 01101011 01110000

如果待编码的二进制数据长度不是3的倍数,Base64编码会在末尾添加1或2个填充字符"=",以确保编码后的文本长度是4的倍数。这进一步增加了数据的大小。

特性

  • 定义了请求/响应格式: HTTP/1.0将请求/响应格式划分为了三个部分——起始行、头部信息、消息体,这个格式一直沿用至今。
  • 加入了状态码和状态描述: 在响应的起始行中加入了状态代码和状态的描述信息,提供了关于请求处理结果的信息,以便客户端和服务器能够进行适当的处理和决策。
  • 加入了内容协商: 虽然在HTTP/1.0中起始行与头部信息都保留为 ASCII 编码,但它通过加入了Content-Type、Content-Length、Transfer-Encoding等头部属性,可以对不同类型的文件在消息体中进行不同的编码。也就是说起始行、头部信息传输仍是ASCII编码,而消息体则会根据头部属性让客户端/服务器进行内容协商,进行不同的编码方式。
  • 可以传输任何文件: 由于HTTP/1.0加入了内容协商的机制,使得只要客户端/服务器协商一致,HTTP/1.0就可以传输任何形式的文件。
  • 新增POST、HEAD请求: POST请求方法允许客户端向服务器提交数据,而HEAD请求方法允许客户端获取资源的元数据。这些新增的请求方法丰富了HTTP协议的能力,使得客户端和服务器能够进行更多样化的交互和处理。
  • 不再只是传输英文文本数据: HTTP/1.0引入字符集(Character Set),解决了其他国家语言文本数据的字符编码问题,并确保文本数据能够以正确的方式被处理和显示。
  • 初步引入缓存概念: HTTP/1.0引入了头部字段如Expires和Last-Modified,用于控制缓存的行为,以及头部字段如If-Modified-Since和If-None-Match,用于条件性请求。
  • 初步引入持久链接: HTTP/1.0引入一个名为 Connection: keep-alive 的新标头,来保持请求建立起来的TCP连接,以供后续请求继续使用该链接完成请求。
  • 初步引入代理支持: HTTP/1.0引入了Proxy-Connection头部字段来指示代理服务器是否应保持持久连接,并引入了Via头部字段,用于标识请求经过的代理服务器链。

缺陷

  • 持久链接未得到广泛支持,默认仍为短连接: HTTP/1.0 的尝试通过引入一个名为 Connection: keep-alive 的新标头来克服短连接问题,但它仍然没有得到广泛的支持,问题仍然存在。

  • 请求阻塞: 在HTTP/1.0中,每个请求都需要按照顺序进行,即必须等待前一个请求的响应返回后才能发送下一个请求。如果前一个请求很耗时,会导致后续请求被阻塞,影响并发性能。

  • 无状态: HTTP是一种无状态协议,即服务器不维护有关客户端的信息,当客户端需要记录状态时,必须发送一些记录状态的冗余数据,从而导致带宽使用量增加。

  • 缺乏压缩支持: HTTP/1.0没有内置的数据压缩机制,因此在传输大量文本数据时,没有有效地压缩数据,增加了网络传输的开销。

  • 安全性问题: HTTP/1.0没有提供任何加密和安全机制,所有的数据都是明文传输。这使得数据容易受到窃听和篡改,缺乏对隐私和数据完整性的保护。

  • 实现混乱: 由于HTTP/1.0并不是官方标准,许多浏览器厂商并没有按照HTTP/1.0的指导实现HTTP,导致在实际运用中混乱不堪。

可以看到对于HTTP/0.9人们诟病的是它的文件类型支持不够丰富,由于它的文件类型支持不丰富,所有通常只是请求HTML文档,往往发送一次请求就能获取到完整的内容。而在HTTP/1.0丰富了文件类型后,一次请求不再能获取到全部内容,并且请求的内容也从较小的HTML文档发展到了图片、音频、视频等较大的文件,此时HTTP的性能问题也被暴露出来。

此外,由于各个浏览器相互竞争,各自为战,并且HTTP/1.0并不是官方标准,只是一些指导意见。这导致各个厂商对于HTTP都有各自的实现方式,导致在实际运用中混乱不堪。

由于上述原因,人们迫切需要优化HTTP性能,制定一份标准化HTTP协议!

HTTP/1.1——标准化的协议(1997)

HTTP/1.0 多种不同的实现方式在实际运用中显得有些混乱。自 1995 年开始,即 HTTP/1.0 文档发布的下一年,就开始修订 HTTP 的第一个标准化版本。 在 1997 年 1 月HTTP1.1 标准以 RFC 2068 文件发布,后续 HTTP/1.1 协议进行过两次修订,分别是RFC 2616 发布于 1999 年 6 月,以及 RFC 7230-RFC 7235 发布于 2014 年 6 月。

HTTP/1.1 标准消除了早期版本中大量歧义内容,并引入了许多关于性能优化的措施:持久链接、管道化技术、支持范围请求和部分响应、分块传输机制、明确缓存控制机制、增加压缩技术、增强内容协商机制、增加客户端cookie等。除了改进HTTP性能方面HTTP/1.1还新增状态码、引入了基本认证和摘要认证,提供更强大的用户认证机制,确保更安全的通信、 引入了 Host 头字段,该字段允许在同一个物理服务器上托管多个域名。这使得虚拟主机能够通过在 Host 头中指定域名来区分不同的网站、并且它还新增了许多请求方法,极大丰富了请求类型。

以下为HTTP/1.1的请求示例

http
GET /zh-CN/docs/Glossary/Simple_header HTTP/1.1
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/zh-CN/docs/Glossary/Simple_header

以下为HTTP/1.1的响应示例

http
200 OK
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Wed, 20 Jul 2016 10:55:30 GMT
Etag: "547fa7e369ef56031dd3bff2ace9fc0832eb251a"
Keep-Alive: timeout=5, max=1000
Last-Modified: Tue, 19 Jul 2016 00:59:33 GMT
Server: Apache
Transfer-Encoding: chunked
Vary: Cookie, Accept-Encoding

我们可以看到在请求/响应格式上HTTP/1.1与HTTP/1.0并无差异,这是因为HTTP/1.1沿用了HTTP/1.0的请求/响应格式。

特性

  • 持久化连接: 在HTTP/1.0中持久连接默认为关闭,并没有得到广泛应用,而在HTTP/1.1中连接默认情况下不会关闭,而是保持打开状态,从而允许多个顺序请求。要关闭连接,请求头 Connection: close 必须可用。客户端通常在最后一个请求中发送此标头以安全地关闭连接。

  • 管道化技术: HTTP/1.1引入了管道化技术,以解决请求阻塞问题,客户端可以在同一连接上向服务器发送多个请求,而无需等待服务器的响应,并且服务器必须按照接收请求的顺序发送响应。

  • 新增客户端Cookie: 由于HTTP是无状态协议,当客户端需要记录状态时,必须发送一些记录状态的冗余数据,从而导致带宽使用量增加。HTTP/1.1引入了客户端cookie以解决该问题,通过在浏览器中设置cookie,服务器可以跟踪用户会话状态,实现用户身份认证,个性化用户体验等。

  • 新增Gzip、Deflate等压缩技术: 在HTTP/1.1中,服务器可以使用Gzip、Deflate等压缩算法来压缩HTTP响应的实体主体部分(如HTML、CSS、JavaScript等),然后在响应头中使用"Content-Encoding"字段来指示客户端该响应已经被压缩以及压缩的算法。客户端收到压缩的响应后,会自动解压缩以获取原始内容。

  • 引入了基本认证和摘要认证机制: 在HTTP/1.1中,可以通过基本认证和摘要认证机制,在请求头中传递用户名和密码等凭据进行用户身份验证。

  • 引入了范围请求和部分响应机制: HTTP/1.1引入了范围请求和部分响应的功能,通过在HTTP请求头中使用"Range"字段指定所需的资源范围,而服务器在响应头中使用"206 Partial Content"状态码表明返回的是部分响应,并通过"Content-Range"字段指示返回内容的字节范围。这使得客户端可以请求大文件的特定部分,例如断点续传的情况下,从而避免重新下载整个文件。此外,范围请求还能让客户端只获取媒体资源的特定片段,优化数据传输并提升用户体验。

  • 引入了分块传输机制: HTTP/1.1引入了分块传输(Chunked Transfer Encoding)机制,用于在动态内容传输时,服务器无法提前确定整个内容的长度的情况下,逐块发送内容。在分块传输中,服务器将响应拆分为一系列块,每个块都有一个独立的大小,并使用"Transfer-Encoding: chunked"请求头来通知客户端有关分块传输的信息。客户端接收到该头信息后,知道响应将以分块的形式传输,它可以按照指定的块大小逐块接收内容,直到接收到一个长度为零的块,表示传输已完成。这种机制适用于动态生成内容、流式传输以及服务器长时间运行的响应等场景,提供了更灵活和高效的数据传输方式。

  • 明确缓存机制: HTTP/1.1在HTTP/1.0的基础上进一步明确了缓存机制,服务器可以通过设置响应头字段来控制缓存行为,例如使用"Cache-Control"头字段来指定缓存策略,如"max-age"用于设置资源的最大缓存时间,"no-cache"用于要求客户端验证资源的有效性等。同时,服务器也可以在响应头中添加"Expires"头字段,设置资源的过期时间,以便客户端在接收到资源后在过期时间之前可以直接使用缓存的副本。另外,HTTP/1.1还支持"Last-Modified"和"If-Modified-Since"头字段,以及"ETag"和"If-None-Match"头字段,用于在客户端缓存资源后,再次请求时验证资源是否已经发生变化,如果未变化,服务器返回304 Not Modified状态码,让客户端使用缓存的资源,从而避免重复传输。通过这些头字段的灵活组合使用,HTTP/1.1的缓存机制实现了更高效、可控的缓存管理,优化了Web应用程序的性能和用户体验。

  • 增强内容协商机制: HTTP/1.1增加了Accept-Language、Accept-Encoding和Accept-Charset等头字段,允许客户端明确指定其首选语言、内容编码方式和字符集,让服务器能够更好地提供适配客户端需求的内容。此外,HTTP/1.1还引入了Vary头字段,用于标明服务器响应可能因客户端请求头的不同而变化,这样确保代理服务器能够存储和提供正确的缓存内容。这些改进使得HTTP/1.1的内容协商机制更加强大和智能,提高了资源传输的效率和用户体验。

  • 添加了新的 HTTP 方法: HTTP/1.1新增了 PUT、PATCH、OPTIONS、DELETE方法。

  • 新增了Host 头字段: TTP/1.1 引入了 Host 头字段,该字段允许在同一个物理服务器上托管多个域名。这使得虚拟主机能够通过在 Host 头中指定域名来区分不同的网站。

  • 新增了状态码

缺陷

  • 线头阻塞(Head-of-Line Blocking): HTTP/1.1在同一连接上使用持久连接,但由于串行发送请求和响应,如果一个请求或响应的处理时间较长,那么后续的请求和响应将被阻塞,为此它引入了管道化技术(pipelining)试图解决该问题,但它并没有完全解决这个问题,因为即使在客户端请求选择某一管道并被异步发送出去,但在服务器如果该请求前面存在缓慢或繁重的请求,那么该请求就会被阻塞。这种情况也被称为线头阻塞
  • 无法处理较多的并发请求: 由于头阻塞问题和单个连接的限制,HTTP/1.1在处理较多的并发请求时表现较差。浏览器限制了同时与同一域名建立的连接数,针对同一域名的并发请求最多可以同时发送6到8个,如果有超过限制的请求需要发送,浏览器会将这些请求放入队列,从而限制了并发请求的数量。
  • 未能被充分利用的TCP: HTTP 1.1很难榨干TCP协议所能提供的所有性能。HTTP客户端和浏览器必须要另辟蹊径的去找到新的解决方案来降低页面载入时间。
  • 明文传输: HTTP/1.1默认是明文传输,数据在网络上传输时不加密,可能被窃听或篡改。这会导致安全隐患,尤其是对于敏感信息的传输。
  • 头部冗余: HTTP/1.1的请求和响应头部会携带一些冗余的信息,导致了较大的头部开销,特别是对于小的资源请求。
  • 没有对头字段进行压缩: HTTP/1.1没有对头字段进行压缩,尽管HTTP响应的主体可以使用Gzip等压缩算法,但头字段仍然是明文传输,可能在一些情况下浪费带宽。
  • 请求-响应模式: HTTP/1.1使用请求-响应模式,每个请求需要等待响应后才能继续。这种模式对于实时性要求较高的应用场景,如实时通信和流媒体,效率较低。
  • 缺乏推送功能: HTTP/1.1缺乏服务器主动向客户端推送资源的机制。客户端只能通过不断发送请求来获取资源,这导致了一定的延迟和额外的开销。

超过 25 年的逐步扩展

从HTTP/1.1协议发布至今,HTTP/1.1协议已稳定使用超过25年,目前大部分网站仍是基于HTTP/1.1来运行的。这期间HTTP/1.1也做了多次扩展与修改,并发展不同的应用模式,以弥补之前的缺陷以及满足日益进步的需求。

HTTP 用于安全传输

在1994年,网景通信公司(Netscape Communications Corporation)为了解决当时互联网上数据传输的安全问题,发布了第一个安全套接字层(Secure Sockets Layer,SSL)协议。SSL协议使用了加密技术,对HTTP的数据进行加密传输,保护数据的安全性。

在SSL协议的基础上,网景通信公司将安全传输的功能整合到HTTP协议中,形成了HTTPS协议。HTTPS使用了SSL/TLS(Transport Layer Security)协议来加密HTTP传输过程中的数据,使得网站和用户之间的通信不再以明文传输,变得安全。

HTTP 用于复杂应用

在 2000 年,一种新的使用 HTTP 的模式被设计出来:具象状态传输(representational state transfer) (或者说 REST)。由 API 发起的操作不再通过新的 HTTP 方法传达,而只能通过使用基本的 HTTP / 1.1 方法访问特定的 URI。这允许任何 Web 应用程序通过提供 API 以允许查看和修改其数据,而无需更新浏览器或服务器。所有需要的内容都被嵌入到由网站通过标准 HTTP/1.1 提供的文件中。REST 模型的缺点在于每个网站都定义了自己的非标准 RESTful API,并对其进行了全面的控制。RESTful API 在 2010 年变得非常流行。

自 2005 年以来,可用于 Web 页面的 API 大大增加,其中几个 API 为特定目的扩展了 HTTP 协议,大部分是新的特定 HTTP 头:

  • Server-sent events,服务器可以偶尔推送消息到浏览器。
  • WebSocket,一个新协议,可以通过升级现有 HTTP 协议来建立。

SPDY——Google的尝试(2009)

这些年来,网页愈渐变得的复杂,甚至演变成了独有的应用,可见媒体的播放量,增进交互的脚本大小也增加了许多。 如果仔细观察当前的一些网站所需要下载的资源的话,会发现一个非常明显的趋势—— 近年来加载网站首页需要的下载的数据量在逐渐增加,平均每个页面为了完成显示与渲染所需要下载的资源数已经超过了100个。HTTP/1.1 链接需要请求以正确的顺序发送,理论上可以用一些并行的链接(尤其是 5 到 8 个),带来的成本和复杂性堪忧。比如,HTTP 管线化(pipelining)就成为了 Web 开发的负担。HTTP 1.1很难榨干TCP协议所能提供的所有性能。HTTP客户端和浏览器必须要另辟蹊径的去找到新的解决方案来降低页面载入时间。与此同时,人们也尝试去用新的协议来替代TCP,但结果证明这也非常困难。无奈之下,我们只能尝试同时改进TCP协议本身和基于TCP的上层协议。

在 2010 年早期,谷歌通过实践了一个实验性的 SPDY 协议,想要以此来解决当前HTTP存在的问题。SPDY 的功能包括多路复用、压缩、优先级、安全性等。本节不打算详细介绍 SPDY,因为我们在下一节中深入了解 HTTP/2 的本质时,您就会明白了,HTTP/2 主要是受到 SPDY 的启发。

在2015年,Google决定将SPDY协议与HTTP合并,并基于SPDY的特点推出了HTTP/2。通过将SPDY融入HTTP/2,Google希望避免存在两个竞争的标准,统一标准有利于更好地推动Web协议的发展。同时,Google宣布SPDY协议被废弃,不再继续作为一个独立的协议存在,HTTP/2成为了SPDY的继任者,并继续在未来发展和推广。

HTTP/2——更优异的性能(2015)

由于HTTP/1.1发展年限久远,用户数量庞大,因此对于HTTP/1.1的升级需要制定非常严格的边界与观念,这也给小组成员的创新带来了一些许限制。

升级的边界与观念

  • 必须保持早期HTTP的标准范式: 由于HTTP/1.1使用年限久远,用户数量庞大,使用该协议的网站太多了,如果改动过大且费力,这肯定会阻碍HTTP/2.0发展。因此HTTP/2.0必须保留所有现有的接口、内容、URI格式和结构, 不需要站点和应用做出改变,拥有一个最新的服务器和新点的浏览器即可升级HTTP/2.0。

如上所述,现有的URI结构正在被HTTP 1.x使用而不能被更换,所以http2也必须沿用该结构。因此不得不找到一种方式将使用的协议升级至http2

HTTP 1.1本身就制定过“升级”的方案:提供一个首部字段,表示允许服务器在收到旧协议请求的同时,可以向客户端发送新协议的响应。但这一方案往往需要花费一次额外的往返通信来作为升级的代价。

而这一代价是SPDY团队不想接受的。因为他们只实现了基于TLS的SPDY,所以他们开发了一个TLS的扩展去简化协议的协商。这个扩展被称作NPN(Next Protocol Negotiation),借助于此,服务器会通知客户端所有它支持的协议,让客户端从中选择一个合适的来进行通讯。

  • 提供HTTP1到http2服务器的代理: HTTP1的服务器和客户端仍然会长期存在,所以我们必须提供HTTP/1到http2服务器的代理,并且这种代理能够将http2的功能一对一的映射到HTTP 1.1的客户端。
  • 不再使用小版本号: 服务器和客户端都必须确定自己是否完整兼容http2或者彻底不兼容。如果将来该协议需要被扩充或者变更,那么新的协议将会是http3,而不是http 2.x。
  • 降低协议对延迟的敏感
  • 修复HTTP/1.1中pipelining和head of line blocking的问题
  • 防止主机需求更高的连接数量

在确认好升级边界与观念后,HTTP/2协议规范(RFC 7540)于2015年5月发表,HTTP/2解决了HTTP/1中存在的一大堆缺点,其中相当一部分对于开发者来说非常麻烦,在HTTP/2出现前,开发者要用许多种变通方法来解决。

在HTTP/2.0中HTTP从一个ASCII协议变为一个二进制协议,其主要特性是多路复用(multiplexing),它可以通过同一个TCP连接发送多个逻辑数据流。复用使得很多事情变得更快更好,它带来更好的拥塞控制、更充分的带宽利用、更长久的TCP连接————这些都比以前更好了,链路能更容易实现全速传输,标头压缩技术也减少了带宽的用量。采用HTTP/2后,浏览器对每个主机一般只需要 一个 TCP连接,而不是以前常见的 六个 连接。事实上,HTTP/2使用的连接聚合(connection coalescing)和“去分片”(desharding)技术还可以进一步缩减连接数。HTTP/2还解决了HTTP的队头拥塞(head of line blocking)问题,客户端必须等待一个请求完成才能发送下一个请求的日子过去了。

以下为HTTP/2 的请求示例

http
GET /zh-CN/docs/Glossary/Simple_header HTTP/2
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/zh-CN/docs/Glossary/Simple_header

以下为HTTP/2 的响应示例

http
200 OK
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Wed, 20 Jul 2016 10:55:30 GMT
Etag: "547fa7e369ef56031dd3bff2ace9fc0832eb251a"
Keep-Alive: timeout=5, max=1000
Last-Modified: Tue, 19 Jul 2016 00:59:33 GMT
Server: Apache
Transfer-Encoding: chunked
Vary: Cookie, Accept-Encoding

大家可以惊奇的发现,除了版本号,HTTP/2.0与HTTP/1.1在内容与格式上几乎没有任何差别!这得益于早期标准制定者对HTTP/1.1升级边界与观念的思考,这让标准制定者几乎保留了HTTP/1.1内容与形式上的所有标准,从修改HTTP底层技术来解决HTTP/1.1中的缺点。

也正因如此,HTTP/2.0不需 Web 开发者做什么,不需要站点和应用做出改变,随着陈旧的浏览器和服务器的更新,站点自然就会升级到HTTP/2.0而不会带来任何问题,并且还能在数据传输上节省了可观的成本和支出,这使得该协议在互联网和万维网上得到广泛的实现和部署,HTTP/2.0取得了巨大的成功。

特性

  • HTTP/2.0是一个二进制协议: 在HTTP/2.0中,HTTP不再是ASCII协议,而是二进制协议。作为一个二进制协议,它更容易解析,但与 HTTP/1.x 不同的是,它不再被人眼读取。HTTP/2 的主要构建块是帧和流,每个 HTTP/2 请求和响应都被赋予一个唯一的流 ID,并且它被分成帧。帧不过是二进制数据,帧的集合称为流。每个帧都有一个流 ID,用于标识它所属的流,并且每个帧都有一个公共标头(Type, Length, Flags, Stream Identifie)和帧有效负载,规范中一共定义了10种不同的帧,其中最基础的两种分别对应于HTTP 1.1的DATA和HEADERS。此外,流 ID 是唯一的,客户端发起的任何请求流ID都使用奇数,而来自服务器的响应流ID则都是偶数。

  • 多路复用: 由于 HTTP/2 现在是二进制协议,并且正如我上面所说,它使用帧和流来进行请求和响应,因此一旦打开 TCP 连接,所有流都会通过同一连接异步发送,而无需打开任何其他连接。反过来,服务器以相同的异步方式响应,即响应没有顺序,客户端使用分配的流 ID 来识别特定数据包所属的流。流的多路复用解决了 HTTP/1.x 中存在的线头阻塞问题,即客户端不必等待正在花费时间的请求,其他请求仍将得到处理。

  • 流优先级(Stream Prioritization): HTTP/2允许对请求/响应进行优先级排序,每个流都包含一个优先级(也就是“权重”),它被用来告诉对客户端哪个流更重要。当资源有限的时候,服务器会根据优先级来选择应该先发送哪些流。借助于借助于PRIORITY帧,客户端同样可以告知服务器当前的流依赖于其他哪个流,该功能让客户端能建立一个优先级“树”,所有“子流”会依赖于“父流”的传输完成情况。优先级和依赖关系可以在传输过程中被动态的改变。这样当用户滚动一个全是图片的页面的时候,浏览器就能够指定哪个图片拥有更高的优先级。或者是在你切换标签页的时候,浏览器可以提升新切换到页面所包含流的优先级。优先级的引入确保重要资源的优先加载,提高了页面加载速度和用户体验。

  • 头部压缩: HTTP是一种无状态的协议。简而言之,这意味着每个请求必须要携带服务器需要的所有细节,而不是让服务器保存住之前请求的元数据,这导致许多请求和响应头部会携带一些冗余的一摸一样的信息,增大了标头大小,致使带宽使用和延迟增加。为了克服这个问题,HTTP/2使用HPACK算法对头部信息进行压缩,减少了头部大小,从而降低了传输的数据量,提高了效率。

  • 服务器推送: HTTP/2的服务器推送功能允许服务器在客户端请求前主动推送资源。服务器可以通过发送一个名为PUSH_PROMISE的特殊帧,通知客户端即将推送的资源。这个PUSH_PROMISE帧与导致推送发生的流相关联,并包含服务器将要推送的资源的流ID。通过服务器推送,服务器可以在客户端请求之前主动将客户端可能需要的资源发送到客户端缓存中,从而避免了客户端额外的请求,减少了延迟。这种特性在某些情况下可以提高页面加载速度和性能,尤其是对于一些预加载资源或常用资源,可以减少客户端请求的往返次数,优化了Web应用的性能和用户体验。

  • 中断连接不再断开连接: HTTP/1.1协议有一个缺点:当一个含有确切值的Content-Length的HTTP消息被送出之后,你就很难中断它了。当然,通常你可以断开整个TCP链接(但也不总是可以这样),但这样导致的代价就是需要通过三次握手来重新建立一个新的TCP连接。在http2里面,我们可以通过发送RST_STREAM帧来实现这种需求,它是一种特殊的帧类型,用于中止某些流,即客户端可以发送此帧让服务器知道我不需要此流了。客户端可以使用 RST_STREAM 并停止接收特定流,同时连接不会被关闭其他流仍会正常运行。

  • 可靠性和安全性: HTTP/2支持数据的流量控制和优先级设置,保障了数据的可靠传输。此外,HTTP/2要求使用TLS协议进行加密通信,提高了数据的安全性。

  • 兼容性: HTTP/2通过与HTTP/1.1的兼容性保证,使得现有的Web应用可以逐渐迁移到HTTP/2,而不需要进行大规模的更改。

HTTP/3——基于 QUIC 的 HTTP(2022)

HTTP 的下一个主要版本,HTTP/3 有这与 HTTP 早期版本的相同语义,但在传输层部分使用 QUIC (en-US) 而不是 TCP。到2022年10月,26% 的网站正在使用 HTTP/3

QUIC 旨在为 HTTP 连接设计更低的延迟。类似于 HTTP/2,它是一个多路复用协议,但是 HTTP/2 通过单个 TCP 连接运行,所以在 TCP 层处理的数据包丢失检测和重传可以阻止所有流。QUIC 通过 UDP 运行多个流,并为每个流独立实现数据包丢失检测和重传,因此如果发生错误,只有该数据包中包含数据的流才会被阻止。

RFC 9114 定义的 HTTP/3 被大多数主流浏览器所支持,包括 Chromium(及其他的变体,例如 Chrome 和 Edge)和 Firefox。

(更多HTTP3的内容会随着发展逐步更新)

🎈本节参考

转载需要经过本人同意,并标明出处!

请勿将本站文章用作商业用途 | 转载请标明来源