HTTP1.0,1.1,2.0,3.0

641 阅读8分钟

HTTP1.0

队头阻塞

HTTP1.0的每个连接都基于一个TCP连接进行建立,假设一个页面中需要同时加载十张图片,每张图片都需要建立一个HTTP和它对应的一个TCP连接,因此一共建立了十个HTTP连接和十个TCP连接。

但是我们知道TCP存在队头阻塞的问题,这是因为TCP在收到数据包时需要进行整合排序后再给上层使用(TCP传输的有序性),利用一个队列来维护这些数据包。

就像你踢足球把脚崴了去医院检查,但是除了你之外还有打篮球崴脚的、打羽毛球崴脚的,他们都比你先来,所以你得等他们都检查完了才能检查,生活中很多这样的场景,其实简单点来说就是排队。假设打篮球的那个崴得很严重,医生给他检查了半天,那么打羽毛球崴脚的那个和你只能一直等着,这就是队头阻塞。

所以一旦在传输过程中有数据包丢失(丢包)或者长时间未传完的现象发生,TCP会等待该数据包重传或传完,这时候就会阻塞后面的数据包的传输。

简而言之就是所有处在后面传输的数据包都需要等待上一个数据包传输完成后才可以进行传输。

优化队头阻塞

浏览器针对队头阻塞的问题对同一域名下的TCP连接进行了限制,每个浏览器的限制不同,平均在5-6个连接。

但是用户往往是不买账的,我们再以上面的十张图片的加载为例,当你在加载前五张图片的时候发生了丢包或者其中的某张图片特别大需要加载很久,后面的五张图片将会一直处于等待状态。

在用户的角度看就是五处空白,非常影响体验,当用户的等待时间越久,他们的耐心就会越少,等耐心消磨殆尽后只能选择退出。

所以在HTTP2.0甚至3.0还未出现的时候,许多拥有大量图片的应用如淘宝、京东等都采取了域名分发的优化手段。

如上例中的十张图将被分为五张和五张两组分发到不同域名的静态资源服务器下,这样每组图片在同一时间段都会进行异步加载从而优化了队头阻塞的问题。

但是采取这样的优化方式的缺点是需要占用服务器资源,当需要加载的资源越多时占用的服务器资源也越多。

除此之外的优化手段还有雪碧图、合并js文件等方案,这里不过多阐述了,不属于本文范畴。

无状态短连接

HTTP1.0时期的每个连接都是无状态的短连接,即每次请求完成后就会断开连接,每个连接断开后都无法获取上次连接的状态和信息。

这样每个连接都是无法复用的,每次进行HTTP请求都要建立一个新的TCP连接,请求结束后就断开该TCP连接,这就意味着每次请求都需要进行三次握手和慢启动,请求结束后都需要经历四次挥手。

HTTP1.1

管道化传输

HTTP1.1的管道化传输允许一个TCP连接对应多个HTTP请求,但这也只限于单个文件的情况下。

假设文件A中有多个静态资源,这些静态资源可以基于一个TCP连接进行多个HTTP请求,但是如果一个页面要同时请求文件B、文件C甚至更多的文件时(如js文件、css文件等),就需要建立多个TCP连接了。
而且假设文件A中有三个静态文件D,E和F,虽然管道化技术使E不需要等待D请求完成并接收到响应后再开始请求,F也同理,但是最后服务端还是需要按照发送请求时的顺序来依次给出响应,这时候依旧会发生阻塞。

所以HTTP1.1的管道化传输优化还是没有从根本上解决队头阻塞的问题。

长连接

HTTP1.1默认支持keep-alive的长连接模式,解决了1.0中无法复用TCP连接的问题,当本次请求结束后不会断开连接并保持本次连接的状态与信息,客户端与服务端都有权取消该模式。

断点续传

基于长连接的模式就可以实现断点续传的功能了,这样对大文件的传输和下载会更加友好,同时HTTP1.1在请求头中引入了range头域,它允许只请求资源的某部分,此时的返回码是206

支持Host头

在HTTP1.0中认为每台服务器都只绑定一个唯一的IP地址,因此并不支持Host头。

但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机,并且它们共享一个IP地址。

所以HTTP1.1的请求消息和响应消息都必须传Host头,如果没有传Host头域会返回400状态码。

HTTP2.0

多路复用

HTTP2.0实施了多路复用机制真正解决了HTTP1.x版本的队头阻塞问题,同一域名下的所有HTTP请求都基于同一个TCP连接,即使是多个文件也是一样。

每个HTTP请求中都会有一个帧来标识,因此可以乱序请求,使每个请求都不互相依赖,服务端也可以乱序给出响应,最后客户端根据帧标识也能够进行重新排序整合。

但是这只是解决了HTTP层的队头阻塞问题并没有解决TCP的队头阻塞问题

如果出现某个请求丢包的情况时会使整个TCP连接开始等待重传,因为HTTP应用层数据流的多路复用在TCP的传输层层面上并不感知,TCP只知道是某个数据包丢失了从而阻塞了后面的数据包,这时候的性能反而不如HTTP1.X了。

因为HTTP1.X中建立了多个TCP连接,其中一个连接所对应的请求丢包了并不会影响到其他连接的请求传输。

二进制分帧层

HTTP2.0在TCP传输层与HTTP应用层中间插入了二进制分帧层,支持了二进制数据的传输,而上述多路复用中的帧也是在这一层进行传输。

头部压缩

HTTP1.x的头带有大量信息,而且每次都要重复发送。HTTP2.0中使用头部压缩来减少需要传输的header大小,并且通讯双方各自缓存一份头部字段表,既避免了重复header的传输,又减小了需要传输的大小。

服务端推送

HTTP2.0的服务端推送将支持服务端向客户端的单次请求返回多个响应,而不必每次都要客户端重复发起资源请求。

假设客户端想要请求文件A和图片B,客户端会先与服务端建立连接,向服务器发送请求文件A后再发送请求图片B。

而服务器推送可以在客户端请求文件A的时候就将图片B也返回给客户端,待客户端同意后进行接收。

最重要的是服务器推送可以缓存,在遵循同源策略的情况下,让不同页面之间可以共享缓存资源。

HTTP3.0

Quic协议

由于TCP的队头阻塞问题困扰着HTTP1.X和HTTP2.0,而去改造TCP的成本又非常高,所以HTTP3.0推出了Quic协议,它底层基于UDP,又在UDP的基础上实现了许多TCP的功能并进行了优化,如拥塞控制、可靠传输和流量控制(滑动窗口)等。

热插拔

在TCP中如果要修改拥塞控制策略,需要在系统层面进行操作,因为它集成在内核层。而Quic修改拥塞控制策略只需要在应用层操作,同时QUIC会根据不同的网络环境、用户来动态选择拥塞控制算法。

新多路复用

HTTP3.0同样采取HTTP2.0中流和帧的多路复用机制,但是Quic基于UDP并不存在队头阻塞,这使得每个流之间不互相依赖,如果发生丢包并不会影响其他数据包的传输。

同时也不需要等待重传,这得益于以下的前向纠错机制。

前向纠错

Quic协议在数据包中增加了一个冗余的前向纠错数据包,该包中会对所有数据包进行一个异或的运算。

假设本连接中的某个数据包丢失了,Quic协议会根据其他所有数据包与前向纠错包来推算并恢复该数据包的内容,从而无需等待重传

连接迁移

TCP通过IP来识别连接,这将会对移动端不友好,因为移动端存在网络环境切换的情况,如果用户从wifi环境切到了移动流量环境,就需要重新建立TCP连接,因为IP发生了变化。

而Quic通过ID来识别连接,该ID是一个64位的随机数,即使IP发生变化也不会影响到本次连接的ID,从而实现网络环境的切换仍然可以维持原连接的功能。