在本文中,我们将深入探讨万维网数据通信的基础 - HTTP。
什么是超文本?
HTTP(超文本传输协议)的命名源于“超文本”。
那么,什么是超文本?
想象一下由超链接组成的文本、图像和视频的混合物。这些链接充当我们从一个超文本集合跳转到另一个集合的门户。HTML(超文本标记语言)就是超文本的一个典型示例。
HTML是一个纯文本文件。它包含许多标签,这些标签定义了到图像、视频等的链接。浏览器解释这些标签后,将看似普通的文本文件转换为充满文本和图像的网页。
HTTP/1.1、HTTP/2和HTTP/3
自从1989年诞生HTTP 0.9以来,HTTP经历了重大变革。让我们回顾一下每个HTTP版本解决的问题。下图展示了关键的改进。
•HTTP/1.0在1996年最终定稿并正式记录下来。该版本有一个重要限制:对同一服务器的每个请求都需要单独的TCP连接。•HTTP/1.1在1997年推出。它引入了“持久连接”的概念,意味着可以保持TCP连接以进行重用。尽管有这一改进,HTTP/1.1无法解决“HOL(Head-of-Line)阻塞”问题。简而言之,当浏览器的所有并行请求槽都被占满时,后续请求必须等待前面的请求完成,这就是HOL阻塞。•HTTP/2.0于2015年发布,旨在解决HOL阻塞问题。它实现了“请求多路复用”(request multiplexing)的策略,以消除应用层的HOL阻塞。如下图所示,HTTP/2.0引入了HTTP“流”的概念。这种抽象允许将不同的HTTP交换复用到同一TCP连接中,无需按顺序发送每个流。但是,HOL阻塞仍可能发生在传输(TCP)层。•HTTP/3.0于2020年发布了一份草案,作为HTTP/2.0的继任者,它将TCP替换为QUIC作为底层传输协议。这有效地消除了传输层的HOL阻塞。QUIC基于UDP。它在传输层引入了流作为一级公民。QUIC流共享相同的QUIC连接,无需额外的握手或慢启动来创建新的流。QUIC流可以独立传输。这意味着在大多数情况下,一个流中的数据包丢失不会影响其他流。
HTTP头部
HTTP头部在客户端和服务器之间发送和接收数据时起着关键作用。它们为这些实体提供了一种结构化的方式来传递有关请求或响应的重要元数据。这些元数据可以包含各种信息,例如发送的数据类型、长度、压缩方式等。
HTTP头部由多个字段组成,每个字段具有特定的作用和含义。现在我们对HTTP头部有了一定的了解,让我们深入探讨一些特定的HTTP字段。
HTTP字段
当我们向服务器发送HTTP请求时,有一些常见的字段起着至关重要的作用。让我们逐个剖析一些字段。
•Host:这是服务器的域名。•Content-Length:请求或响应头部中的这个字段在数据传输中起着至关重要的作用。它明确地指示请求或响应主体的大小(以字节为单位)。这有助于接收方理解当前消息何时结束,并可能为下一个消息做准备,特别是在多个HTTP消息通过同一连接发送的情况下。•Connection:该字段在HTTP持久连接中至关重要,其中一个TCP连接用于发送和接收多个HTTP请求和响应。我们将详细讨论这个。•Content-type:该字段告诉客户端接收到的数据的格式。•Content-encoding:该字段指示用于数据的压缩格式。例如,如果客户端看到'gzip'编码,它知道需要解压缩数据。
HTTP GET与HTTP POST
HTTP协议定义了各种方法或“动词”来对Web资源执行不同的操作。常用的方法包括GET、POST、PUT和DELETE,通常用于读取、创建、更新和删除资源。较少使用的方法包括HEAD、CONNECT、OPTIONS、TRACE和PATCH,我们在之前的“API设计”问题中已经介绍过。
一个常见的面试问题是:“GET和POST有什么区别?”让我们深入了解它们的定义。
HTTP GET:该方法通过URL从服务器检索资源,不会产生其他影响。由于GET请求通常没有有效载荷主体,因此可以对网页进行书签、共享和缓存。
HTTP POST:该方法基于有效载荷主体与资源进行交互。交互方式因资源类型而异。例如,如果我们在购买了一台 iPhone 14 后留下了一条评论,并单击“提交”,将发送带有评论的POST请求到服务器。虽然HTTP协议本身对POST请求的消息主体大小没有定义限制,但实际上,浏览器和服务器通常会施加自己的限制。
理解GET和POST的特性
HTTP方法具有一些属性,定义了它们与服务器资源交互的方式。其中两个属性是它们是否“非变异”和“幂等”。
非变异方法不会改变任何服务器资源。相反,幂等方法无论重复多少次,都会产生相同的结果。
HTTP GET:GET方法检索数据而不会引起更改,因此是非变异的。此外,重复GET请求不会改变结果,因此是幂等的。
HTTP POST:与GET不同,POST方法发送的数据可以修改服务器资源,因此可能是可变异的。此外,如果我们重复POST请求,它可能会创建额外的资源,因此是非幂等的。
然而,重要的是要注意,实际行为可能取决于服务器如何实现这些方法。虽然标准建议特定行为,但开发人员有时会以非标准的方式使用这些方法。例如,GET方法可能用于删除数据(使其既可变异又非幂等),或者POST方法可能用于检索数据(使其非变异和潜在幂等)。
有一个臭名昭著的例子是一个博客作者使用GET来执行帖子删除操作,假设没有人会访问该博客。但是当Google抓取该博客时,所有帖子都被删除了!
此外,值得注意的是,无论是GET还是POST,它们本身并不安全,无法防止信息泄露。GET参数在URL中可见,而POST主体虽然在URL中不可见,但如果没有加密仍然可以被拦截。为了确保安全的数据传输,建议使用HTTPS,这是我们将在后面更详细地讨论的一个主题。
HTTP Keep-Alive与TCP keepalive
我们已经讨论过HTTP如何使用“Connection: Keep-Alive”来启动持久连接。还记得在TCP问题中提到的TCP keepalive机制吗?它们是否相同?不,它们完全不同:
•HTTP Keep-Alive与HTTP持久连接相关,它在应用层运行。•TCP keepalive在传输层工作,在数据交换空闲期间保持TCP连接活动。
让我们深入了解。
HTTP Keep-Alive
除了HTTP/3以外,HTTP都构建在TCP之上。建立HTTP连接需要进行3次TCP握手。在发送HTTP请求并接收到响应后,TCP连接断开。
以这种方式向同一服务器发送多个请求非常低效。重用相同的TCP连接是否更好?这就是HTTP Keep-Alive的目的。它保持TCP连接,直到任一方请求断开连接。
HTTP/1.1默认启用了HTTP Keep-Alive。
HTTP Keep-Alive减少了打开和关闭TCP连接的开销。当与HTTP/2结合使用时,效果更好。HTTP/2引入了“流”的概念。
流允许我们同时发送多个请求,而无需等待服务器的响应。更重要的是,这些请求和响应可以按照任意顺序处理,这在仅使用HTTP Keep-Alive时是不可能的。
下面的对比图显示了HTTP Keep-Alive和HTTP/2流之间的区别。通常,我们等待第一个响应返回后才发送第二个请求。但是使用HTTP/2流,我们可以同时发送多个请求,而无需等待第一个响应,服务器可以按任意顺序响应。
为什么这很重要?这个特性对于避免“HOL(Head-of-Line)阻塞”非常关键。在HTTP的早期版本中,如果服务器处理一个请求的时间很长,后续的请求必须等待,导致延迟。但是使用HTTP/2流,每个请求是独立的。即使服务器处理一个请求的时间较长,它仍然可以响应其他请求。响应可以在准备就绪时立即返回,即使这意味着它们的顺序与原始请求不同。
TCP keepalive
TCP keepalive与HTTP Keep-Alive无关。在TCP连接中,双方保持在已建立状态,直到一方结束连接。如果一方不通知另一方而断开连接,剩余的一方将不知道。TCP keepalive通过在没有数据交换时定期发送探测来解决这个问题。我们在之前的TCP问题中讨论过。下面的图表应该对此有所复习。