HTTP
HTTP 的简要历史
1989 年,蒂姆・伯纳斯 - 李在欧洲核子研究中心(瑞士的研究实验室)工作时,需要在科学家之间交换信息。当时,像 FTP 这样的协议已经存在,用于在互联网上传输文件。然而,一个文件通常会包含指向互联网上其他资源的链接。他的目标是创建一种协议和文件格式,允许页面之间相互链接并获取这些页面。
最初的 HTTP 规范被赋予版本号 HTTP/0.9,并于 1991 年发布。HTTP/1.0 于 1996 年标准化,HTTP/1.1 于 1997 年标准化。除非另有说明,本节所指的是 HTTP/1.1,因为它是当今使用最广泛的版本。确实存在更新的版本(见本节末尾),但该协议的基本原理 20 多年来保持不变。
HTTP 基础知识
HTTP 运行在 TCP 之上。想要通过 HTTP 发送数据的双方会首先建立一个 TCP 连接。然后,他们可以利用其字节流抽象来可靠地交换任意长度的数据。运行 HTTP 的主机不必担心数据包的重排序、丢失等问题。

HTTP 是一种客户端 - 服务器协议。我们将一方指定为客户端(例如,作为终端用户的你),另一方指定为服务器(例如,谷歌、银行网站等)。客户端几乎总是在网页浏览器(如 Firefox 或 Chrome)中运行 HTTP,不过 HTTP 也可以通过其他方式运行(例如,直接在终端中)。
建立 HTTP 连接时,服务器必须在众所周知的固定端口号 80 上监听连接请求。(HTTPS,一种较新的安全版本,使用端口 443)。客户端可以选择任意随机的临时端口号来启动连接,服务器可以向该端口号发送回复。
HTTP 是一种请求 - 响应协议。对于客户端发送的每个请求,服务器都会发送恰好一个对应的响应。
HTTP 请求
HTTP 请求消息以人类可读的纯文本格式编写,这意味着你可以在终端中输入原始的 HTTP 请求。请求包含三个部分:方法、URL、版本和可选内容。
消息以换行符结束(严格来说是 CRLF,有兴趣可以查阅),你可以理解为用户在终端中输入 HTTP 请求后按 Enter 键。
版本号指定你使用的 HTTP 版本,例如 HTTP/0.9、HTTP/1.0、HTTP/1.1 等。
请求的 URL 标识服务器上的资源。你可以将 URL 视为你试图从远程服务器检索的文件路径。例如,在 URL http://cs168.io/assets/lectures/lecture1.pdf中,我们试图检索 cs168.io 远程服务器上 assets/lectures 文件夹中的名为 lecture1.pdf 的文件。(服务器不一定必须这样工作,但这是一个有用的直觉理解。)
方法标识用户想要执行的操作。最初,HTTP 只有一个方法 GET,它允许客户端从服务器检索特定页面(由 URL 指示)。
后来,HTTP 进行了扩展,增加了其他方法。值得注意的是,添加了 POST 方法,它允许客户端也向服务器提供信息。例如,如果用户填写表单并点击提交,该数据会通过 POST 请求发送到服务器。
还存在一些不太常用的方法。HEAD 方法检索响应的头部(元数据),但不检索响应的实际内容。其他方法如 PUT、CONNECT、DELETE、OPTIONS、PATCH 和 TRACE 将 HTTP 扩展为允许用户与服务器上的内容进行交互的协议。现在用户可以对内容进行更改,这与最初只能检索内容的设计不同。这些额外的方法使 HTTP 对于各种不同的应用非常灵活。
请注意,对于像 POST 这样的其他方法,我们仍然必须提供 URL 来指示如何解释我们发送的数据。在银行网站上,向 /send-money URL 发送一个姓名可能与向 /request-money URL 发送相同的姓名会产生不同的结果。
对于 GET 请求,请求的内容通常是空的,因为我们是向服务器请求页面,而不是发送我们自己的信息。相比之下,对于 POST 请求,请求的内容包含我们想要发送给服务器的数据。
HTTP 响应
每个 HTTP 请求对应一个 HTTP 响应。响应也是人类可读的纯文本格式,这意味着你可以在终端中读取原始的 HTTP 响应。响应包含四个部分:版本、状态码、可选消息和内容。
和之前一样,版本指定所使用的 HTTP 版本。
内容是服务器放置例如客户端在 GET 请求中请求的页面的地方。
状态码是一个数字,允许服务器指示客户端请求的结果。每个状态码都有一个对应的人类可读消息。
状态码根据数值分为不同的类别:
100 = 信息性响应。
200 = 成功响应。200 OK 表示请求成功,成功的定义取决于请求的方法和使用 HTTP 的应用程序(请记住,无论请求使用的是 GET/POST 等哪种方法,每个响应中都有状态码)。201 Created 表示请求成功且创建了一些新资源。这通常在 POST 或 PUT 请求中出现。
300 = 重定向消息。这些允许服务器告诉客户端他们应该去其他地方(由 URL 指定)查找资源。两个常见的是 301 Moved Permanently(永久移动)和 302 Found(临时移动的奇怪名称)。有时,状态码本身提供的上下文不够(如这些重定向所示)。因此,响应还会包含关于资源移动到何处的额外信息(例如,另一个 URL)。
使用更具体的状态码允许客户端根据代码确定其未来的行为。例如,301 Moved Permanently 告诉客户端停止在原始位置查找,而 302 Found(临时移动)可能告诉客户端稍后再来检查。
400 = 归因于客户端操作的错误。401 Unauthorized 表示客户端不允许访问此内容,但如果他们验证自己的身份(例如登录),则可能能够访问该内容。403 Forbidden 表示客户端已通过身份验证,服务器知道他们的身份,但他们仍然不允许访问该内容。
同样,使用更具体的代码可以让客户端确定未来的行为。401 Unauthorized 可能会导致客户端浏览器显示登录窗口,而 403 Forbidden 可能会导致客户端浏览器显示错误消息(因为用户已经登录)。
500 = 归因于服务器操作的错误。500 Internal Server Error(内部服务器错误)和 503 Service Unavailable(服务不可用)是常见的。客户端对这些错误无能为力,除了可能稍后再试。
一些错误代码如 404(文件未找到)和 503(服务不可用)非常容易识别。
有时,使用适当的状态码可能会有歧义。例如,如果我们使用 0.9 版本向谷歌发送 HTTP 请求,合适的响应可能是 505(HTTP 版本不支持)。相反,谷歌会响应 400(错误请求)。通常,目标是提供来自正确类别的错误(例如 400 和 500 表示错误),以引发客户端的正确行为。
HTTP 头部
如果客户端有额外的信息想要发送给服务器,他们可以包含称为头部的额外元数据。在 HTTP/1.1 中,没有强制性的头部,因此不包含任何头部是合法的(尽管服务器 / 客户端可能期望某个头部并出现错误)。
例如,Location 头部可用于 HTTP 300 响应中,以指示资源已移动到何处。
有时,头部信息是可选的。例如,请求中的 User-Agent 头部让客户端告诉服务器关于客户端浏览器或程序(如 Firefox 或 Chrome)的信息。这可以允许根据头部字段(例如用户使用的是 Chrome 还是终端)对请求进行不同的处理。
其他时候,头部信息更为关键。例如,Content-Type 告诉我们有效负载是 HTML 页面、图像、视频等。这告诉浏览器如何显示 HTTP 响应。如果服务器托管多个网站,请求中的 Host 头部可用于指定要请求的网站。
一些头部与请求相关。这些允许客户端向服务器传递信息。例如,Accept 头部让客户端告诉服务器客户端期望的内容类型(例如,用于人类可读页面的 HTML,用于机器可解析数据的 JSON)。User-Agent 头部指示所使用的客户端类型,Host 头部指示正在访问的特定主机(以防服务器托管多个网站)。Referer 头部指示客户端是如何发出请求的(例如,他们是否点击了来自 Facebook 的链接来发出此请求)。
其他头部与响应相关。请记住,头部是关于内容的元数据,而不是内容本身。例如,Content-Encoding 告诉我们应该如何解释响应的比特(例如,用于人类可读文本的 Unicode/ASCII,或用于压缩文件的 gzip)。Date 头部告诉我们服务器生成响应的时间。
一些头部是表示头部,用于请求和响应中,描述内容的表示方式。例如,Content-Type 头部指定文档的类型(如文本、图像),可以在 POST 请求或 GET 响应中出现。表示头部允许我们通过 HTTP 传输不同类型的内容,这使得该协议具有通用性,可用于各种应用。
HTTP 示例
在终端中,你可以输入telnet ``google.com`` 80来连接到谷歌服务器的 80 端口(HTTP)。然后,终端将允许你输入带有头部的原始 HTTP 请求,例如:
GET / HTTP/1.1
User-Agent: robjs
这是一个获取服务器根页面的 GET 请求,使用 HTTP 1.1 版本。User-Agent 头部指示我们使用的客户端类型。
同样,响应也是人类可读的。
HTTP/1.1 200 OK
Date: Sat, 16 Mar 2024 18:33:08 GMT
Content-Type: text/html; charset=ISO-8859-1
\<!doctype html>\<html lang="en">\<head>\<meta content="Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for." name="description">...
HTTP/1.1 200 OK告诉我们版本以及状态码(200)和其对应的消息(OK)。附加了两个头部,响应的日期和内容类型。然后,内容包含网页的原始 HTML。如果我们在网页浏览器中打开此 HTML,它会看起来像一个实际的网页。


这里有一些其他示例。注意,GET 请求中的内容部分是空的,但 POST 和 PUT 请求中包含数据。相反,POST 和 PUT 响应没有内容,但 GET 响应有内容。
状态码和头部告诉我们关于请求的有用元数据。例如,状态 201 Created 告诉我们我们发送的文件已成功存储在服务器上。头部告诉我们文件在服务器上的存储位置(我们以后可能会使用该位置来检索文件)。
用流水线技术加速 HTTP
在网页浏览器中加载单个页面可能需要多个 HTTP 请求。当你请求一个 YouTube 视频时,你的浏览器必须分别请求视频本身、包含网页上其他文本(如视频标题、评论)的 HTML、相关视频的缩略图等等。其中许多请求可能会发送到同一服务器(例如,在这种情况下是 YouTube 的服务器)。
回想一下,HTTP 运行在 TCP 之上。在简单情况下,每个单独的请求都需要通过三次握手建立一个新的 TCP 连接。请求之后,我们关闭连接,然后立即为下一个请求重新进行握手。

HTTP 1.1 通过允许在同一连接上流水线传输多个 HTTP 请求和响应来优化这一点。现在,我们不再需要为每个请求建立单独的 TCP 连接(带有单独的握手)。

这种优化的一个缺点是,服务器现在必须保持更多的同时打开的连接。服务器需要有某种方式来使连接超时。如果服务器被过多的打开连接重载,客户端可能会收到类似 503 Service Unavailable 的错误。攻击者可能会在拒绝服务攻击中利用这一点。
用缓存加速 HTTP:类型
另一种加速 HTTP 的策略是缓存响应,以避免对相同数据进行重复请求。
如果我们不缓存,每个请求都必须到达服务器。

有三种类型的 HTTP 缓存:
私有缓存与连接到服务器的特定终端客户端相关联(例如,你自己浏览器中的缓存)。现在,如果同一用户第二次请求相同的资源,他们可以从本地缓存中获取该资源。但是,私有缓存不会在用户之间共享。

代理缓存位于网络中(不在终端主机上),由网络运营商而非应用程序提供商控制。这些缓存可以在许多用户之间共享,因此首次请求资源的用户可能从代理缓存而不是源服务器获取数据。

代理缓存的一个问题是,客户端需要某种方式被重定向到代理缓存。应用程序不运行代理缓存,因此源服务器不一定知道代理缓存。网络运营商需要某种方式控制终端客户端,以告知他们有关代理缓存的信息。
一种常见的方法是在 DNS 响应中提供虚假信息,如果网络运营商同时控制代理缓存和递归解析器,这是可能的。当客户端向源服务器发出请求时,它必须查找源服务器的 IP 地址。递归解析器可以谎称:“源服务器的 IP 地址是 1.2.3.4(代理缓存的 IP 地址)。” 现在,对源服务器的请求会发送到代理缓存,代理缓存可以提供缓存的响应。或者,如果请求的资源不在代理缓存中,代理缓存可以向源服务器发出请求,然后缓存可以将请求返回给用户。
代理缓存的另一个问题是,应用程序不管理代理缓存。源服务器必须信任代理缓存正在做正确的事情(例如,尊重缓存过期日期,提供正确的数据)。
托管缓存位于网络中,由应用程序提供商控制。请注意,托管缓存服务器是单独部署的,不是生成内容的原始服务器。由于这些缓存由应用程序提供商控制,这给了应用程序更多的控制权。

由于应用程序同时控制源服务器和缓存,它们可以自行将用户重定向到缓存。例如,如果你从源服务器请求 YouTube 视频页面,回复可能包含 HTML(视频标题、评论)。然后,HTML 可能包含从代理缓存专门获取视频和图像的链接(例如,从static.youtube.com而不是www.youtube.com加载)。
用缓存加速 HTTP:优缺点
缓存对每个人都有好处。客户端可以更快地加载页面,因为他们可以避免重复请求,并使用附近的代理。互联网服务提供商(ISPs)受益,因为网络上发送的 HTTP 请求 / 响应更少,因此他们可以减少带宽建设。服务器受益,因为用户发出的请求更少,他们不需要处理那么多请求。
客户端、互联网服务提供商和服务器都关心为客户端提供良好的性能。客户端希望以高质量观看视频,而互联网服务提供商和应用程序将通过提供良好的性能获得更多客户。缓存帮助每个人实现这一点,因为客户端可以从更近的缓存(本地或网络中的)更高效地获取请求,延迟更低。此外,回想一下 TCP 吞吐量和往返时间(RTT)成反比,因此到更近服务器的更短 RTT 意味着我们可以获得更高的吞吐量。这对于视频等大型内容特别有帮助。
考虑缓存时,我们必须考虑内容在未来的请求中是否会发生变化。有些 HTTP 资源是静态的。如果你请求谷歌徽标,它在多次请求中保持不变。
其他 HTTP 资源是动态的。如果你发出谷歌搜索请求,响应可能会根据谁请求以及何时请求而变化。服务器需要为每个请求动态生成不同的响应。
有些资源是静态的,可以在代理或托管缓存中缓存和提供,而其他资源必须动态生成。例如,如果你进行谷歌搜索,HTML 响应可能需要由源服务器动态生成。然而,HTML 可以包含从其中一个托管缓存服务器获取谷歌徽标的链接,这是一个静态资源。
方便的是,像图像和视频这样的较大资源是静态的,可以积极缓存。动态内容,如定制的 HTML 页面,往往更小。客户端可以从源服务器(较远)请求动态内容,并使用(较近的)缓存和代理获取所有静态内容。
用缓存加速 HTTP:实现
要实现缓存,我们需要使用头部来携带一些关于缓存的元数据(例如,缓存数据的时间)。这是头部允许扩展原始协议(不支持缓存)的另一个例子。
HTTP/1.0 中原始的遗留缓存功能使用 Expires 头部,它只是指定数据可以缓存多长时间。在 HTTP/1.1 中,引入了更复杂的 Cache-Control 头部。为了支持兼容性,一些 Web 服务器返回的数据将同时包含这两个头部。HTTP/1.0 客户端不会理解较新的 Cache-Control 头部,并会忽略它。HTTP/1.1 客户端将优先考虑较新的 Cache-Control 头部,而不是较旧的 Expires 头部。
Cache-Control 头部指定可以缓存数据的缓存类型,以及数据可以缓存的时间。例如,如果资源是动态的,因用户而异,但对于特定用户,随着时间的推移保持不变,那么服务器可以回复:Cache-Control: private, max-age:86400。这表示该内容应仅存储在用户的本地缓存中(不存储在共享的代理 / 托管缓存中),并且可以存储一天(86400 秒)。
有些数据不能缓存(例如,频繁变化的动态内容)。在这种情况下,服务器可以设置 Cache-Control: no-store,以指示客户端和代理不能缓存该内容。
Cache-Control 头部是可选的,因此不能保证客户端会读取或遵守该头部。你可以将此头部视为服务器请求缓存某些内容。对于不是由应用程序提供商运营的代理缓存,这尤其令人担忧。相比之下,私有缓存由客户端(即他们的浏览器)运行,违反规则只会影响客户端自己。托管缓存由同一应用程序提供商运行,因此他们可以确保源服务器的规则被托管缓存遵守。
此头部也可用于更复杂的策略。例如,服务器可能会说,你可以缓存此数据,但在使用缓存的数据之前,请发出 HTTP HEAD 请求重新请求头部并重新验证数据。如果头部指示数据已更改,则使缓存无效。
内容分发网络(CDNs)
earlier,我们看到托管缓存是缓存和提高用户性能的好策略。与私有缓存不同,它们在用户之间共享(例如,首次请求某物的用户可以由缓存提供服务)。此外,与代理缓存不同,它们由应用程序提供商控制,这给了应用程序更多的控制权。应用程序可以确保缓存遵循源服务器设置的规则,并且源服务器可以控制用户被重定向到哪些缓存。
在网络中部署托管缓存使我们想到了内容分发网络(CDNs),它们是网络中一组提供内容(例如 HTTP 资源)的服务器。
为了获得良好的性能,我们尝试将 CDN 放置在靠近终端用户的地方。这里的 “靠近” 指的是地理上靠近,也指从网络角度靠近(跳数更少)。
CDN 为我们提供了缓存的所有好处。用户可以获得更高性能的内容交付,因为服务器更近。我们可以减少所需的网络带宽和基础设施,因为用户的大多数请求是发送到附近的服务器,而不是单个源服务器(可能很远)。
CDN 允许提供商更轻松地扩展其服务器基础设施。对于单个源服务器,我们必须通过使其变得极其强大并为其提供极高的带宽来扩展该服务器。相比之下,使用 CDN,我们只需在整个互联网中添加更多小型服务器即可进行扩展。
CDN 还为提供商提供了更好的冗余。如果单个源服务器出现故障,服务可能会变得不可用。相比之下,使用 CDN,如果一个服务器出现故障,用户仍然可以被重定向到其他服务器。
CDN 部署
回想一下我们的互联网模型:客户端的请求通过 WAN 路由器(由 ISP 拥有)转发,直到到达对等点。然后,请求到达应用程序提供商网络中的对等点。请求通过应用程序的 WAN 网络,直到到达数据中心网络,源服务器就在那里。
如果我们不部署任何 CDN,每个请求都必须到达源服务器。这具有最大的延迟(与后面带有 CDN 的选项相比),导致最低的性能。此外,这需要传输最多的带宽,这意味着我们必须建设更多的带宽。最后,这需要源服务器能够处理每个请求。

更好的选择是在应用程序提供商网络的边缘部署一些 CDN 服务器。例如,如果谷歌的网络在纽约与 ISP 网络对等,我们可以在那里放置一些 CDN。
现在,通过应用程序提供商网络发送的带宽量要少得多。源服务器将视频发送到 CDN 一次,CDN 可以将该视频提供给许多用户。应用程序网络不再需要扩展其 WAN 网络。
此外,如前所述,我们现在可以通过添加更多 CDN 来扩展,而不是升级单个源服务器。我们也有了更多的冗余。

我们可以做得更好,将缓存更深地推入网络。现在,应用程序在 ISP 的网络内部署服务器。

为什么 ISP 会同意让应用程序在其网络中部署 CDN?事实证明,这对每个人都是互利的。ISP 的客户将获得更好的性能,因为他们可以使用这个新的更近的 CDN。此外,用户和 CDN 之间的大量流量现在都包含在 ISP 的网络中。这意味着 ISP 在 ISP 和应用程序之间的对等连接中需要更少的带宽(因为内容只通过该对等连接发送一次)。
实际上,ISP 和 CDN 通常合作部署服务器。例如,应用程序免费提供服务器,ISP 免费将服务器连接到网络。在某些情况下,ISP 和 CDN 需要就某些付款进行协商(CDN 支付给 ISP,或 ISP 支付给 CDN),这取决于服务器在网络中的部署位置以及服务器和连接的成本。尽管如此,双方都有兴趣部署这些服务器。
我们可以尝试更进一步,但最终会遇到成本效益权衡。在最极端的情况下,我们可以在每个人的家中部署 CDN,但成本可能超过收益。特别是,当有多个用户使用 CDN 时,CDN 的效果最好。集体缓存更大,一次部署可以覆盖许多用户。

更一般地说,添加新 CDN 的成本与减少带宽建设所节省的资金之间存在权衡。实际上,CDN 确实存在于 ISP 网络中,因为在那里安装它们仍然有利可图。
Sandvine( packet inspection 公司)2023 年的一份报告显示,所有互联网流量的 15% 来自 Netflix,11.4% 来自 YouTube,4.5% 来自 Disney+。如果 ISP 在其网络中仅为这三个应用程序安装服务器,他们可能可以减少 25% 的网络容量建设。
像谷歌、Netflix、亚马逊和 Facebook 这样的主要应用程序提供商在自己的网络和 ISP 网络中都部署了 CDN。
如果你是应用程序提供商,你可能不是像谷歌或亚马逊这样的科技巨头,但你仍然希望通过 CDN 提供内容以获得良好的性能。这些较小的应用程序可能负担不起安装自己的 CDN。然而,像 Cloudflare、Akamai 和 Edgio 这样的公司已经部署了 CDN,你可以付费让这些公司在其现有的 CDN 上部署你的内容。这些 CDN 提供商也在自己的网络和 ISP 网络中部署基础设施。
CDN 也可以被 ISP 使用,因为 ISP 本身也可以有提供内容的应用程序。当你支付互联网服务费用时,ISP 可能还提供电视服务(直播电视或视频点播)。这些 ISP 安装自己的 CDN,以有效地向你提供该电视内容。
从根本上说,CDN 中的服务器与互联网上提供内容的任何其他服务器相同,尽管它们通常经过高度优化,用于存储和交付大量内容。有些服务器可能更擅长存储和提供大量内容,而其他服务器可能更擅长向大量客户快速提供较小的内容片段。
引导客户端到缓存
在 CDN 中,互联网上的许多不同服务器都在提供相同的内容。客户端如何知道要联系哪个服务器?
一些 DNS 技巧也可以应用于 CDN。我们可以使用任播(anycast),其中多个服务器通告相同的 IP 前缀。这允许路由算法找到到任何一个服务器的最佳路径。

任播的一个问题是长连接。假设客户端与其中一个服务器有一个正在进行的 TCP 连接。在连接期间,网络中的某个中间链路发生故障。由于所有服务器都有相同的 IP 地址,从中间路由器的角度来看,转发到任何一个服务器都是有效的。中间路由器现在可能开始将数据包转发到另一个服务器(具有相同的 IP 地址)。然而,TCP 连接是与原始服务器建立的,这个新服务器无法继续原始连接。
请注意,当我们在 DNS 中使用任播时,这个问题并不存在,因为 DNS 连接非常短(通常只是一个 UDP 数据包)。

我们也可以使用 DNS 进行负载均衡。与任播不同,服务器现在有不同的 IP 地址,尽管它们仍然都有相同的域名。当客户端查询域名到 IP 的映射时,DNS 名称服务器可以根据客户端的位置提供不同的 IP 地址。
这种基于 DNS 的方法没有任播在长连接上的问题,因为服务器现在有不同的地址。路由器不会突然开始将数据包转发到不同的服务器。
基于 DNS 的方法的一个问题是缺乏粒度。举一个极端的例子,假设康卡斯特(Comcast)ISP 的所有人都使用相同的递归解析器。这意味着每个人都将他们的 DNS 查询发送到该解析器,然后解析器向应用程序名称服务器发出查询。应用程序名称服务器只能看到 DNS 请求来自康卡斯特,并且必须向康卡斯特返回一个 IP 地址。现在,康卡斯特网络中的每个用户都在使用相同的服务器,即使用户遍布世界各地。

比任播或 DNS 更可靠的方法是应用程序级映射。当源服务器收到 HTTP 请求时,响应中的链接可以指向不同的服务器(例如static1.google.com或static2.google.com,两个位于不同地方的服务器),这取决于请求来自何处。或者,源服务器可以回复 HTTP 300 级状态码,将用户重定向到适当的服务器。
这种应用程序级方法没有 DNS 的粒度问题,因为应用程序可以在 HTTP 请求中看到客户端的地址。这也没有任播的问题,因为不同的服务器可以有不同的 IP 地址。
然而,与 DNS 负载均衡一样,应用程序仍然需要某种方式来猜测离客户端最近的服务器(“近” 可能是地理上的或基于网络拓扑的)。
应用程序级映射的一个好处是可以根据内容有额外的粒度。例如,热门视频可以部署到许多服务器,允许每个客户端从附近的服务器获取视频。相比之下,很少被访问的冷门视频可以部署到更少的服务器,需要用户走更远的路获取内容。
较新的 HTTP 版本
随着互联网的发展,HTTP 开始被越来越多的应用使用,因为它是一种非常通用的协议。
最终,HTTP 安全成为一个日益受到关注的问题。运行 HTTP 的银行服务器可能不希望信息以人类可读的纯文本形式在网络上发送,因为中间路由器或恶意攻击者可以读取这些信息。
HTTPS 是 HTTP 的扩展,引入了额外的安全性。一种称为 TLS(传输层安全)的协议构建在 TCP 之上,用户在通过字节流发送消息之前交换密钥并加密消息。HTTPS 具有相同的基本协议,但现在运行在 TLS 之上(TLS 本身运行在 TCP 之上),而不是直接运行在原始的不安全 TCP 之上。近年来,出现了推动网站升级到 HTTPS 的趋势,截至 2024 年,超过 85% 的网站现在默认使用 HTTPS。
HTTP/2.0 于 2015 年推出,是自 1997 年以来对该协议的首次重大修订。修订的主要目标是通过减少延迟和提高页面加载速度来提高性能。
HTTP/2.0 引入了服务器推送,服务器可以发送响应,即使客户端没有发出请求。这允许服务器预测并预先提供用户可能需要的内容,而无需等待用户发出请求。例如,如果我们进行谷歌搜索,结果的 HTML 会返回。然后,用户的浏览器解析 HTML,意识到需要谷歌徽标,并发出另一个 HTTP 请求。使用 HTTP/2.0,服务器可以预先将谷歌徽标提供给用户,而无需等待用户请求。
HTTP/2.0 还有其他性能改进。头部可以被压缩以节省空间。请求和响应可以设置优先级,因此高优先级内容(如搜索结果的文本)比低优先级内容(如谷歌徽标)先交付。可以更高效地多路复用同时进行的请求。如果第一个请求有 4 GB 的响应,第二个请求有 1 KB 的响应,简单的实现可能会导致第二个响应被卡在那里,等待第一个响应完成。HTTP/2.0 允许对这些响应进行更智能的管理。
HTTP/2.0 被客户端(如现代浏览器)和服务器(如 CDN)广泛采用。
HTTP/3.0 于 2022 年推出(与 1.1 和 2.0 之间的间隔相比,在 2.0 之后不久)。其语义与 HTTP/2.0 相同,但它替换了底层的传输层协议。HTTP/3.0 不再运行在 TCP 字节流之上,而是运行在一种称为 QUIC 的新传输协议之上,该协议是为与 HTTP/3.0 良好协作而定制的。QUIC(快速 UDP 连接)由谷歌设计,并在 IETF 中标准化。
HTTP/3.0 是一个例子,我们有意放弃了核心网络范式之一(分层),以换取更高的效率。通过给予设计者定制传输层(QUIC)和应用层(HTTP/3.0)协议的自由,我们可以设计出能够良好协作的两种协议。
(注:文档部分内容可能由 AI 生成)