什么是 HTTP?
HTTP(超文本传输协议)是在全球网络上最通用数据通信的方式。有三个主要的版本,我们将在这里逐个介绍。
简单来说,该协议定义了浏览器如何与服务器进行通信以传递数据。在所有版本的协议中,浏览器会主动请求某些内容(“Request”),服务器会被动响应数据回来(“Response”)。
HTTP/1 于 1996 年推出。在那之前,有 HTTP/0.9,这是一个简单的协议,只支持 GET 方法,没有头部信息。HTTP 响应中只包括 HTML 文档。没有 HTTP 头部,也没有 HTTP 状态码。
HTTP/1.0 添加了头部信息、状态码以及额外的方法,如 POST 和 HEAD。然而,HTTP/1 仍然存在一些限制。例如,每个请求-响应对都需要一个新的 TCP 连接。
1997 年,为了解决 HTTP/1 的限制,发布了 HTTP/1.1,这个版本是 HTTP1 的权威版本,支持了Keepalive与Pipeline。这个版本推动了互联网的快速增长,尽管已经超过 25 年的历史,仍然被广泛使用。
如下是一个简单的HTTP请求
GET / HTTP/1.1
Host: www.baidu.com
服务端接收到请求后,响应html文档
HTTP/1.1 200 OK
<!DOCTYPE html>
<a href=http://home.baidu.com>关于百度</a>
</html>
在响应的html文档中,还包含各种资源,包括图片,js,css文档等等
HTTP/1.x的特性
持久连接
正如前面提到的,HTTP/1.1之前都是一种单请求-响应的协议。客户端打开与服务器的连接,发出请求,然后获取响应,连接随后关闭。如果有第二个请求,该循环将重复。对于后续的请求,同样的循环会重复。
随着网络资源向媒体发展,每次在每次响应后关闭连接显得很浪费。如果一个网页包含多个需要获取的资源,您将不得不多次打开和关闭连接。
由于 HTTP/1 是建立在 TCP协议之上的,每次新连接都意味着要经历TCP的三次握手过程。
HTTP/1.1 支持了Keepalive的特性,来规避了这个问题。它规定了在HTTP请求之后不需要关闭TCP连接,除非明确告知需要关闭。这意味着:
- 每个请求后不关闭连接。
- 没有多次的 TCP 握手。
Pipeline
HTTP/1.1 还引入了流水线处理(pipeline)的概念。
其想法是允许客户端在单个 TCP 连接上发送多个请求,而无需等待相应的响应。例如,当浏览器发现它需要两个图像来呈现一个网页时,它可以一个接一个地请求它们。
Pipeline通过减少每个响应之前的延迟,进一步提高了性能。但是Pipeline仍然存在头部阻塞的问题。
Range的分片传输
HTTP/1.1 引入了分片传输特性(chunked transfer encoding),允许服务器将响应分成较小的片发送,而不是等待整个响应生成完毕。
这使得初始页面渲染更快,特别是对于大型或动态生成的内容,比如视频播放场景,就大量使用了range请求,提高用户体验。
缓存
HTTP/1.1 引入了复杂的HTTP缓存机制
它添加了诸如 Cache-Control 和 ETag 等头部信息,允许客户端和服务器更好地管理缓存内容,减少不必要的数据传输。并支持如 If-Modified-Since 和 If-None-Match 等Header,允许客户端发送缓存有效性的探测请求,只有在内容发生修改之后,才会请求和发送完整的资源内容,节约带宽并响应速度。
HTTP/1.1的问题
串行请求
http/1.x及之前的版本,都是一个请求-一个响应,得上一个请求完成之后才能进行下一次请求。
头部阻塞
http/1.x中一个请求一个响应,每一次的情况都需要关闭连接。后来使用了Keepalive支持在一个TCP连接中发送多个HTTP请求,不过也只会保持一段时间。
在http/1.1中为了解决串行请求的问题,支持了Pipeline。发送端发送多个http请求的时候,不需要等待服务端响应,就可以顺序地的继续发送后续内容。但是pipeline仍然需要保证顺序。接收端必须按顺序接收,如果有之前发送的请求接收端没有完成接收,如发生了丢包,那后续的请求就无法接收,这就是http/1.1中的头部阻塞。
缺乏优先级设置
HTTP/1.x 没有提供请求优先级的方式,可能导致次要资源阻塞重要资源。
HTTP头部占用大
HTTP请求及响应的发送,除了数据之外,需要发送大量纯文本头部信息,尤其在使用 cookie 时。
HTTP/2-解决了性能问题
随后于2015年发布了 HTTP/2,HTTP/2 中引入了多个特性来解决HTTP/1.1中碰到的性能问题。
多路复用
看看我们在 HTTP/1.1 中上面的例子,在这个例子中,您的浏览器请求一个包含多个资源的文档。在 HTTP/1.1 中,这些资源请求被排队,并逐个请求。
在 HTTP/2 中,情况有些不同。使用多路复用,浏览器可以在一个连接上一起请求这些资源,然后以相同的方式接收它们。
头部压缩
HTTP/2 使用 HPACK 算法来压缩请求和响应头部,通过静态字典,动态字典及Huffman编码来对HTTP头部进行压缩,显著减少了头部大小,减少了传输的数据量。
二进制分帧
帧:帧是 HTTP/2 数据通信消息的最小单位,例如请求和响应等,一个消息由一个或多个帧组成。
流:存在于连接中的一个虚拟通道。流可以承载双向消息,每个流都有一个唯一流ID。
HTTP/2 采用二进制格式传输数据,而非 HTTP 1.x 的文本格式,二进制协议解析起来更高效。HTTP / 1 的请求和响应报文,都是由起始行,首部和实体正文(可选)组成,各部分之间以文本换行符分隔。HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码。
HTTP/2 中,同域名下所有通信都在单个连接上完成,该连接可以承载任意数量的双向数据流。每个数据流都以消息的形式发送,而消息又由一个或多个帧组成。多个帧之间可以乱序发送,接收端根据帧首部的流标识可以重新组装。
流优先级
在HTTP/2中,每个数据流都有一个唯一的ID,并且可以被分配一个优先级。这个优先级告诉网络服务器和客户端,在处理多个数据流时,应该先处理哪个。优先级高的数据流会优先获得网络资源,从而更快地完成传输。
服务器推送
HTTP/2支持服务端在被请求之前主动将资源推送到客户端的缓存中,减少延迟,提高整体用户体验。
HTTP/2的问题
HTTP/2虽说已经没有了HTTP/1.1中的对头阻塞的问题,但是因为HTTP/2依然是构建在TCP协议之上的。TCP本身仍然存在对头阻塞的问题,并且TCP本身在弱网条件、网络频繁切换的情况下,TCP的本身的性能仍然对HTTP/2有着一定的影响。
HTTP/3
HTTP/3 保留了从 HTTP/2 中的语法和语义,但是HTTP/3不再依赖TCP协议,而是构建在 QUIC之上,QUIC 在 UDP 上运行。所以也就自然没有了TCP的对头阻塞问题,及弱网传输问题。
QUIC 是构建在UDP协议之上的,主要增强功能如下:
传输层的多字节流
QUIC在传输层引入了多字节流的概念,可以针对每个流进行单独数据包丢失处理。这意味着QUIC流被视为独立实体,一个流的数据包丢失不会影响其他流。QUIC还为每个流实现了单独的流量控制,有效解决了传输层的队头阻塞问题。
连接迁移
在TCP协议之上,连接由4元组定义:客户端IP地址、客户端端口、服务器IP地址和服务器端口。如果这些参数中的任何一个发生变化,连接就会变为无效,需要连接重建。QUIC引入了一个称为连接ID的概念。为每个QUIC连接分配一个唯一的连接ID,可以识别两个对等方之间的连接。连接ID在QUIC传输层内部定义,在网络发生变化的情况下,也可以恢复连接。
灵活的拥塞控制
TCP采用严格的拥塞控制机制,很难调整,QUIC的拥塞控制算法被设计在用户态实现,可以更好的变更,有着更好的适应性。
内置加密
QUIC 默认集成了传输层安全性TLS1.3,确保了安全连接,无需单独进行 TLS 握手。这降低了延迟,提高了连接建立时间。
减少头部阻塞
与 TCP 不同,QUIC 是以流级别处理数据包丢失。这意味着单个数据包的丢失不会阻塞整个连接,进一步减少了首部阻塞问题。
0-RTT 连接建立
基于TLS1.3,HTTP/3可以实现 0-RTT的连接建立,这可以显著降低连接延迟。
HTTP/3的问题
HTTP/3构建在UDP之上,在当前网络基础设施上,UDP协议的支持尚不是很完善。
HTTP/1 vs HTTP/2 vs HTTP/3该如何抉择
HTTP/1.1的支持最为广泛,HTTP/2和HTTP/3提供了更好的性能,提供了如多路复用、头部压缩和安全性改进。
HTTP/2已被广泛采用,浏览器及Web服务器也都支持。HTTP/2和HTTP/3都设计为与HTTP/1.1向后兼容,HTTP 1.1迁移到HTTP/2相对来说比较轻松,因为两者都基于TCP协议。
从HTTP/2迁移到HTTP/3需要采用UDP协议,支持TLS 1.3,当前客户端与服务端也并不是完全支持。当前网络基础设施中对UDP协议的支持仍不是很完善。