HTTP
是一种用于在计算机网络上进行通信的协议。它是互联网的基础之一,被用于在 Web
浏览器和 Web
服务器之间传输超文本 HTML
以及其他资源。
HTTP/0.9
HTTP
最早诞生的版本是 0.9
,它发布与 1991
年,它是一种非常简单的协议,为了实现基本的超文本 HTML
传输而设计。与后续的版本相比,它仅具有非常有限的功能和结构,其特点如下:
- 无版本号: 没有明确的版本号,因为它是最早的版本且相对简单,不需要进行版本区分;
- 文本传输: 仅支持纯文本的传输,只能传输
HTML
格式的文本内容。这意味着它不支持多媒体、CSS
、JavaScript
等现代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
传输显然不能满足于当下需求,于是 1996
年 5
月发布了该版本。
它诞生的原因主要有以下几个方面:
- 需要持久连接: 早期的
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
性能问题,可以采取以下一些优化手段:
- 使用请求资源的合并:将多个小文件合并为一个大文件可以减少请求的数量。通过将
CSS
和JavaScript
文件合并,以及使用CSS精灵图
来组合背景图像,可以减少头部阻塞问题。但是带来了新的问题,当某张小图片更新了,那么需要重新请求大图片,浪费了大量的网络带宽; - 图片 base64 编码: 将图片的二进制数据通过
base64
编码后,把编码数据嵌入到HTML
或CSS
文件中,以此来减少网络请求次数; - 使用
CDN
内容分发网络: 将静态资源部署到CDN
上,可以将资源缓存到离用户更近的位置,提高资源加载速度和降低延迟; - 压缩内容: 使用
Gzip
等压缩算法对响应内容进行压缩,可以减小传输数据的大小,减少网络传输时间; - 采用域名分片: 将网站的资源分布在多个子域名下,以实现更高的并发连接数。浏览器针对同一域名会有并发连接数限制,但对不同域名则没有限制;
- 利用缓存机制: 使用适当的缓存策略,使得重复请求的资源能够从浏览器缓存中获取,而不是重新向服务器请求。这样可以减少不必要的网络请求和延迟;
尽管对 HTTP/1.1
协议的优化手段如此之多,但是效果还是不尽人意,因为这些手段都是对 HTTP/1.1
协议的外部做优化,而一些关键的地方是没办法优化的,比如请求-响应模型、头部巨大且重复、并发连接耗时、服务器不能主动推送等,要改变这些必须重新设计 HTTP
协议。
SPDY 协议
处于持续开发状态的 SPDY
协议,正是为了在协议界别消除 HTTP
所遭遇的瓶颈。
SPDY
没有完全改写 HTTP
协议,而是在 TCP/IP
的应用层和传输层之间通过新加会话层的形式运作。同时考虑到安全性问题,SPDY
规定通信中使用 SSL
。
使用 SPDY
之后,HTTP
协议额外获得以下功能:
- 多路复用: 通过单一的
TCP
连接,可以无限制处理多个HTTP
请求,所有请求的处理都在一条HTTP
连接上完成,因此TCP
的处理效率得到提高; - 赋予请求优先级:
SPDY
不仅可以无限制地并发处理请求,还可以给请求逐个分配优先级顺序。这样做主要是为了在发送多个请求时,结局因带宽低而导致响应慢的问题; - 压缩
HTTP
首部: 压缩HTTP
请求和响应的首部。这样一来,通信产生的数据报数量和发送的字节数就更少了; - 推送功能: 支持服务器主动想客户端推送数据的功能。这样,服务器可直接发送数据,而不必等待客户端的请求了;
- 服务器提示功能: 服务器可以主动提示客户端请求所需的资源。由于在客户端发现之前就可以获知资源的存在,因此在资源已缓存等情况下,可以避免发送不必要的请求;
因为 SPDY
基本上只是将单个域名 (IP地址
) 的通信多路复用,所以当一个 Web
网站上使用多个域名下的资源,改善效果就会受到限制。SPDY
的确是一种可有效消除 HTTP
瓶颈的技术,但很多 Web
网站存在的问题并非仅仅是 HTTP
瓶颈所导致。对 web
本身的速度的提升,还应该从其他可细致钻研的地方入手。
HTTP/2.0
由于前面的 HTTP/1.1
在 HTTPS
的加持下已经在安全方面做得非常好了,所以 HTTP/2
的唯一目标就是改进性能。
但它不仅背负着众多的期待,同时还背负着 HTTP/1.1
庞大的历史包袱,所以协议的修改必须小心谨慎,兼容性是首要考虑的目标,否则就会破坏互联网上无数现有的资产。
因为必须要保持功能上的兼容,所以 HTTP/2
把 HTTP
分解成了 语义
和 语法
两个部分,语义层不做改动,与 HTTP/1
完全一致。比如请求方法、URI、状态码、头字段等概念都保留不变,这样就消除了再学习的成本,基于 HTTP
的上层应用也不需要做任何修改,可以无缝转换到 HTTP/2
。与 HTTPS
不同,HTTP/2
没有在 URI
里引入新的协议名,仍然用 http
表示明文协议,用 https
表示加密协议。
接下来我们就来用一个动图的方式看看 HTTP/2
有多牛逼吧:
头部压缩
HTTP
协议的报文是由 Header + Body
构成的,对于 Body
部分,HTTP/1.1
协议可以使用头字段 Content-Encoding
指定 Body
的压缩方式,比如用 gzip
压缩,这样可以节约带宽,但报文中的另外一部分 Header
,是没有针对它的优化手段。
HTTP/1.1
中的 Header
存在一些问题,主要包括以下几点:
- 文本格式:
HTTP/1.1
中的Header
采用纯文本格式传输,以键值对的形式表示。这种文本格式有一定的冗余,占用了大量的传输空间。而且由于文本格式的不规范,解析起来相对较慢。并且含有很多固定的字段,例如Cookie
、User Agent
、Accept
等,这就成了不择不扣的大头儿子,而小头爸爸就是body
。更要命的是,成千上万的请求响应报文里有很多字段值都是重复的,非常浪费; - 重复传输: 在每个请求和响应中,都需要发送完整的
Header
信息,即使前后两个请求或响应中有部分Header
是相同的。这样就造成了重复传输,浪费了带宽和网络资源;
HTTP/2
没使用常见的 gzip
压缩方式来压缩头部,而是开发了 HPACK
算法,该算法主要包含三个组成部分:
- 静态字典;
- 动态字典;
Huffman
编码(压缩算法);
HTTP/2
头部的编码通过静态表、动态表、Huffman
编码共同完成的,如下图所示:
二进制分帧
HTTP/2
厉害的地方在于将 HTTP/1.1
的文本格式改成二进制格式传输数据,极大提高了 HTTP
传输效率,而且二进制数据使用位运算能高效解析。
它把 TCP
协议的部分特性挪到了应用层,把原来的 Header+Body
的消息打散为数个小片的二进制帧(Frame)
,用 HEADERS
帧存放头数据、DATA
帧存放实体数据,如下图所示:
又或者可以像下图那样:
流
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
引入了服务器推送机制,它允许服务器在客户端请求之前主动将额外的资源推送给客户端,提前缓存可能需要的资源,从而减少延迟和提高性能。
服务器接收到客户端的请求后,会解析请求并识别出请求所需的资源。服务器根据请求所需的资源,主动将相关的资源推送给客户端。服务器会生成一个新的帧,其中包含被推送资源的信息,并通过同一连接发送给客户端。推送的资源可以是 HTML
、CSS
、JavaScript
、图片等。
安全
出于兼容的考虑,HTTP/2
延续了 HTTP/1.1
的明文特点,可以像以前一样使用明文传输数据,不强制使用加密通信,不过格式还是二进制,只是不需要解密。
但由于 HTTPS
已经是大势所趋,而且主流的浏览器 Chrome
、Firefox
等都公开宣布只支持加密的 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/1
、HTTPS
和 HTTP/2
的协议栈,你应该就可以对这个知识点有更好的理解了。
在 HTTP/3
的版本中,它在 HTTP/2
的基础上又实现了质的飞跃,真正完美地解决了队头阻塞问题。这个后续会放在一篇文章中讲解。