HTTP发展史,从无到牛逼😲😲😲

2,857 阅读21分钟

HTTP 是一种用于在计算机网络上进行通信的协议。它是互联网的基础之一,被用于在 Web 浏览器和 Web 服务器之间传输超文本 HTML 以及其他资源。

HTTP/0.9

HTTP 最早诞生的版本是 0.9,它发布与 1991 年,它是一种非常简单的协议,为了实现基本的超文本 HTML 传输而设计。与后续的版本相比,它仅具有非常有限的功能和结构,其特点如下:

  • 无版本号: 没有明确的版本号,因为它是最早的版本且相对简单,不需要进行版本区分;
  • 文本传输: 仅支持纯文本的传输,只能传输 HTML 格式的文本内容。这意味着它不支持多媒体、CSSJavaScript 等现代 Web 所需的其他资源;

对于使用 HTTP/0.9 的网页,样式通常会通过内联样式来定义,或者直接写在 HTML 文档的 <style> 标签中。

  • 无头信息: 没有定义请求和响应的标头信息,因此在通信过程中缺乏元数据。发送请求时,只需发送一个 GET 请求并指定要获取的文件名,服务器收到请求后直接返回请求的文本内容;
  • 仅支持 GET 方法: 由于 HTTP/0.9 的简单性,它仅支持 GET 方法,不能使用 POST 或其他 HTTP 方法进行交互;

HTTP/0.9 版本中,数据通过使用 TCP/IP 协议传输,而 TCP/IP 协议是基于字节流的。在此情况下,HTML 文档被视为纯文本,并且没有指定具体的字符编码方式。由于 HTTP/0.9 的设计非常简化,它通常使用 ASCII 字符集来传输文本数据。

HTTP/1.0

HTTP/1.0 的出现是为了解决早期 Web 通信的一些限制和问题。在之前的版本中,仅仅支持 HTML 传输显然不能满足于当下需求,于是 19965 月发布了该版本。

它诞生的原因主要有以下几个方面:

  • 需要持久连接: 早期的 HTTP 版本每次请求都需要建立新的 TCP 连接,并在请求结束后立即断开连接。这种方式效率低下,特别是对于多个请求的情况。因此,HTTP/1.0 引入了持久连接,允许在单个 TCP 连接上进行多个请求和响应,从而提高了性能;
  • 需要更多的功能和灵活性: 早期的 HTTP 协议非常简单,只支持 GET 方法来获取文本内容。随着 Web 应用的快速发展,人们需要更多的功能来实现更复杂的操作,例如上传和下载文件、发送表单数据等。因此,HTTP/1.0 的诞生是为了提供更多的功能和灵活性,使得 Web 能够处理更多样化的应用需求;
  • 需要更好的错误处理机制: 早期的 HTTP 协议没有明确定义错误处理的规范,客户端很难准确地理解服务器返回的错误信息,并采取适当的操作。HTTP/1.0 引入了标准的状态码和错误响应格式,客户端可以根据状态码来判断请求的成功与否,以及采取相应的错误处理操作;

HTTP/1.0 中,为了支持多种类型的文件,主要通过 Content-Type 头部字段来实现,并通过不同的文件压缩方式进行传输。例如,如果服务器返回一个名为 index.html 的文件,它会将 Content-Type 头字段设置为 text\html,以告诉客户端该文件是 HTML 类型,并设置 content-encoding 头字段为 gzip 高数返回的压缩类型是 gzip。也就是说最终浏览器需要根据响应头的信息来处理数据,下面是一段响应头的数据信息:

content-encoding: gzip
content-type: text/html; charset=UTF-8

HTTP/1.1 优点

不过随着技术的继续发展,需求也在不断迭代更新,很快 HTTP/1.0 也不能满足需求了,所以 HTTP/1.1 又在 HTTP/1.0 的基础之上做了大量的更新。接下来我们来看看 HTTP/1.0 遇到了哪些主要的问题,以及 HTTP/1.1 又是如何改进的:

  • 更高效的资源利用: 早期的 HTTP/1.0 每次请求都需要建立新的 TCP 连接,这样会导致较大的延迟和资源浪费。为了提高网络资源的利用率,HTTP/1.1 引入了持久连接,通过在同一连接上进行多个请求和响应,减少了连接建立和断开的开销。这样可以显著提高请求和响应的效率,同时减少了网络负载;
  • HTTP 管线化: 随着 Web 应用的发展,页面中的资源数量和大小也越来越大,这导致了较长的页面加载时间。为了加快页面加载速度,HTTP/1.1 引入了管线化 Pipeline 机制,允许客户端在没有等待前一个响应的情况下发送多个请求。这样可以减少请求的延迟,提高并行处理能力,加快页面的渲染速度;
  • 缓存和内容协商支持: 缓存是提高 Web 性能的重要手段之一。HTTP/1.1 对缓存的支持进行了改进,引入了更灵活的缓存控制策略,例如通过 Cache-Control 头字段来指定缓存行为。此外, HTTP/1.1 还引入了协商缓存机制,使服务器可以根据客户端的需求提供不同版本的资源,如语言、编码方式和媒体类型等。这样可以实现更精确的资源缓存和传输,提高了 Web 应用的效率和可扩展性;
  • 安全性和认证支持: 随着 Web 应用的广泛应用,更加复杂和安全的通信需求也逐渐涌现。为了提供更强大的安全性和认证支持,HTTP/1.1 引入了一系列安全增强特性,如对 SSL/TLS 的原生支持HTTPS、基本认证和摘要认证等。这样可以保护用户的隐私和敏感信息,增强了 Web 应用的安全性;
  • 错误处理和状态管理: HTTP/1.1 对错误处理和状态管理进行了改进。引入了更多的状态码,包括常见的成功、重定向、客户端错误和服务器错误等。这样可以提供更精确的错误信息,帮助开发者和用户更好地理解和处理错误情况,改善了用户体验;
  • 提供虚拟主机的支持: 在 HTTP/1.0 中,每个域名绑定了一个唯一的 IP 地址,因此一个服务器只能支持一个域名。HTTP/1.1 的请求头中增加了 Host 字段,用来表示当前的域名地址,这样服务器就可以根据不同的 Host 值做不同的处理。使用虚拟主机的好处之一是可以在单个服务器上托管多个网站,这样可以更有效地利用资源并降低成本;

HTTP/1.1 缺陷

HTTP/1.1 是一种用于Web 通信的协议,虽然它在推出时是一项重大进步,但也存在一些缺陷。以下是 HTTP/1.1 的一些主要缺陷:

  • 高延迟: HTTP/1.1 使用持久连接来减少建立连接的开销,但每个连接只能处理一个请求。这导致了头部阻塞问题,即当一个请求的响应未返回时,后续的请求必须等待。这会影响整体性能并增加页面加载时间;
  • 无法优先级处理请求: HTTP/1.1 无法对请求进行优先级处理。如果同时发送多个请求,在网络拥塞的情况下,低优先级的请求可能会阻塞高优先级的请求,导致用户体验下降;
  • 多次请求和响应的开销: 在 HTTP/1.1 中,每个请求都需要单独建立连接,并且每个请求和响应都需要发送和接收头部信息。这增加了网络传输的开销,并且可能导致不必要的延迟。
  • 安全性限制:HTTP/1.1 没有内置的加密机制,而是依赖于额外的安全层 TLS/SSL 来提供安全性。这使得数据容易受到窃听和篡改的威胁;

队头阻塞

我们再来看看你高延迟这个问题。虽然现在网络的 带宽 相比以前变多了,但是延迟降到一定幅度后,就很难再下降了,说白了就是到达了延迟的下限,这主要的原因还是队头阻塞导致带宽无法被充分利用。

chrome 浏览器中,它支持的最大并发连接数是 6 个,也就是同时最多可以与服务器建立 6 个连接来加载页面资源,如图像、CSS 文件、JavaScript 文件等。这种限制是为了防止过度加载和保持性能。

虽然能公用一个 TCP 管道,但是在一个管道中同一时刻只能处理一个请求,在当前的请求没有结束之前,其他的请求只能处于阻塞状态。

为了解决 HTTP/1.1 性能问题,可以采取以下一些优化手段:

  • 使用请求资源的合并:将多个小文件合并为一个大文件可以减少请求的数量。通过将 CSSJavaScript 文件合并,以及使用 CSS精灵图 来组合背景图像,可以减少头部阻塞问题。但是带来了新的问题,当某张小图片更新了,那么需要重新请求大图片,浪费了大量的网络带宽;
  • 图片 base64 编码: 将图片的二进制数据通过 base64 编码后,把编码数据嵌入到 HTMLCSS 文件中,以此来减少网络请求次数;
  • 使用 CDN内容分发网络: 将静态资源部署到 CDN 上,可以将资源缓存到离用户更近的位置,提高资源加载速度和降低延迟;
  • 压缩内容: 使用 Gzip 等压缩算法对响应内容进行压缩,可以减小传输数据的大小,减少网络传输时间;
  • 采用域名分片: 将网站的资源分布在多个子域名下,以实现更高的并发连接数。浏览器针对同一域名会有并发连接数限制,但对不同域名则没有限制;
  • 利用缓存机制: 使用适当的缓存策略,使得重复请求的资源能够从浏览器缓存中获取,而不是重新向服务器请求。这样可以减少不必要的网络请求和延迟;

尽管对 HTTP/1.1 协议的优化手段如此之多,但是效果还是不尽人意,因为这些手段都是对 HTTP/1.1 协议的外部做优化,而一些关键的地方是没办法优化的,比如请求-响应模型、头部巨大且重复、并发连接耗时、服务器不能主动推送等,要改变这些必须重新设计 HTTP 协议。

SPDY 协议

处于持续开发状态的 SPDY 协议,正是为了在协议界别消除 HTTP 所遭遇的瓶颈。

SPDY 没有完全改写 HTTP 协议,而是在 TCP/IP 的应用层和传输层之间通过新加会话层的形式运作。同时考虑到安全性问题,SPDY 规定通信中使用 SSL

20230720101830

使用 SPDY 之后,HTTP 协议额外获得以下功能:

  • 多路复用: 通过单一的 TCP 连接,可以无限制处理多个 HTTP 请求,所有请求的处理都在一条 HTTP 连接上完成,因此 TCP的处理效率得到提高;
  • 赋予请求优先级: SPDY 不仅可以无限制地并发处理请求,还可以给请求逐个分配优先级顺序。这样做主要是为了在发送多个请求时,结局因带宽低而导致响应慢的问题;
  • 压缩 HTTP 首部: 压缩 HTTP 请求和响应的首部。这样一来,通信产生的数据报数量和发送的字节数就更少了;
  • 推送功能: 支持服务器主动想客户端推送数据的功能。这样,服务器可直接发送数据,而不必等待客户端的请求了;
  • 服务器提示功能: 服务器可以主动提示客户端请求所需的资源。由于在客户端发现之前就可以获知资源的存在,因此在资源已缓存等情况下,可以避免发送不必要的请求;

因为 SPDY 基本上只是将单个域名 (IP地址) 的通信多路复用,所以当一个 Web 网站上使用多个域名下的资源,改善效果就会受到限制。SPDY 的确是一种可有效消除 HTTP 瓶颈的技术,但很多 Web 网站存在的问题并非仅仅是 HTTP 瓶颈所导致。对 web 本身的速度的提升,还应该从其他可细致钻研的地方入手。

HTTP/2.0

由于前面的 HTTP/1.1HTTPS 的加持下已经在安全方面做得非常好了,所以 HTTP/2 的唯一目标就是改进性能。

但它不仅背负着众多的期待,同时还背负着 HTTP/1.1 庞大的历史包袱,所以协议的修改必须小心谨慎,兼容性是首要考虑的目标,否则就会破坏互联网上无数现有的资产。

因为必须要保持功能上的兼容,所以 HTTP/2HTTP 分解成了 语义语法 两个部分,语义层不做改动,与 HTTP/1 完全一致。比如请求方法、URI、状态码、头字段等概念都保留不变,这样就消除了再学习的成本,基于 HTTP 的上层应用也不需要做任何修改,可以无缝转换到 HTTP/2。与 HTTPS 不同,HTTP/2 没有在 URI 里引入新的协议名,仍然用 http 表示明文协议,用 https 表示加密协议。

接下来我们就来用一个动图的方式看看 HTTP/2 有多牛逼吧: 动画.gif

头部压缩

HTTP 协议的报文是由 Header + Body 构成的,对于 Body 部分,HTTP/1.1 协议可以使用头字段 Content-Encoding 指定 Body 的压缩方式,比如用 gzip 压缩,这样可以节约带宽,但报文中的另外一部分 Header,是没有针对它的优化手段。

HTTP/1.1 中的 Header 存在一些问题,主要包括以下几点:

  • 文本格式: HTTP/1.1 中的 Header 采用纯文本格式传输,以键值对的形式表示。这种文本格式有一定的冗余,占用了大量的传输空间。而且由于文本格式的不规范,解析起来相对较慢。并且含有很多固定的字段,例如 CookieUser AgentAccept 等,这就成了不择不扣的大头儿子,而小头爸爸就是 body。更要命的是,成千上万的请求响应报文里有很多字段值都是重复的,非常浪费;
  • 重复传输: 在每个请求和响应中,都需要发送完整的 Header 信息,即使前后两个请求或响应中有部分 Header 是相同的。这样就造成了重复传输,浪费了带宽和网络资源;

HTTP/2 没使用常见的 gzip 压缩方式来压缩头部,而是开发了 HPACK 算法,该算法主要包含三个组成部分:

  • 静态字典;
  • 动态字典;
  • Huffman 编码(压缩算法);

HTTP/2 头部的编码通过静态表、动态表、Huffman 编码共同完成的,如下图所示: 20230720124911

二进制分帧

HTTP/2 厉害的地方在于将 HTTP/1.1 的文本格式改成二进制格式传输数据,极大提高了 HTTP 传输效率,而且二进制数据使用位运算能高效解析。

它把 TCP 协议的部分特性挪到了应用层,把原来的 Header+Body 的消息打散为数个小片的二进制帧(Frame),用 HEADERS 帧存放头数据、DATA 帧存放实体数据,如下图所示: data

又或者可以像下图那样: 20230720125457

HTTP/2 中的流是帧的逻辑容器。每个流都有一个唯一的流标识符,用于区分不同的流。流可以用于承载请求和响应,并支持双向通信。通过流的并发和优先级管理,可以实现更高效的数据传输。

因为 是虚拟的,实际上并不存在,所以 HTTP/2 就可以在一个 TCP 连接上用 同时发送多个碎片化的消息,这就是常说的多路复用,多个往返通信都复用一个连接来处理。

多路复用

HTTP/2 中,有了二进制分帧之后,HTTP/2 不再依赖 TCP 连接去实现多流并行了。

HTTP/2 通过多路复用机制实现在单个 TCP 连接上并发处理多个请求和响应。这种能力大大提高了性能和效率,以下是关于 HTTP/2 多路复用的详细解释以及实现原理:

  • 多路复用的概念: 在传统的 HTTP/1.1 协议中,每个请求都需要建立一个独立的 TCP 连接,导致了连接的创建、维护和关闭等开销增加,虽然它们在同一个 TCP 连接上,但它们仍然是按照请求的顺序进行处理。也就是说,如果前面的请求还没有响应返回,后续的请求需要等待。而 HTTP/2 通过在一个 TCP 连接上同时处理多个流来实现多路复用,使得多个请求和响应可以同时在同一个连接上进行传输;
  • 帧和流的关系: 在 HTTP/2 中,数据被分割为一个个小的帧,然后由服务器和客户端之间交换。每个帧都包含一个特定的类型和标识符,以及帧的有效载荷。多个帧组成了一个流 Stream,每个流都有唯一的流标识符用于标识;
  • 帧的优先级: 在发起请求时,客户端可以为每个请求设置优先级。服务器可以根据这些优先级来决定处理顺序,以确保重要或紧急的请求可以尽早得到处理。这种方式可以提供更好的资源管理和性能控制;
  • 帧的交错发送: 在 TCP 连接上传输帧时,帧可以以任意的顺序进行交错发送。这意味着不同流的帧可以混合在一起发送,而无需等待之前的流完成。这样就能够充分利用带宽,并显著减少延迟;
  • 流级别的流量控制: 为了防止某个流过多占用连接资源,HTTP/2 引入了流级别的流量控制机制。每个流都有自己的发送窗口和接收窗口,控制着数据的传输速率。发送方发送的帧大小受到接收方窗口大小的限制,从而实现了流量的平衡;

通过多路复用,HTTP/2 克服了传统 HTTP/1.1 中串行传输的限制,允许多个请求和响应同时在单个 TCP 连接上进行传输。这样可以降低建立和维护连接的开销,减少了网络延迟和资源占用,提高了性能和效率。通过与流量控制和优先级机制的结合,确保了对带宽和资源的有效管理,提供了更好的用户体验。

HTTP/2 连接中,流标识符是全局唯一的,每个帧都会包含一个头部,其中包括了流标识符字段,表示该帧属于哪个流。通过流标识符,接收方能够将属于同一流的帧组装在一起,实现对请求和响应的解复用。

服务器推送

HTTP/2 引入了服务器推送机制,它允许服务器在客户端请求之前主动将额外的资源推送给客户端,提前缓存可能需要的资源,从而减少延迟和提高性能。

服务器接收到客户端的请求后,会解析请求并识别出请求所需的资源。服务器根据请求所需的资源,主动将相关的资源推送给客户端。服务器会生成一个新的帧,其中包含被推送资源的信息,并通过同一连接发送给客户端。推送的资源可以是 HTMLCSSJavaScript、图片等。

安全

出于兼容的考虑,HTTP/2 延续了 HTTP/1.1 的明文特点,可以像以前一样使用明文传输数据,不强制使用加密通信,不过格式还是二进制,只是不需要解密。

但由于 HTTPS 已经是大势所趋,而且主流的浏览器 ChromeFirefox 等都公开宣布只支持加密的 HTTP/2,所以事实上的 HTTP/2 是加密的。也就是说,互联网上通常所能见到的 HTTP/2 都是使用 https 协议名,跑在 TLS 上面。

虽然 HTTP/2.0 在设计上极大地减轻了队头阻塞问题,但并不意味着完全消除了这个问题。在一些特定情况下,仍然可能存在队头阻塞,例如当某些请求占用了大量带宽或传输时间,导致后续请求等待较长时间。不过相对于 HTTP/1.x,HTTP/2.0 显著改善了性能和效率,提供更快的页面加载速度和更高的并发性。

HTTP/2 的缺点

虽然 HTTP/2 解决了很多之前旧版本的问题,但是它还是存在一个巨大的问题,主要是底层支撑的 TCP 协议造成的。HTTP/2 的缺点主要有以下几点。

队头阻塞

虽然 HTTP/2.0 在设计上极大地减轻了队头阻塞问题,但并不意味着完全消除了这个问题。在一些特定情况下,仍然可能存在队头阻塞,例如当某些请求占用了大量带宽或传输时间,导致后续请求等待较长时间。不过相对于 HTTP/1.x,HTTP/2.0 显著改善了性能和效率,提供更快的页面加载速度和更高的并发性。

只能说 HTTP/2 解决了 HTTP 的队头阻塞问题,但是并没有解决 TCP 队头阻塞问题!

HTTP/2 废弃了管道化的方式,而是创新性的引入了帧、消息和数据流等概念。客户端和服务器可以把 HTTP 消息分解为互不依赖的帧,然后乱序发送,最后再在另一端把它们重新组合起来。因为没有顺序了,所以就不需要阻塞了,就有效的解决了 HTTP 对头阻塞的问题。

TCP 传输过程中会把数据拆分为一个个按照顺序排列的数据包,这些数据包通过网络传输到了接收端,接收端再按照顺序将这些数据包组合成原始数据,这样就完成了数据传输。

但是如果其中的某一个数据包没有按照顺序到达,接收端会一直保持连接等待数据包返回,这时候就会阻塞后续请求。这就发生了 TCP 队头阻塞。

HTTP/1.1 的管道化持久连接也是使得同一个 TCP 链接可以被多个 HTTP 使用,,但是 HTTP/1.1 中规定一个域名可以有 6 个 TCP 连接。而 HTTP/2 中,同一个域名只是用一个 TCP 连接。

所以,在 HTTP/2 中,TCP 队头阻塞造成的影响会更大,因为 HTTP/2 的多路复用技术使得多个请求其实是基于同一个 TCP 连接的,那如果某一个请求造成了 TCP 队头阻塞,那么多个请求都会受到影响。

建连延时

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

参考文献

总结

最后我们用一张图来看看 HTTP/1HTTPSHTTP/2 的协议栈,你应该就可以对这个知识点有更好的理解了。 20230720162248

HTTP/3 的版本中,它在 HTTP/2 的基础上又实现了质的飞跃,真正完美地解决了队头阻塞问题。这个后续会放在一篇文章中讲解。