每一个技术诞生的背后,都有其历史。了解技术背后的历史,也有助于我们了解技术的本质。同时,译文如有翻译不当,欢迎提出修改意见。
超文本传输协议(HTTP)是 Internet 上最普遍和广泛采用的应用程序协议之一:它是客户端和服务器之间的通用语言,支持现代 Web。从简单的单一关键字和文档路径开始,它已成为不仅适用于浏览器,而且适用于几乎所有连接互联网软硬件应用的协议。
在本章中,我们将简要介绍一下 HTTP 协议的演变历程。对不同 HTTP 语义的完整讨论超出了本书的范围,但是理解 HTTP 的关键设计变更以及每个变更背后的动机将为我们讨论 HTTP 性能提供必要的背景,特别是在当前即将到来的 HTTP/2 时代讨论其带来的许多改进。
HTTP 0.9——单行协议
Tim Berners-Lee 最初的 HTTP 提案在设计时考虑到了简单性,以帮助他实现他的另一个新想法——万维网。该策略似乎有效——有抱负的协议设计者,请注意。
1991年,Berners-Lee 概述了新协议的设计动机并列出了几个高级设计目标:文件传输功能,请求索引搜索超文本存档的能力,格式协商以及将客户端引用到另一个服务器的能力。为了证明该理论的实际应用效果,他构建了一个简单的原型,它实现了所提议功能的一小部分:
- 客户端请求是单行 ASCII 字符串
- 客户端请求行以 CRLF 结束
- 服务器响应是 ASCII 字符流
- 服务器响应体是一种超文本标记语言(HTML)
- 文档传输完成后终止连接
虽然,听起来很复杂。但这些规则创建的是一个非常简单的,Telnet 友好的协议,一些 Web 服务器在当天就支持了这一协议:
$> telnet google.com 80
Connected to 74.125.xxx.xxx
GET /about/
(hypertext response)
(connection closed)
该请求只由一行组成:GET 方法和所请求文档的路径。响应是单个超文本文档——没有标题或任何其他元数据,只有 HTML。它真的不能变得更简单。同时,由于这些规则是预期协议的子集,因此它非正式地命名为 HTTP 0.9 版本。其余的,正如他们所说,是历史了。
从1991年这些不起眼的规则开始,HTTP 开始了自己的生命,并在未来几年迅速发展。让我们快速回顾一下 HTTP 0.9 的特性:
- 客户端——服务端,请求——响应协议
- ASCII 协议,通过 TCP/IP 链路运行
- 旨在传输超文本文档(HTML)
- 每次请求后,服务器和客户端之间的连接都将关闭
流行的 Web 服务器,如 Apache 和 Nginx,仍然支持部分 HTTP 0.9 协议 – 原因是协议内容并没有多少!如果您好奇,请打开 Telnet 会话并尝试通过 HTTP 0.9 访问 google.com 或您自己喜欢的网站,并体验早期协议的行为和限制
HTTP/1.0——快速增长和信息化 RFC
1991年至1995年期间是 HTML 规范、一种被称为“网络浏览器”的新型软件的快速共同进化,以及面向消费者的公共互联网基础设施的出现和快速增长。
完美风暴:20世纪90年代初的互联网热潮
在 Tim Berner-Lee 最初的浏览器原型的基础上,国家超级计算应用中心(NCSA)的一个团队决定开发他们自己的版本。因此,第一个流行的浏览器诞生了——NCSA Mosaic。1994年10月,NCSA 团队的一名程序员Marc Andreessen 与 Jim Clark 合作创建了 Mosaic Communications。该公司后来改名为 Netscape,并于1994年12月发布了 Netscape Navigator 1.0。到目前为止,情况已经很明了了——万维网必然不仅仅是学术上的好奇心。
事实上,同年第一次万维网会议在瑞士日内瓦举办,同时意味着万维网联盟(W3C)诞生了,该组织帮助指导 HTML 的发展。同样,在 IETF 内部建立了并行 HTTP 工作组(HTTP-WG)——专注于改进 HTTP 协议。这两个组织持续帮助万维网的发展。
最后,为了创造完美风暴,CompuServe,AOL 和 Prodigy 开始在1994-1995这个相同的时间范围内向公众提供拨号上网服务。凭借这股迅速应用的浪潮,Netscape 在1995年8月9日以非常成功的 IPO 创造了历史——互联网热潮已经到来,每个人都想要它的一部分!
早期 Web 功能需求以及公共网站上访问数量的不断增加很快暴露了 HTTP 0.9 的许多基本限制——我们需要的协议不仅可以提供超文本文档,还可以提供有关请求和响应更丰富的元数据,启用内容协商等。反过来,早期 Web 开发人员社区在这一过渡时期设计了大量实验性的 HTTP 服务器和客户端实现——他们实现,部署,看看是否有其他人采用它。
从这段快速实验开始,一系列最佳实践和常见模式开始出现。1996年5月,HTTP 工作组(HTTP-WG)发布了 RFC 1945,它记录了当时出现的许多 HTTP/1.0 的“常见用法”。请注意,这只是写进了 RFC——HTTP/1.0,因为我们知道它不是正式规范或 Internet 标准!
话虽如此,示例 HTTP/1.0 请求看起来应该非常熟悉:
$> telnet website.org 80
Connected to xxx.xxx.xxx.xxx
GET /rfc/rfc1945.txt HTTP/1.0 #具有HTTP版本号的请求行,后跟请求头
User-Agent: CERN-LineMode/2.15 libwww/2.17b3
Accept: */*
HTTP/1.0 200 OK #响应状态,后跟响应头
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 01 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 1 May 1996 12:45:26 GMT
Server: Apache 0.84
(plain-text response)
(connection closed)
前面的例子只包含了 HTTP/1.0 协议部分内容,但它确实说明了一些协议关键改进。
- 请求可能包含多个换行符分隔的标题字段
- 响应添加了响应状态行作为前缀
- 响应本身有自己的一组换行符分隔的标题字段
- 响应内容不限于超文本
- 每次请求后,服务器和客户端之间的连接都将关闭
请求头和响应头都保存为 ASCII 编码,但响应内容本身可以是任何类型:HTML 文件,纯文本文件,图像或任何其他内容类型。因此,HTTP 的『超文本传输』名字在引入后不久就变成了用词不当。实际上,HTTP 已经迅速发展成为超媒体传输,但仍然沿用原有名称。
除了媒体类型协商之外,RFC 还记录了许多其他常用功能:内容编码,字符集支持,多部分类型,授权,缓存,代理行为,日期格式等。
今天,Web 上的几乎所有服务器都可以并且仍将使用 HTTP/1.0。除此之外,到现在为止,你应该知道更好!每个请求需要新的 TCP 连接会对 HTTP/1.0 造成严重的性能损失——依据于 TCP 拥塞控制的慢启动后的三次握手。
HTTP/1.1——互联网标准
将 HTTP 转变为官方 IETF 互联网标准的工作与围绕 HTTP/1.0 的文档工作并行进行,并发生在大约四年的时间内——1995年至1999年。事实上,第一个正式的 HTTP/1.1 标准定义于 RFC 2068,在 HTTP/1.0 发布大约六个月后于1997年1月正式发布。然后,两年半之后,即1999年6月,该标准中包含了许多改进和更新,并作为 RFC 2616发布。
HTTP/1.1 标准解决了早期版本中发现的许多协议歧义,并引入了许多关键性能优化:keepalive 连接,分块编码传输,字节范围请求,附加缓存机制,传输编码和请求流水线。
有了这些功能,我们现在可以查看由任何现代 HTTP 浏览器和客户端执行的典型 HTTP/1.1 会话
$> telnet website.org 80
Connected to xxx.xxx.xxx.xxx
#请求HTML文件,包含编码,字符集和cookie元数据
GET /index.html HTTP/1.1
Host: website.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4)... (snip)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: __qca=P0-800083390... (snip)
#原始HTML请求的分块响应
HTTP/1.1 200 OK
Server: nginx/1.0.11
Connection: keep-alive
Content-Type: text/html; charset=utf-8
Via: HTTP/1.1 GWA
Date: Wed, 25 Jul 2012 20:23:35 GMT
Expires: Wed, 25 Jul 2012 20:23:35 GMT
Cache-Control: max-age=0, no-cache
Transfer-Encoding: chunked
#块中的八位字节数表示为 ASCII 十六进制数
100
<!doctype html>
(snip)
100
(snip)
#分块流响应结束
0
#请求在同一TCP连接上创建的图标文件
GET /favicon.ico HTTP/1.1
Host: www.website.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4)... (snip)
Accept: */*
Referer: http://website.org/
Connection: close #通知服务器不会重用连接
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: __qca=P0-800083390... (snip)
#图标响应,然后关闭连接
HTTP/1.1 200 OK
Server: nginx/1.0.11
Content-Type: image/x-icon
Content-Length: 3638
Connection: close
Last-Modified: Thu, 19 Jul 2012 17:51:44 GMT
Cache-Control: max-age=315360000
Accept-Ranges: bytes
Via: HTTP/1.1 GWA
Date: Sat, 21 Jul 2012 21:35:22 GMT
Expires: Thu, 31 Dec 2037 23:55:55 GMT
Etag: W/PSA-GAu26oXbDi
(icon data)
(connection closed)
哎呀,那里有很多事情发生!第一个也是最明显的区别是我们有两个对象请求,一个用于 HTML 页面,另一个用于图像,两者都通过单个连接传递。这是连接 keepalive 的实际应用,它允许我们重用现有的 TCP 连接,以便对同一主机发出多个请求,并提供更快的最终用户体验;请参阅优化TCP。
要终止持久连接,请注意第二个客户端请求通过 Connection 字段向服务器发送显式关闭令牌。类似地,一旦传输响应,服务器就可以通知客户端关闭当前 TCP 连接的意图。从技术上讲,任何一方都可以在任何时候都不通过这样的方式来终止 TCP 连接,但是客户端和服务器应该尽可能地提供它以在双方上实现更好的连接重用策略。
HTTP/1.1 默认情况下将 HTTP 协议的语义更改为使用连接 keepalive。这意味着,除非另有说明(通过Connection: close 头部),服务器应默认保持连接打开。
但是,同样的功能也被反向移植到 HTTP/1.0 并通过 Connection: Keep-Alive 头部启用。因此,如果您使用 HTTP/1.1,从技术上讲,您不需要 Connection: Keep-Alive 头部,但许多客户仍然选择提供它。
此外,HTTP/1.1 协议添加了内容,编码,字符集,甚至语言协商,传输编码,缓存指令,客户端cookie,以及可以在每个请求上协商的十几种其他功能。
我们不打算详述每个 HTTP/1.1 功能的语义。这已经是可以专门用来写书的主题,同时市面上已经有很多很棒的书。相反,前面的示例可以很好地说明 HTTP 的快速进展和演变,以及每个客户端 – 服务器交换的错综复杂的舞蹈。那里有很多事情发生!
有关 HTTP 协议所有内部工作原理的详细参考,请查看 David Gourley 和 Brian Totty 撰写的 O’Reilly 的《HTTP 权威指南》。
HTTP/2——提高运输性能
自发布以来,RFC 2616 已经成为互联网空前增长的基础:数十亿台各种形状和大小的设备,从台式电脑到我们口袋里的小型网络设备,每天都会用 HTTP 来传送新闻,视频,以及数以百万计的我们生活中依赖的其他网络应用程序。
最初用于检索超文本的简单单行协议很快演变成通用的超媒体传输,现在十年后可以用来为你能想象的任何应用提供支持。大量服务器和广泛使用的客户端意味着现在许多应用程序都是专门在 HTTP 之上设计和部署的。
需要一个协议来控制你的咖啡壶?RFC 2324 已经涵盖了超文本咖啡壶控制协议(HTCPCP / 1.0) – 原本是 IETF 的愚人节笑话,并且在我们新的超链接世界中会有越来越多的笑话。
超文本传输协议(HTTP)是用于分布式的协作超媒体信息系统的应用程序级协议。它是一种通用的无状态协议,可以通过扩展其请求方法,错误代码和标头,用于超出其用于超文本的许多任务,例如名称服务器和分布式对象管理系统。 HTTP 的一个特性是数据表示的输入和协商,允许系统独立于正在传输的数据而构建
HTTP 协议的简单性使其成为最初的应用方案和快速增长。事实上,现在使用 HTTP 作为主要控制和数据协议的嵌入式设备(传感器、执行器和咖啡壶)并不罕见。但在它自身成功的重压下,随着我们越来越多地将日常互动迁移到网络上——社交、电子邮件、新闻和视频,以及包括越来越多的我们的整个个人和工作空间——它也开始显示出压力的迹象。用户和 web 开发人员现在都要求 HTTP/1.1 提供近乎实时的响应和协议性能,如果不进行一些修改, 它根本无法满足这些响应能力和协议性能。
为了应对这些新的挑战,HTTP 必须继续发展,因此,HTTPbis 工作组于2012年初宣布了 HTTP/2 的一项新举措:
在没有 HTTP/1.x 消息机制和语法的情况下保留 HTTP 语义的协议促成了新的实践经验和兴趣,这些机制和语法已被确定为妨碍了性能并鼓励滥用基础运输。
工作组将在当前有序的、双向的 HTTP 连接中生成 HTTP 当前语义的新规范。与 HTTP/1.x 一样,主要目标传输是 TCP,但应该可以使用其他传输。
HTTP/2 的主要重点是提高传输性能,实现更低的延迟和更高的吞吐量。新协议版本号的主要版本序号增加听起来是一个很大的更新。单就性能而言,确实现在和将来的变化都是一个很大的更新。但请注意,所有高级协议语义都不会受到影响:所有 HTTP 头部字段、值和用例都是相同的。
任何现有的网站或应用程序都可以而且将通过 HTTP/2 交付,而无需修改:您不需要修改应用程序标记以利用 HTTP/2。HTTP 服务器必须使用 HTTP/2,但这应该是对大多数用户的透明升级。如果工作组实现了其目标,唯一的区别应该是我们的应用程序以更低的延迟和更好地利用网络链接的方式交付!
话虽如此,但我们不要走在前面。在我们使用新的 HTTP/2 协议之前,值得退一步,研究我们现有的 HTTP/1.1 自身的部署和性能最佳实践。HTTP/2 工作组在新规范方面进展迅速,但即使最终标准已经完成并准备就绪,在可预见的未来(实际上, 十年或更长时间),我们仍需使用较旧的 HTTP/1.1。