HTTP/1.1、HTTP/2和HTTP/3的区别

1.HTTP/1.1

每当说起HTTP/1.1,我都会想起以前叫外卖的方式,那个年代没有智能手机,绝大部分都是堂食。很多店都没有专门的外卖员,打电话点了外卖以后,老板就会叫人把外卖送过来。但是这种方式有一个很大的问题,店员老是忘记放筷子,于是店员在送了外卖给我以后,要再送别的东西,就必须再跑一趟。如果中途想要加鸡腿,也得等到店员回到店里才能送过来。

1.1 一次一份

这种“一次一份”的方式就对应着HTTP/1.1的核心。这里说明一下,HTTP/1.1才是互联网的第一个真正意义的HTTP标准版本。为什么说“一次一份”是HTTP/1.1的核心呢?因为你发送一个HTTP请求的时候,是需要等收到HTTP响应,才可以发送下一个HTTP请求。

HTTP请求一次一份

我们浏览一个网页的时候虽然只需要点一次鼠标,似乎只需要发送一次请求。其实一个网页一般由多个文件组成,最基本的就是HTML,CSS,JS和图片文件,对于HTTP协议来说,我们打开一个网页,需要进行TCP三次握手,建立起了TCP连接,才正式进行请求。服务器会先发送HTML文件给我们,但其他文件不会发给我们。我们的浏览器在收到HTML文件以后,根据HTML里面的内容,再向服务器一次请求CSS,JS等文件。

TCP三次握手

整个过程都是浏览器在帮我们完成,所以用户的直接感受是只有一次请求。如果在请求队伍里,有一个文件没有收到,后面的文件也无法接收了。这就会造成HTTP队头阻塞了,因为对于HTTP来说,一次一个请求响应。

1.2 持久连接

虽然对于HTTP/1.1来说,默认是持久连接的(Keep-Live)的,也就是保持这个TCP连接,不需要对每个请求再来一次TCP握手。请求和响应都可以放在同一个连接里面,但是只有一个连接肯定太慢了,连接太多又怕会造成DDos攻击。因此各家浏览器允许的持久连接数都不太相同,通常我们都会看到说,Chrome默认的是同时6个连接。

1.3 HTTP队头阻塞

即使有多个连接还是有问题的,假设浏览器的其它连接的响应文件都收到了,就只有一个连接的文件还没有收到,刚好是个会导致浏览器没法渲染的CSS文件,这就会造成HTTP队头阻塞了。为了解决这个问题,其实HTTP/1.1里有个叫「管线化」的技术,意思就是单个连接可以发送多个请求。

但是这里有个坑,虽然可以一次发送多个,但是响应的时候,必须按照请求发送的顺序接收。我先发了什么,就得先收到什么,这就会造成很大的执行难度。网络情况很难预知,非常有可能第一个要收到的响应丢包了,然后第二个响应变成第一个接收到的。所以我们很少会看到有浏览器在实际场景中,使用「管线化」这个技术。

HTTP队头阻塞

1.4 雪碧图

在网络协议层面上解决不了这个问题,在开发方面自然就多了很多“黑科技”出来了,你第一个会想到的是什么呢?很多网站会把小图标全部做成一张单独的图片,请求的时候就只需请求这一个文件,而不是分开请求多个文件。然后再用JS或是CSS把小图标分布到网站的各个部分。这个叫做精灵图或者雪碧图。确实减少了请求次数,但是对于开发者来说就很麻烦了。

1.5 Data URLs

除此之外,Data URLs 是另一种替代方案,比方说下面的这种图片,可以用base64进行编码,然后就可以以字符串的形式写进HTML或者CSS里面,不需要单独一份图片文件,但是一般这个编码结果比长颈鹿的脖子还要长,很不方便代码的维护和管理。

1.6 域名分片

前面说到浏览器会限制同一连接的请求数量,其实是限制每个域名的链接数,因此网站就弄出多个域。使得浏览器可以同时下载这些资源,举个极端的例子,比方说网站有6张图片,那么就可以设置6个图片域名,这样浏览器就可以同时进行下载了。就不用等前面的资源下载后再轮到下个资源了。这就是「域名分片」,不过不用想这样做很可怕,开发的复杂度被硬生生地提高了。

1.7 其他方式减少请求

为了减少请求,还有很多方法,比如把JS和CSS合并,或者把JS和CSS都整合到HTML里面,各种操作“百花齐放”。虽然大家都在绞尽脑汁减少请求,但是后来越来越多的网站都需要“上锁”了,也就是HTTPS。不然像谷歌这样的浏览器就会给你的网站提示不安全。对于HTTP/1.1来说无疑是雪上加霜。

1.8 HTTPS

因为原本进行通信前就要进行TCP三次握手,HTTPS里的TLS/1.2又要进行握手,握手后才可以进行加密通信。虽然后来的TLS/1.3减少了来回次数,但对于HTTP/1.1来说这一来一回的负担还是太重了,越多的来回就意味着越多的未知。如果网络上有中间件炸了,容易造成丢包,结果还是可能会阻塞网页的渲染。

1.9 TCP慢启动

可是TCP除了三次握手的固定开销以外,还会有个「慢启动」。因为要进行拥塞控制,也就是说为了不造成网络拥堵,而且不知道网络的实际情况,一开始只会发送较小量的TCP数据段,到了后面再慢慢增加,因此会导致新访问网页刷新速度较慢

1.10 HTTP/1.1 首部不压缩和明文发送

除了TCP的各种开销以外,我们千万别忘了HTTP本身就会产生固定开销,请求和响应都是有各种首部的,而且大部分首部都是重复的,发完一次下一次还发。特别是cookie,每次都得发送,字符还特别长。

再加上HTTP/1.1本来就是明文的,首部也没有进行压缩,使得大部分首部又累赘又臃肿,这就像你每次去办事的时候,对方都一定要求你携带一大堆的证明材料。于是后来就有了HTTP/2。

2.HTTP/2

HTTP/2有点像外卖2.0,很多商家开始发现外卖是个商机,于是自己就聘请专门送外的店员。假设我现在一次性地点了早餐、午餐、晚餐和宵夜,老板一点都不需要担心,因为有足够多的外卖店员,不用因为只一个店员送外卖而烦恼。这里就对应着HTTP/2的一大特点——「多路复用」。

2.1 多路复用

主要就是** **的问题,可能有很多人会和你说,多路复用好厉害,再也不用多个连接了。单个TCP连接就可以进行交错发送请求和响应,而且请求和响应之间不影响。

这样说确实没错,但是却忽略了里头重要的细节,HTTP/2并不是单个文件,就这样直接响应过去,请求和响应报文都被划分为各个不同的帧,这个帧可以分为首部帧和数据帧。其实就是把原本HTTP报文的首部和实体,给拆分为两部分,所以原本的HTTP报文就不再是原来的报文了,而有点像数据链路层的帧了。

2.2 流标识符

一开始我们并不需要去细细研究这里的结构,最重要的是,这里有个流标识符,正因为有这个流标识符,使得帧可以不用按照顺序抵达对方那里,因为你即使没有按照顺序,最终有这个流标识符,就可以按照顺序进行组合,而且帧类型里还可以设置优先级,这样来标注流的权重。

流标识符

听起来HTTP/2的多路复用,很爽快地解决了队头阻塞的问题,其实并没有,HTTP/2 的诸多问题依旧存在 。但HTTP/2还是有值得表扬的地方,之前的HTTP/1.1报文主体压缩,首部不压缩。

2.3 首部压缩

HTTP/2,这回就把首部也给压缩了,不过这次是引入了叫HPACK的压缩算法。HPACK算法,要求浏览器和服务器都保存一张静态可读的表,比方说经典的”HTTP/1.1 200 OK”起始行,在HTTP/2里就变为”:status :200”,从肉眼看只少了三个字节,大家可能觉得没什么大不了,但因为是重复的首部,可以实现在二次请求和响应里直接去掉。

首部压缩

另外像cookie这样的首部,可以作为动态信息加入动态表里,这样节省下来的资源更是可观的。再加上这个HTTP/2的帧并不是ASCII编码的报文,而是是被提前转换为二进制的帧,解析起来更有效率。

HTTP/2二进制的帧

2.4 服务器推送

当年推出HTTP/2的时候,还有个逆天的技术让很多人吹捧,就是服务器推送。其实比较好理解的,当浏览器进行请求的时候,服务器可以不像以往一样,等浏览器解HTML时再一个一个响应。而是把浏览器后续可能需要的文件,一次性全部发送过去。

看起来很美好,实际操作却很“骨感”,因为客户端有可能点错了网页,轻轻点了你一下,你就给我重重的一拳,让我多了这么多缓存,而且服务器推送也可能会造成DDOS非对称攻击,因为这里存在一个明显的杠杆,因此服务器推送也隐含着安全性的问题。

2.5 安全性

但实际上HTTP/2比HTTP/1.1要安全很多,首先是前面说到报文变为二进制的帧。对于人来说,可读性就差了很多。其次虽然没有说HTTP/2规定要用TLS加密,但如果用了HTTP/2还不用TLS加密就说不过去了。

HTTP/2用TLS加密

因为到了HTTP/2这个年代还不用TLS,大牌浏览器都会提示不安全,而且HTTP/2的多路复用也已经减少了等待的时间,久而久之就变成HTTP/2必须用TLS了。

看着HTTP/2就很好,但为什么2015年公布的HTTP/2,在2019年就冒出来一个HTTP/3呢?要知道HTTP/1.1公布的时候是1997年,从v2~v 3的进程太快了吧,这里肯定有问题。

2.6 缺陷

其实HTTP/2只解决了HTTP层面的队头阻塞,要知道HTTP下面还有个传输层呢,而且HTTP是基于TCP的,HTTP/2的帧下来以后就要由TCP处理了。TCP才不知道你帧里面的内容是哪个和哪个一起的,TCP还是按照自己的数据段来发送,如果有丢失的,还得重传。

夸张点说,即使丢失的TCP数据段刚好是一行代码注释,也没办法,也得继续等待。这就是TCP层面的队头阻塞,怎么办呢?比较好的办法就是把TCP也变成HTTP/2的帧那样。

关键是TCP协议是由操作系统内核实现的,除非让整个世界大部分的操作系统,都进行一次革新的升级,那不知道要等到猴年马月了,于是就有了HTTP/3协议了。

3.HTTP/3

你们应该猜到我要说什么了,那就是外卖3.0,就有点像我们现在点的外卖,在App上点外卖非常方便,不会因为打电话点餐导致的不顺畅。直接点了付款,App上直接整合了多个步骤,而且现在道路四通八达,外卖小哥骑着小电动到哪里基本都不会阻塞。

3.1 整合

这里就对应着HTTP3的一大核心「整合」,虽然我们很难把TCP进行快速的升级,并且广泛应用。但是HTTP3把TCP和TLS的握手过程整合在一起了,直接减少了来回带来的开销

3.2 QUIC-基于UDP协议的新协议

如果是恢复的会话,还可以不用握手实现0-RTT。但问题是TCP和TLS是两个协议,并不是说合并就合并了。实际上为了能够让HTTP3进行部署,只能选择传输层的UDP协议了,并且基于UDP协议上新增一个协议,也就是QUIC。

3.3 QUIC 加密传输

这个QUIC整合了TCP和TLS,使得HTTP3默认就是要使用加密传输的。也可以不严谨的说是TCP2.0,但不能说是UDP2.0。很多人觉得使用TCP就慢,使用UDP就是快,所以QUIC快就是因为基于UDP的缘故,这种说法其实很坑爹的。

3.4 QUIC 广泛部署

QUIC其实是因为能够广泛部署,才只能用UDP的,但实际上的机制还是大部分采用TCP的。但QUIC必须要解决TCP队头阻塞的问题,从应用层那边过来的数据会被封装成QUIC帧

3.5 QUIC 帧

这个QUIC帧和HTTP/2的帧很像,也是加了流标识符。但和HTTP/2不同的是,HTTP3的应用层上并没有所谓帧的概念,把数据帧移到了QUIC里面。相当于在传输层就有了数据帧,从源头解解决队头阻塞的问题,实现多路复用。

QUIC帧又再次被封装为QUIC数据包,QUIC数据包会加上一些信息,这里最重要的是加了Connection Id 连接Id。如果网络发生全面改变,比如从WIFI突然转到4G网络,虽然IP地址发生改变,但是因为客户端和服务端都协商好了连接Id,因此可以用连接Id来识别为同一个链接,避免再次握手。

Connection Id

这是QUIC其中一个速度快的原因,而且QUIC数据包会把里面的QUIC帧给加密了。也就是在TLS握手后,QUIC帧的内容被加密了。接着QUIC数据包会被UDP封装成数据段,UDP就会加上端口号,当我们选择HTTP3通信的时候,QUIC就像TCP那样开启连接,QUIC数据包就是在这连接通道里收发的。