HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP 是 WWW(World Wide Web,全球广域网)万维网的数据通信基础。
HTTP 协议简介
起源
设计 HTTP 协议最初的目的是为了提供一种发布和接收 HTML 页面的方法,现在已然是短连接通信的事实工业标准。通过 HTTP 或者 HTTPS 协议请求的资源由 URI (Uniform Resource Identifiers,统一资源标识符)来标识。
HTTP 协议是由 蒂姆·伯纳斯-李(Tim Berners-Lee)于 1989 年在 CERN (European Organization
for Nuclear Research,欧洲核子研究组织)所发起。HTTP 的标准制定由 W3C (World Wide Web Consortium,万维网协会)和 IETF (Internet Engineering Task Force,互联网工程任务组)进行协调,最终发布了一系列的 RFC(Request For Comments,请求评论)可以理解为“协议规范”。
演进
超文本传输协议已经演化出了很多版本,它们中的大部分都是向下兼容的。在 RFC 2145 中描述了 HTTP 版本号的用法。客户端在请求的开始告诉服务器它采用的协议版本号,而后者则在响应中采用相同或者更早的协议版本。
HTTP/0.9
这是 HTTP 于 1991 年推出的首个可用协议标准,此版本协议内容很简单,支持的方法更是廖少,仅支持 GET
一种方法,使用 80 端口,服务器仅可响应 HTML 格式的数据,发送完毕后关闭 TCP 连接。
序号 | 方法 | 引入版本 | 描述 |
---|---|---|---|
1 | GET | HTTP/0.9 | 请求指定的页面,并返回页面内容 |
实际上 HTTP 协议问世时,并未制定版本号,HTTP/0.9 是后来为了方便区分才给定义的。
HTTP/1.x
在 1996 年 5 月 HTTP 协议迎来一次重大升级:HTTP/1.0 发布(详见 RFC1945)。首先传输内容不再受限于 HTML,图片、二进制文件等也可传输,这为互联网的大发展奠定了基础。其次方法不再仅有 GET 方法,额外支持了 POST 和 HEAD 方法,增加了于浏览器间的互动手段。
序号 | 方法 | 引入版本 | 描述 |
---|---|---|---|
2 | HEAD | HTTP/1.0 | 与 GET 相同,但只返回 HTTP 报头,返回的响应并不包含页面内容。 |
3 | POST | HTTP/1.0 | 向指定的资源提交要被处理的数据(提交表单,上传文件),数据将被包裹在请求体中。 |
在 1999 年 6 月,HTTP/1.1 发布(详见 RFC2616),【实际上 HTTP/1.0 发布后半年的 1997 年 1 月,HTTP/1.1 草案就已提交】进一步完善了 HTTP 协议,此版本一直沿用至今,截至目前仍是互联网中主流版本。
小贴士:在此版本中添加了很多特性,解决了 HTTP/1.0 中的缺点,以下选择部分重点特性进行说明。
持久连接,在 HTTP/1.0 中每个请求都会发起一个 TCP 连接,发送完成后立即关闭,因为 TCP 协议复杂的握手流程导致每个连接的新建成本很高(主要指延迟),在 HTTP/1.1 中,TCP 连接默认不中断,可被多个请求复用,客户端和服务端发现对方长时间无活动后才会关闭连接,不过,规范的做法是:客户端在最后一个请求时,发送 Connection: close
标识,要求服务器关闭连接,这个特性极大提高了连接的利用率,但是随之而来的问题就是会明显提高服务端的资源占用,部分请求即使请求并不活跃也会占用一个或多个 TCP 连接。
内容长度,因为持久连接特性的加入,引发了一个新问题:就是一个连接被多个请求复用,客户端无法确认当前的数据属于哪一个请求,因此引入了 Content-Length
字段,用来声明本次回应的数据长度,以区分不同的请求内容。
分块传输,使用内容长度字段的前提条件是,服务器返回响应之前,必须知道响应的数据长度。因此对于一些很耗时的动态操作来说,这意味着服务器要等到所有操作完成,才能发送数据,显然这样的效率不高。于是引入了分块传输编码 Transfer-Encoding: chunked
字段,产生一块数据,就发送一块(即“数据流模式”),在 HTTP/1.1 中可以不指定 Content-Length
字段,只要请求或回应的头信息有分块传输编码字段,就表明接下来的响应将由数量未定的数据块组成,在每个非空的数据块之前,会有一个16进制的数值,表示这个块的长度,最后使用一个大小为零的块表示本次响应数据发送完毕。
队头阻塞,HTTP/1.1 的缺点也因为 TCP 连接复用导致同一个连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。如果前面的回应特别慢,后面就会有许多请求排队等着,这称为"队头堵塞"(Head-of-line blocking)。
主机字段,客户端请求的头信息新增了主机 Host: example.com
字段,用来指定请求服务器的域名,有了主机字段,就可以将请求发往同一台服务器上的不同网站,为虚拟主机的兴起打下了基础。
此外还引入了更多的请求方法:
序号 | 方法 | 引入版本 | 描述 |
---|---|---|---|
4 | PUT | HTTP/1.1 | 向指定资源位置上传其最新内容。 |
5 | DELETE | HTTP/1.1 | 请求服务端删除指定资源。 |
6 | CONNECT | HTTP/1.1 | 把请求连接转换到透明的 TCP/IP 通道,通常用于SSL加密服务器的链接。 |
7 | OPTIONS | HTTP/1.1 | 返回服务器支持的 HTTP 方法。 |
8 | TRACE | HTTP/1.1 | 回显服务器收到的请求,用于诊断和测试。 |
小贴士:至此,HTTP/1.1 中定义的八种方法(也称为动作)介绍完毕,这些新增的方法扩充了操作指定资源的方式,完善了使用的体验。方法名称是区分大小写的。当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码 405 (Method Not Allowed) ,当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码 501 (Not Implemented) 。一般情况下服务器至少应该实现 GET 和 HEAD 方法,其他方法皆为可选。此外,除了上述方法,特定的 HTTP 服务器还能够扩展自定义的方法。例如:在 2010 年引入的 PATCH
方法(详见 RFC5789)用于将局部修改应用到资源。
HTTP/2
在 2014 年 12 月,互联网工程任务组的 HTTPBIS (Hypertext Transfer Protocol Bis)工作组将 HTTP/2 标准提议递交至 IESG 进行讨论,于 2015 年 2 月 17 日被批准。直至 2015 年 5 月 HTTP/2 发布(详见 RFC7540)取代 HTTP/1.1 成为 HTTP 的实现标准。
小贴士:说到 HTTP/2 协议就不得不提谷歌的 SPDY 协议,在 2009 年谷歌公开了自行研发的 SPDY 协议(含义及发音皆为 SPeeDY)主要解决 HTTP/1.1 效率不高的问题。世界上最大的视频网站 YouTube 最先进行了 SPDY 协议的尝鲜,这个协议在 Chrome 浏览器上证明可行以后,就被当作 HTTP/2 的基础,主要特性都在 HTTP/2 之中得到继承。SPDY 并不是新的一种协议,而是在 HTTP 之前做了一层会话层。为了达到减少页面加载时间的目标,SPDY 引入了一个新的二进制分帧数据层,以实现优先次序、最小化及消除不必要的网络延迟,目的是更有效地利用底层 TCP 连接。
在 2015 年,互联网标准委员会不打算再发布 HTTP/2 子版本了,于是 HTTP/2 不写作 HTTP/2.0 ,下一个新版本将是 HTTP/3。
HTTP/2 版本中添加了很多特性,解决了 HTTP/1.1 中的缺点,以下选择部分重点特性进行说明。
- 二进制传输:传输数据流全部二进制编码传输,在 HTTP/1.1 中,头信息为文本格式,使用 ASCII 编码,数据体则不做限制,既可为文本,也可为二进制格式。在 HTTP/2 中则彻底变为二进制,头信息和数据体都是二进制,并且统称为"帧"(frame):头信息帧和数据帧。二进制协议的一个好处是,可以定义额外的帧。HTTP/2 定义了近十种帧,为将来的高级应用打好了基础。如果使用文本实现这种功能,解析数据将会变得非常麻烦,二进制解析则更加快速。
数据流:如上图所示,HTTP/2 将每个请求或回应(统称为“消息”)的所有数据包,称为一个数据流(stream)。而消息又由一个或多个“帧”组成。每个帧头部都自带“流标识”,根据标识可以重新组装为完整的数据流,因此可以实现乱序发送。另外还规定,客户端发出的数据流标识一律为奇数,服务端发出的数据流标识一律为偶数。数据流发送途中,客户端和服务端都可以发送信号(RST_STREAM帧),取消这个数据流。HTTP/1.1 取消数据流的唯一方法,就是关闭 TCP 连接。这就是说,HTTP/2 可以取消某一次请求,同时保证 TCP 连接还打开着,可以被其他请求使用。客户端还可以指定数据流的优先级:优先级越高,服务端优先进行处理和回应。
多路复用:在 HTTP/2 中复用 TCP 连接,同域名下的所有通信全部在单个 TCP 连接中完成,并且连接可以承载任意数量的双向数据流。在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了"队头堵塞"。举例来说,在一个 TCP 连接里,服务器同时收到了 A 请求和 B 请求,于是先回应 A 请求,结果发现处理过程非常耗时,于是就发送 A 请求中已经处理好的部分,接着回应 B 请求,完成后,再发送 A 请求剩下的部分,这种双向的、实时的通信,就叫做多工(Multiplexing)。并且在 HTTP/2 中,每个请求都可以带一个 31 bit 的优先值,数值越大优先级越低,0 表示最高优先级。有了这个优先值,客户端和服务器就可以在处理不同流时采取不同的策略,以最优的方式发送流、消息和帧。
因此采用了多路复用技术后,网页加载速度有了非常大的提升,H/2 速度对比可以在网站 Akamai HTTP/2 demo 中进行体验,注意浏览器必须支持 H/2 协议才能正常进行测试。
消息头压缩:由于 HTTP 协议是无状态协议,每次请求都必须附上全部的请求头 Header。所以,请求头的很多字段都是重复的,比如 cookie 和 User Agent,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。HTTP/2 对这一点做了优化,引入了头信息压缩机制。一方面,头部信息压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,同样字段不发送具体信息,只发送索引号,这样就极大提高速度,且节约带宽。
小贴士:为什么说 HTTP 协议是无状态协议?无状态是指协议对于事务处理没有记忆功能。缺少状态意味着,假如后面的处理需要前面的信息,则前面的信息必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要前面信息时,应答就较快。直观地说,就是每个请求都是独立的,与前面的请求和后面的请求都是没有直接联系的,就是因为如此,所以 HTTP 协议中引入了 cookie 和 session 等来实现有状态的连接。
服务器推送:HTTP/2 允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送(Server Push)。常见场景是客户端请求一个网页,这个网页里面包含很多静态资源。正常情况下,客户端必须收到网页后,解析 HTML 源码,发现有静态资源,再发出静态资源请求。在 HTTP/2 中服务器可以预期到客户端请求网页后会继续请求静态资源,所以就主动把这些静态资源随着网页一起发给客户端。
- 安全性提升:在 HTTP/1.1 及之前,协议自身无加密,若实现加密需要借助 TLS 层实现,也称 HTTPS ,在 HTTP/2 标准中并不要求必须 TLS 加密,但是 Chrome Firefox 等主流浏览器发布声明,不支持非 TLS 的 HTTP/2 ,因此实际上 H/2 变成了事实上的强制要求加密。
HTTP/3
说到 HTTP/3 就不得不再次提到谷歌,没错还是它!
QUIC (Quick UDP Internet Connections)是由 Google 从 2013 年开始研究的基于 UDP 的可靠传输协议,它最早的原型是 SPDY + QUIC-Crypto + Reliable UDP,后来经历了 SPDY 转型为 2015 年 5 月 IETF 正式发布的 HTTP/2.0,以及 2016 年 TLS/1.3 的正式发布。2016 年成立,IETF 的 QUIC 标准化工作组启动,考虑到 HTTP/2.0 和 TLS/1.3 的发布,它的核心协议族逐步进化为现在的 HTTP/3.0 + TLS/1.3 + QUIC-Transport 的组合。
在说 HTTP/3 之前先说一说 HTTP/2 的不足,每次版本更新都是为了解决前一代存在的问题,或者扩充其特性,由于 HTTP/2 及之前的协议都是基于 OSI 模型中第四层传输层协议 TCP 协议实现的。因此 TCP 协议存在的问题在 HTTP/2 中也都存在。
HTTP/2 的缺点主要有如下两点:
- TCP 连接时的握手延迟较大,包括 HTTPS 中 TLS 证书交换和 TLS 建立的延时。
- TCP 队头阻塞问题依然存在。
小贴士:为什么是三次握手?因为三次握手是建立可靠连接(双向确认ACT)所需的最少次数。
很显然,这些问题主要都是底层支撑的 TCP 协议造成的。HTTP/2 都是使用 TCP 协议来传输的,而如果使用 HTTPS (HTTP over TLS) 的话,还需要使用 TLS 协议进行安全传输,而使用 TLS 也需要一个握手过程,这样就需要有两个握手延迟过程:
- 建立 TCP 连接耗时,需要和服务器进行三次握手来确认连接成功,也就是说需要在消耗完 1.5 个 RTT 周期之后才能进行数据传输。
- 建立 TLS 连接耗时,TLS 有两个版本:TLSv1.2 和 TLSv1.3,不同版本建立连接所需 RTT 时间不同,大致需要 1~2 个 RTT 周期。
小贴士:RTT(Round-Trip Time,往返时延),是指数据从网络一端传到另一端所需的时间。
总之,在真正传输数据之前,就已经消耗了 3~4 个 RTT 周期,以在中国大陆访问美国的本土网站为例,中美直连的链路一般需要 100ms 时间(根据所处大陆地域不同存在些许差别,在北上广等国际出口城市)。因此在传输网络数据前,仅仅是建立 HTTPS 连接的 RTT 时间已经消耗了近 500ms 的时间。
上文在介绍 HTTP/2 时就提到了,HTTP/2 的一大改进就是多路复用,让多个请求存在于同一个 TCP “管道”中,如果网络连接差,比如存在大量丢包时,此时 HTTP/2 的性能还不如 HTTP/1 ,因为 TCP 协议的可靠特点,当出现丢包时,会触发重传机制,丢失的包会重新传输一次或者多次,直到接收方确认为止,当 HTTP/2 出现丢包时,整个 TCP 连接都要开始等待重传,就会阻塞此 TCP 管道中的所有请求。而对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。
实际上谷歌在开发 SPDY 协议时就意识到它存在的不足,因此同时开发了一个基于另一个传输层协议 UDP 的的 QUIC 协议,此协议就是 HTTP/3 的基础模型。在 2018 年 11 月互联网正式批准了 HTTP-over-QUIC 成为下一代 HTTP/3 协议,其在 HTTP/2 的基础上实现了很多飞跃,较完美的解决了队头阻塞问题。
由于 HTTP/3 基于用户数据报协议(UDP,User Datagram Protocol),此协议相比较于 TCP 可靠连接,最大的特点就是无状态、不可靠。因此可以实现 O 个 RTT 周期完成连接并开始传输数据,也就解决了队头阻塞问题。
HTTP/3 版本中添加了很多特性,解决了 HTTP/2 中的缺点,以下选择部分重点特性进行说明。
0-RTT:传输层和加密层只需 0-RTT 时间即可建立,无需 TCP 的多个 RTT 时间才能创建完毕。
多路复用:可能读者很好奇为什么会再次提到这个,在 HTTP/2 中不是已经实现了吗?这是因为 HTTP/2 中的实现依赖于 TCP 协议,而 HTTP/3 中换用了 UDP 协议,因此“多路复用”需要重新实现一遍,QUIC 原生就实现了这个功能,并且传输的单个数据流可以保证有序交付且不会影响其他的数据流。同一条 QUIC 连接上可以创建多个 stream,来发送多个 HTTP请 求,但是 QUIC 是基于 UDP 的,同一个连接上的多个 stream 之间没有依赖。比如下图中 stream2 丢了,不会影响后面跟着 stream3 和 stream4,不存在 TCP 队头阻塞问题。虽然 stream2 需要重传,但是stream3、stream4 无需等待,可以直接发给用户。
报文加密:TCP 协议头部是无加密的,传输过程中可能被中间设备篡改、注入、监听。虽然有些篡改是出于性能优化,但是也表示这是不安全的隐患。在 HTTP/3 的 QUIC 中,每个 Packet 都是加密传输的(除了 PUBLIC_RESET 和 CHLO 外)。并且所有的报文 Header 都是有认证、校验的,报文 Boby 为完全加密。如下图,红色部分是 Stream Frame (流帧)的报文头部,绿色部分是报文内容。
- 向前纠错:QUIC 协议有一个非常独特的特性,称为向前纠错 (Forward Error Correction,FEC),每个数据包除了它本身的内容之外,还包括了部分其他数据包的数据,因此少量的丢包可以通过其他包的冗余数据直接组装而无需重传。向前纠错牺牲了每个数据包可以发送数据的上限,但是减少了因为丢包导致的数据重传,因为数据重传将会消耗更多的时间(包括确认数据包丢失、请求重传、等待新数据包等步骤的时间消耗),当然如果出现大量的包丢失,则 FEC 也无能为力,只能请求重传。
目前主流浏览器已经支持了 HTTP/3 ,但是需要服务端的支持,这将是一个较为漫长的过程。
常用 HTTP 服务
常用网页服务
- Apache HTTPd by Apache Software Foundation
- NGINX by NGINX Inc.
- IIS(Internet Information Server) by Microsoft Inc.
- Google Web Server by Google Inc.
- lighttpd by lighttpd Inc.
- Cherokee
市场占有率
Developer | June 2018 | Percent | July 2018 | Percent | Change |
---|---|---|---|---|---|
Apache | 76,721,597 | 35.23% | 76,312,577 | 34.60% | -0.63 |
Microsoft | 56,030,491 | 25.73% | 57,395,894 | 26.02% | 0.29 |
NGINX | 50,860,718 | 23.35% | 50,246,244 | 22.78% | -0.57 |
1,909,658 | 0.88% | 1,948,858 | 0.88% | 0.01 |
附录
状态码表
参考链接
- Evolution of HTTP — HTTP/0.9, HTTP/1.0, HTTP/1.1, Keep-Alive, Upgrade, and HTTPS - Medium
- Is TLS mandatory for HTTP/2? - StackOverflow
- HTTP/2笔记之流和多路复用 - 聂永的博客
- 解读HTTP/2与HTTP/3 的新特性 - CSDN
- HTTP协议之版本差异(2) - 博客园
- Hypertext Transfer Protocol - WikiPedia
- HTTP协议图文简述--HTTP/HTTPS/HTTP2 - 博客园
本文由 柒 创作,采用 知识共享署名4.0
国际许可协议进行许可。
转载本站文章前请注明出处,文章作者保留所有权限。
最后编辑时间: 2018-08-04 06:57 AM
写的不错,get 了