HTTP的进化史

374 阅读21分钟

HTTP在万维网起源中的部分 1989年,经历了多次迭代,本文较为详细地梳理了HTTP发展中几个比较重要的版本迭代,以HTTP迭代的主线为主,不会过于详细,如果需要探究技术细节可以进一步查阅资料

HTTP在万维网起源中的部分

1989年,Tim Berners-Lee 博士写了一份关于建立一个通过网络传输超文本系统的报告。这个系统起初被命名为 Mesh

在随后的1990年项目实施期间被更名为万维网(World Wide Web)。 它在现有的TCP和IP协议基础之上建立,由四个部分组成:

  • 一个用来表示超文本文档的文本格式,超文本标记语言(HTML)。
  • 一个用来交换超文本文档的简单协议,超文本传输协议(HTTP)。
  • 一个显示(以及编辑)超文本文档的客户端,即浏览器

HTTP/0.9 – 单行协议

最初版本的HTTP协议并没有版本号,后来它的版本号被定位在 0.9 以区分后来的版本

HTTP/0.9 的特点

  1. 请求
  • 请求由单行指令构成,以唯一可用方法 GET 开头,其后跟目标资源的路径(请求文件的办法 + 路径)
  • 无 HTTP 标头(无法传输其他内容类型文件)、无状态/错误代码、无 URL、无版本控制。 一旦连接到服务器,协议、服务器、端口号这些都不是必须的。
GET /mypage.html
  1. 响应
  • 只包含响应文档本身, 也就是说只返回 超文本。
  • HTTP/0.9 的响应内容并不包含HTTP头,这意味着只有HTML文件可以传送,无法传输其他类型的文件
  • 没有状态码或错误代码:一旦出现问题,一个特殊的包含问题描述信息的HTML文件将被发回,供人们查看。
<HTML>
这是一个非常简单的HTML页面
</HTML>

HTTP/1.0

1996年11月,一份新文档 RFC 1945 定义了 HTTP/1.0,但它是狭义的,并不是官方标准。

HTTP/0.9 的问题

非常僵化,不具备扩展性。 HTTP/1.O 最大的改进就是为其增加扩展性

HTTP/1.0 的改进方案

1. 引入了HTTP头的概念

无论是对于请求还是响应,允许传输元数据,使协议变得非常灵活,更具扩展性。提供的头字段包括关于请求和响应的丰富元数据(HTTP 版本号、状态码、内容类型)

  • 状态码会在响应开始时发送,使浏览器能了解请求执行成功或失败,并相应调整行为(如更新或使用本地缓存)。
  • 在新HTTP头的帮助下,具备了传输除纯文本HTML文件以外其他类型文档的能力(Content-Type头)。任何格式的内容都可以发送。这使得互联网不仅可以传输文字,还能传输图像、视频、二进制文件。

2. 增加支持方法

除了GET命令,还引入了POST命令和HEAD命令,丰富了浏览器与服务器的互动手段。

3. 其他的新增功能

多字符集支持、多部分发送(multi-part type)、权限(authorization)、缓存(cache)、内容编码(content encoding)等。

(Connection 1 Establishment - TCP Three-Way Handshake) 
Connected to xxx.xxx.xxx.xxx

(Request) 
GET /my-page.html HTTP/1.0 
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)

(Response)
HTTP/1.0 200 OK 
Content-Type: text/html 
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

<HTML> 
A page with an image
  <IMG SRC="/myimage.gif">
</HTML> 
(Connection 1 Closed - TCP Teardown) 

------------------------------------------
(Connection 2 Establishment - TCP Three-Way Handshake) 
Connected to xxx.xxx.xxx.xxx 

(Request)
GET /myimage.gif HTTP/1.0
User-Agent: NCSA_Mosaic/2.0 (Windows 3.1)

 (Response) 
HTTP/1.0 200 OK 
Content-Type: text/gif 
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[image content] 
(Connection 2 Closed - TCP Teardown) 

HTTP 1.1

1997年初,HTTP1.1 标准发布,消除了大量歧义内容并引入了多项改进

HTTP/1.0 的问题

每个TCP连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接,TCP连接的新建成本很高,因为需要客户端和服务器三次握手。

HTTP/1.1 的改进方案

改进不断建立请求的问题

1. 长连接

  • 在一个TCP连接上可以传送多个HTTP请求,避免了因为多次建立TCP连接的时间消耗和延时。比如我们有两个对象请求,一个用于 HTML 页面,一个用于图像,两者都通过单个连接传递。这是连接keepalive在起作用,它允许我们为对同一主机的多个请求重用现有的TCP连接,并提供更快的最终用户体验;
  • Keep-Alive 标头在 HTTP/1.1 之前使用,并被 HTTP/1.1 废弃,使持久连接成为默认行为,不用声明Connection: keep-alive。 此外,主机可以添加 timeout 和 max 参数以设置超时或限制每个连接的最大请求计数。
  • 客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求时,发送Connection: close,明确要求服务器关闭TCP连接。
  • 所有连接都是独立协商的。如下图所示,客户端指示超时为 600 秒,但代理仅准备将连接保留至少 120 秒。在代理和服务器之间的链路上,代理请求超时 1200 秒,服务器将其减少到 300 秒。如本例所示,代理维护的超时策略对于每个连接都是不同的。每个连接跳都是独立的

image.png

2. 管道机制(pipelining)

  • 在同一个TCP连接里面,客户端可以同时发送多个请求。这样就进一步改进了HTTP协议的效率。
  • 举例来说,客户端需要请求两个资源。以前的做法是,在同一个TCP连接里面,先发送A请求,然后等待服务器做出回应,收到后再发出B请求。管道机制则是允许浏览器同时发出A请求和B请求,但是服务器还是按照顺序,先回应A请求,完成后再回应B请求。

3. 并发连接

  • 对于一个域名允许分配多个长连接,那么相当于增加了任务队列,不至于一个队伍的任务阻塞其它所有任务。长连接数量是2-6个。

4. 域名分片

  • Host头 使得不同域名配置在同一个IP地址的服务器上。
  • 使用CDN的实现域名分片机制,一个域名只是可以并发 6 个长连接,所以可以域名分片增加长连接数目。
  • 比如 content1.sanyuan.com 、content2.sanyuan.com。这样一个sanyuan.com域名下可以分出非常多的二级域名,而它们都指向同样的一台服务器,能够并发的长连接数更多了,事实上也更好地解决了队头阻塞的问题。

响应数据分块

1. 客户端通过Range头部传递请求范围

The Range 是一个请求首部,告知服务器返回文件的哪一部分。在一个  Range 首部中,可以一次性请求多个部分,服务器会以 multipart 文件的形式将其返回。

Range: bytes=200-1000, 2000-6576, 19000-

2. 服务器 如果服务器返回的是范围响应,需要使用 206 Partial Content 状态码,其中Content-Range头部显示当前片段响应体在完整包体中的位置。假如所请求的范围不合法,那么服务器会返回 416 Range Not Satisfiable 状态码,表示客户端错误。

响应头部Content-Type: multipart/byteranges; boundary=...

服务器允许忽略  Range  首部,从而返回整个文件,状态码用 200。

缓存字段的改变

  1. 强缓存 http1.0:Expires
  • Expires是一个绝对时间。用以表达在这个时间点之前发起请求可以直接从浏览器中读取数据,而无需发起请求

http1.1:Cache-Control

  • Cache-control是一个相对时间,用以表达自上次请求正确的资源之后的多少秒的时间段内缓存有效。
  • Cache-Control的优先级比Expires的优先级高。前者的出现是为了解决Expires在浏览器时间被手动更改导致缓存判断错误的问题。
  • 为了兼容 HTTP/1.0 和 HTTP/1.1,实际项目中两个字段都可以设置。

使用 HTTP/1.1 中引入的 Upgrade 标头

  • 可以使用常用协议(例如 HTTP/1.1)启动连接,然后请求连接切换到增强的协议类型,例如 HTTP/2.0 或 WebSockets。
  • 在升级的协议连接中,不存在 max 参数(最大请求数)。升级后的协议可以为超时参数提供新的策略(如果没有特别定义,则使用底层协议中的默认超时值)。

引入内容协商机制

包括语言,编码,类型等,并允许客户端和服务器之间约定以最合适的内容进行交换。

1. 数据格式:Content-Type针对发送端;Accept针对接收端

  • text: text/html, text/plain, text/css 等
  • image: image/gif, image/jpeg, image/png 等
  • audio/video: audio/mpeg, video/mp4 等
  • application: application/json, application/javascript, application/pdf, application/octet-stream

2. 压缩方式:Content-Encoding针对发送方,Accept-Encoding针对接收方

  • gzip: 当今最流行的压缩格式
  • deflate: 另外一种著名的压缩格式
  • br: 一种专门为 HTTP 发明的压缩算法

3. 支持语言

  • 对于发送方而言,还有一个Content-Language字段,在需要实现国际化的方案当中,可以用来指定支持的语言,在接受方对应的字段为Accept-Language

4. 字符集

  • 在接收端对应为Accept-Charset,指定可以接受的字符集,而在发送端并没有对应的Content-Charset, 而是直接放在了Content-Type中,以charset属性指定。

HTTP的扩展(HTTP2之前)

由于HTTP协议的可扩展性 – 创建新的头部和方法是很容易的

RFC 2616 发布于1999年6月,而另外两个文档 RFC 7230, RFC 7235发布于2014年6月,

HTTPS

HTTP的问题

HTTP是超文本传输协议,是明文传输, Web 最早几乎是一个学术网络,相对信任度很高,但如今不得不面对一个险恶的丛林,对TLS的需求也变得普遍。

HTTPS的改进方案

HTTPS是经过SSL加密的协议,也就是使用 传输层安全性(TLS)或安全套接字层(SSL) 对通信协议进行加密。也就是 HTTP + SSL(TLS) = HTTPS。 HTTP在基本的TCP/IP协议栈上发送信息,网景公司在此基础上创建了一个额外的加密传输层:SSL 。SSL在标准化道路上最终成为TLS。目前 TLS 1.3 目前正在形成。

1. HTTP和HTTPS的区别

  • HTTPS使用443端口,而HTTP使用80,443端口用于网页浏览,关闭电脑443端口,将会导致https网页无法正常打开。
  • HTTPS需要申请证书
  • HTTP是超文本传输协议,是明文传输;HTTPS是经过SSL加密的协议
  • HTTPS比HTTP慢,因为HTTPS除了TCP握手的三个包,还要加上SSL握手的九个包

2. HTTPS的过程

  • 服务器将自己的公钥给证书认证机构CA,机构用自己的私钥加密 服务器的公钥和机构自己生成的数字签名,然后把数字签名发送给服务器
  • Client使用HTTPS的URL访问Web服务器,要求与Web服务器建立SSL连接。Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
  • 各大浏览器和操作系统已经维护了所有权威证书机构的名称和公钥。客户端在本地找到对应的机构公钥,解密出本地证书签名,和服务端传递过来的证书签名进行对比,如果对比成功,则用机构公钥解密出服务器公钥。
  • 客户端生成自己的对称加密密钥,且用服务端密钥对其进行加密(公钥加密密钥),发送加密后的对称密钥
  • Web服务器利用自己的私钥解密出会话密钥。
  • Web服务器利用会话密钥加密与客户端之间的通信。

image.png


HTTP 协议复杂化

WebSocket

1. websocket实际上是HTML5的协议,而不是HTTP协议进行了修改

  • HTTP 1.1使用了keep-alive,把多个HTTP请求合并为一个,但是这和websocket并没有关系。也可以说websocket实际上是HTTP协议上的一种补充
  • websocket是HTML5开始提供的一种浏览器与服务器进行全双工通讯的技术,属于应用层协议。它基于TCP传输协议,并复用HTTP的握手通道,也就是说借用了HTTP的协议来完成一部分握手。

2. websocket解决的问题

  • 当服务器完成协议升级后(HTTP->Websocket),服务端就可以主动推送信息给客户端。只需要经过一次HTTP请求,就可以做到源源不断的信息传送了。(在程序设计中,这种设计叫做回调,即:你有信息了再来通知我,而不是我傻乎乎的每次跑来问你)。这样的协议解决了上面同步有延迟,而且还非常消耗资源的这种情况。
  • Websocket只需要一次HTTP握手,所以说整个通讯过程是建立在一次连接/状态中,也就避免了HTTP的非状态性,服务端会一直知道你的信息,直到你关闭请求,这样就解决了Nginx要反复解析HTTP协议,还要查看identity info的信息

3. 如何建立连接

  • 客户端 客户端发起协议升级请求——采用的是标准的HTTP报文格式,且只支持GET方法。 下面请求省略了部分非重点请求首部。由于是标准的HTTP请求,类似Host、Origin、Cookie等请求首部会照常发送。在握手阶段,可以通过相关请求首部进行 安全限制、权限校验等。

image.png

  • 服务端

返回101状态码,其他字段基本与客户端发送的对应字段一致

image.png


HTTP 2

HTTP 1.1的问题

1. TCP慢启动

慢启动(Slow Start),是传输控制协议TCP)使用的一种阻塞控制机制。慢启动也叫做指数增长期。慢启动是指每次TCP接收窗口收到确认时都会增长。 增加的大小就是已确认段的数目。

TCP连接建立后,会经历一个先慢后快的发送过程,就像汽车启动一般,如果我们的网页文件(HTML/JS/CSS/icon)都经过一次慢启动,对性能是不小的损耗。另外慢启动是TCP为了减少网络拥塞的一种策略,我们是没有办法改变的。

2. 多条TCP连接竞争带宽

对于同一个域名,浏览器最多只能同时创建 6~8 个 TCP 连接 (不同浏览器不一样)。为了解决数量限制,出现了 域名分片 技术,其实就是资源分域,将资源放在不同域名下 (比如二级子域名下),这样就可以针对不同域名创建连接并请求。

以一种讨巧的方式突破限制,但是滥用此技术也会造成很多问题,比如每个 TCP 连接本身需要经过 DNS 查询、三步握手、慢启动等,还占用额外的 CPU 和内存,对于服务器来说过多连接也容易造成网络拥挤、交通阻塞等,对于移动端来说问题更明显

3. HTTP/1.1队头阻塞

尽管HTTP/1.1长链接可以通过一个TCP连接传输多个请求,但同一时刻只能处理一个请求,当前请求未结束前,其他请求只能处于阻塞状态。

HTTP2 解决办法

1. 新的二进制格式:二进制分帧为各种特性提供了基础

  • 什么是二进制分帧 HTTP 2在应用层(HTTP)和传输层(TCP)之间增加一个二进制分帧层。在二进制分帧层上,HTTP2.0会将所有传输信息分割为更小的消息和帧,并对它们采用二进制格式的编码将其封装。其中,HTTP1.X中的首部信息header封装到Headers帧中,而request body将被封装到Data帧中。

  • 二进制分帧如何工作HTTP2 通信都在一个TCP连接上完成,这个连接可以承载任意数量的双向数据流,相应的每个数据流以消息的形式发送。而消息由一或多个帧组成,这些帧可以乱序发送,然后根据每个帧首部的流标识符重新组装。

  • 二进制分帧对性能优化工作的贡献: 二进制分帧主要是为下文中的各种特性提供了基础。它能把一个数据划分封装为更小更便捷的数据。首先是在单链接多资源方式中,减少了服务端的链接压力,内存占用更少,链接吞吐量更大。这一点可以结合下文中的多路复用来体会。另一方面,由于TCP链接的减少而使网络拥塞状态得以改善,同时慢启动时间的减少。使拥塞和丢包恢复的速度更快。

  • 补充知识 a) 帧:HTTP2.0通信的最小单位,所有帧都共享一个8字节的首部,其中包含帧的长度、类型、标志、还有一个保留位,并且至少有标识出当前帧所属的流的标识符,帧承载着特定类型的数据,如HTTP首部、负荷、等等。 b) 消息:比帧大的通讯单位,是指逻辑上的HTTP消息,比如请求、响应等。由一个或多个帧组成。 c) 流:比消息大的通讯单位。是TCP连接中的一个虚拟通道,可以承载双向的消息。每个流都有一个唯一的整数标识符

2. 多路复用: 即多个请求都通过一个TCP连接并发地完成

HTTP2中一个域名只使用一个TCP长连接来传输数据,而且请求直接是并行的、非阻塞的。解决了 HTTP1.1 的多条TCP连接竞争带宽、队头阻塞、慢启动等问题

HTTP2引入一个二进制分帧层处理(二进制分帧是多路复用的基础),转化为一个个带有请求ID的帧,这些帧在传输完成后根据ID组合成对应的数据

  • HTTP1.1 的传输

image.png

  • HTTP2 的多路复用

image.png

3. 服务端推送: 服务端能够主动把资源推送给客户端

服务端根据客户端的请求,提前返回多个响应,推送额外的资源给客户端。比如客户端请求 stream 1(/page.html)。服务端在返回stream 1的消息的同时推送了 stream 2(/script.js) 和 stream 4(/style.css)

  • 服务器推送如何工作

a)  PUSH_PROMISE帧是服务端向客户端有意推送资源的信号。 PUSH_PROMISE帧中只包含预推送资源的首部。如果客户端对PUSH_PROMISE帧没有意见,服务端在PUSH_PROMISE帧后发送响应的DATA帧。如果客户端已经缓存了该资源,不需要推送,可以拒绝PUSH_PROMISE帧。

b)       PUSH-PROMISE必须遵循请求-响应原则,只能借着对请求的响应推送资源。 PUSH_PROMISE帧必须在返回响应之前发送,以免客户端出现竞态条件(竞态条件是指在多线程的情况下不同的执行顺序会导致计算机执行出不同的结果正确性不同)

  • 服务器推送对性能优化工作的贡献

服务端推送是一种在客户端请求之前发送数据的机制。在HTTP2.0中,服务器可以对一个客户端的请求发送多个响应。如果一个请求是由你的主页发送的,服务器可能会响应主页内容、logo以及样式表,因为他知道客户端会用到这些东西。这样不但减轻了数据传送冗余步骤,也加快了页面响应的速度,提高了用户体验。

4. header压缩: HTTP/2压缩消息头,减少了传输数据的大小

HTTP1.x每次通讯(请求或响应)都会携带首部信息用于描述资源属性。

HTTP2 在客户端和服务端之间使用首部表来跟踪和存储之前发送的键值对。请求与响应首部的定义在 HTTP2 中基本没有变,只是所有首部键必须全部小写,而且要求行要独立为:method:、:scheme:、:host:、:path:这些键值对. 对于相同的数据,不再重新通过每次请求和响应发送。每个新的首部键值对要么追加到当前表的末尾,要么替换表中之前的值。首部表在HTTP2.0的链接存续期内始终存在,由客户端和服务端共同渐进的更新。

image.png

5. 请求优先级

把HTTP消息分为很多独立帧之后,就可以通过优化这些帧的交错和传输顺序进一步优化性能。每个流都可以带有一个31bit的优先值:0表示最高优先级;2的31次方-1表示最低优先级

  • 请求优先级如何工作

客户端明确指定优先级,服务端可以根据这个优先级作为交互数据的依据,比如客户端优先设置为.css>.js>.jpg。服务端按此顺序返回结果更加有利于高效利用底层连接,提高用户体验。

然而,在使用请求优先级时应注意服务端是否支持请求优先级,是否会引起队首阻塞问题,比如高优先级的慢响应请求会阻塞其他资源的交互。


HTTP3

HTTP2存在的问题

其实主要就是 TCP/IP协议 存在的问题

1. 丢包问题

HTTP/2中,多个请求是跑在一个TCP管道中的,如果其中任意一路数据流中出现了丢包的情况,就要等待重传,就会阻塞该TCP连接中的所有请求。这不同于HTTP/1.1,使用HTTP/1.1时,浏览器为每个域名开启了6个TCP连接,如果其中的1个TCP连接发生了队头阻塞,那么其他的5个连接依然可以继续传输数据。

2. RTT(Round Trip Time)

在传输数据之前,我们需要花掉3到4个RTT来建立连接。


HTTP3的特点

1. HTTP2和HTTP3的传输层是完全不同的协议,HTTP3的传输层是UDP协议

2. 在会话层新增了QUIC协议

  • QUIC协议

基于UDP实现了类似于 TCP的多路数据流、传输可靠性等功能,我们把这套功能称为QUIC协议

QUIC协议提供类似于HTTP2的流控制功能,QUIC协议使用流ID取代IP和端口,这样就能实现连接迁移。例如说从4G信号切换到wifi,下层的IP和端口变了,但是由于QUIC的流ID没有变,这个连接不会变,可以继续使用这个连接。

HTTP3如何工作

  • HTTPS是类似于TCP握手的工作方式,先工作在HTTP1上,通过HTTP1传递交换得到秘钥,然后切换到HTTPS上工作。

  • HTTP2也是基于TLS的,所以HTTP2的工作方式和HTTPS也是同样的过程,需要握手建立TLS连接,只是TLS连接完成后,发送一个HTTP2的连接确认消息,确认后,客户端服务器使用HTTP2进行连接通讯。

  • HTTP3首先要建立好HTTP2连接,然后发送HTTP2扩展帧,这个帧包含IP和端口,浏览器收到扩展帧,使用该IP和端口,使用QUIC建立连接,如果成功,断开HTTP2,升级为HTTP3。

这三者,都用TCP的握手协议去理解,都是握手,不同的是握手方式不一样。

HTTP3的挑战

  • 从目前的情况来看,服务器和浏览器端都没有对HTTP/3提供比较完整的支持。Chrome虽然在数年前 就开始支持Google版本的QUIC,但是这个版本的QUIC和官方的QUIC存在着非常大的差异。

  • 部署HTTP/3也存在着非常大的问题。因为系统内核对UDP的优化远远没有达到TCP的优化程度,这 也是阻碍QUIC的一个重要原因。

  • 中间设备僵化的问题。这些设备对UDP的优化程度远远低于TCP,据统计使用QUIC协议时,大约有 3%〜7%的丢包率。