目录
1 HTTP 3.0
1.1 引言
从 HTTP/1.1
到 HTTP/2
,HTTP
协议一直都是使用 TCP
作为传输协议。
然而,就在最新的 HTTP/3
,HTTP
就直接把 TCP
抛弃了,向孤立无援的 UDP
伸出了援手,基于 UDP
协议的基础上,在应用层实现了一个可靠的传输协议 —— QUIC
。
很多同学可能就好奇了,HTTP
都用 TCP
都用了几十年了,而且 TCP
已经是那么完善的可靠传输协议了,又有超时重传、按序接收、流量控制、拥塞控制这些特性,怎么突然就把 TCP
抛弃了?到底是 TCP
哪里做的不够好?
所以,下面就说说TCP
的弊端
1.2 TCP存在队头阻塞问题
TCP
队头阻塞的问题要从两个角度看,一个是发送窗口的队头阻塞
,另外一个是接收窗口的队头阻塞
1.2.1 发送窗口的队头阻塞
TCP
发送出去的数据,都是需要按序确认的,只有在数据都被按顺序确认完后,发送窗口才会往前滑动。举个例子,比如下图的发送方把发送窗口内的数据全部都发出去了,可用窗口的大小就为 0 了,表明可用窗口耗尽,在没收到 ACK
确认之前是无法继续发送数据了。
接着,当发送方收到对第 32~36
字节的 ACK
确认应答后,则滑动窗口往左边移动 5 个字节,因为有 5 个字节的数据被应答确认,接下来第 52~56
字节又变成了可用窗口,那么后续也就可以发送 52~56
这 5 个字节的数据了。
但是如果某个数据报文丢失或者其对应的 ACK
报文在网络中丢失,会导致发送方无法移动发送窗口,这时就无法再发送新的数据,只能超时重传这个数据报文,直到收到这个重传报文的 ACK
,发送窗口才会移动,继续后面的发送行为。
举个例子,比如下图,客户端是发送方,服务器是接收方
客户端发送了第 5~9
字节的数据,但是第 5
字节的 ACK
确认报文在网络中丢失了,那么即使客户端收到第 6~9
字节的 ACK 确认报文,发送窗口也不会往前移动。
此时的第 5 字节相当于队头
,因为没有收到队头
的 ACK
确认报文,导致发送窗口无法往前移动,此时发送方就无法继续发送后面的数据,相当于按下了发送行为的暂停键,这就是发送窗口的队头阻塞问题
1.2.2 接收窗口的队头阻塞
接收方收到的数据范围必须在接收窗口范围内,如果收到超过接收窗口范围的数据,就会丢弃该数据,比如下图接收窗口的范围是 32 ~ 51
字节,如果收到第 52
字节以上数据都会被丢弃
接收窗口什么时候才能滑动?当接收窗口收到有序数据时,接收窗口才能往前滑动,然后那些已经接收并且被确认的有序
数据就可以被应用层读取。
但是,当接收窗口收到的数据不是有序的,比如收到第 33~40
字节的数据,由于第 32 字节数据没有收到, 接收窗口无法向前滑动,那么即使先收到第 33~40
字节的数据,这些数据也无法被应用层读取的。只有当发送方重传了第 32 字节数据并且被接收方收到后,接收窗口才会往前滑动,然后应用层才能从内核读取第 32~40
字节的数据
至此发送窗口和接收窗口的队头阻塞问题都说完了,这两个问题的原因都是因为 TCP
必须按序处理数据,也就是 TCP
层为了保证数据的有序性,只有在处理完有序的数据后,滑动窗口才能往前滑动,否则就停留。
停留发送窗口
会使得发送方无法继续发送数据。
停留接收窗口
会使得应用层无法读取新的数据。
其实也不能怪 TCP
协议,它本来设计目的就是为了保证数据的有序性
1.3 HTTP/2 的队头阻塞
HTTP/2
通过抽象出 Stream
的概念,实现了 HTTP
并发传输,一个 Stream
就代表 HTTP/1.1
里的请求和响应
在 HTTP/2
连接上,不同 Stream
的帧
是可以乱序发送的(因此可以并发不同的 Stream
),因为每个帧的头部会携带 Stream ID
信息,所以接收端可以通过 Stream ID
有序组装成 HTTP
消息,而同一 Stream
内部的帧必须是严格有序的。
但是 HTTP/2
多个 Stream
请求都是在一条 TCP
连接上传输,这意味着多个 Stream
共用同一个 TCP
滑动窗口,那么当发生数据丢失,滑动窗口是无法往前移动的,此时就会阻塞住所有的 HTTP
请求,这属于 TCP
层队头阻塞。
1.4 没有队头阻塞的 QUIC
QUIC
也借鉴 HTTP/2
里的 Stream
的概念,在一条 QUIC
连接上可以并发发送多个 HTTP
请求 (Stream
)。
但是 QUIC
给每一个 Stream
都分配了一个独立的滑动窗口,这样使得一个连接上的多个 Stream
之间没有依赖关系,都是相互独立的,各自控制的滑动窗口。
假如 Stream2
丢了一个 UDP
包,也只会影响 Stream2
的处理,不会影响其他 Stream
,与 HTTP/2
不同,HTTP/2
只要某个流中的数据包丢失了,其他流也会因此受影响。
1.5 TCP 建立连接的延迟
对于 HTTP/1
和 HTTP/2
协议,TCP
和 TLS
是分层的,分别属于内核实现的传输层、openssl
库实现的表示层,因此它们难以合并在一起,需要分批次来握手,先 TCP
握手(1RTT),再 TLS
握手(2RTT),所以需要 3RTT 的延迟才能传输数据,就算 Session
会话服务,也需要至少 2 个 RTT,这在一定程序上增加了数据传输的延迟。
TCP
三次握手和 TLS
握手延迟,如图:
HTTP/3
在传输数据前虽然需要 QUIC
协议握手,这个握手过程只需要 1 RTT,握手的目的是为确认双方的连接 ID
,连接迁移就是基于连接 ID
实现的。
但是 HTTP/3
的 QUIC
协议并不是与 TLS
分层,因为 QUIC
也是应用层实现的协议,所以可以将 QUIC
和 TLS
协议握手的过程合并在一起,QUIC
内部包含了 TLS
,它在自己的帧会携带 TLS
里的 记录
,再加上 QUIC
使用的是 TLS1.3,因此仅需 1 个 RTT 就可以 同时 完成建立连接与密钥协商,甚至在第二次连接的时候,应用数据包可以和 QUIC
握手信息(连接信息 + TLS 信息)一起发送,达到 0-RTT 的效果。
如下图右边部分,HTTP/3
当会话恢复时,有效负载数据与第一个数据包一起发送,可以做到 0-RTT(下图的右下角):
1.6 升级 TCP 的工作很困难
TCP
协议是诞生在 1973 年,至今 TCP
协议依然还在实现更多的新特性。
但是 TCP
协议是在内核中实现的,应用程序只能使用不能修改,如果要想升级 TCP
协议,那么只能升级内核。
而升级内核这个工作是很麻烦的事情,麻烦的事情不是说升级内核这个操作很麻烦,而是由于内核升级涉及到底层软件和运行库的更新,我们的服务程序就需要回归测试是否兼容新的内核版本,所以服务器的内核升级也比较保守和缓慢。
很多 TCP
协议的新特性,都是需要客户端和服务端同时支持才能生效的,比如 TCP Fast Open
这个特性,虽然在2013 年就被提出了,但是 Windows
很多系统版本依然不支持它,这是因为 PC
端的系统升级滞后很严重,Windows Xp
现在还有大量用户在使用,尽管它已经存在快 20 年。
所以,即使 TCP
有比较好的特性更新,也很难快速推广,用户往往要几年或者十年才能体验到。
相反,QUIC
是处于应用层的,所以如果升级 QUIC
协议的话,其实就是像升级软件一样轻松。而且,QUIC
可以针对不同的应用设置不同的拥塞控制算法,这样灵活性就很高了,这是 TCP
做不到的,因为 TCP
更改拥塞控制算法是对系统中所有应用都生效,无法根据不同应用设定不同的拥塞控制策略
1.7 网络迁移需要重新建立 TCP 连接
基于 TCP
传输协议的 HTTP
协议,由于是通过 四元组(源IP
、源端口
、目的 IP
、目的端口
)确定一条 TCP
连接
那么当移动设备的网络从 4G
切换到 WIFI
时,意味着 IP
地址变化了,那么就必须要断开连接,然后重新建立 TCP
连接。
而建立连接的过程包含 TCP
三次握手和 TLS
四次握手的时延,以及 TCP
慢启动的减速过程,给用户的感觉就是网络突然卡顿了一下,因此连接的迁移成本是很高的。
QUIC
协议没有用四元组
的方式来绑定
连接,而是通过 连接ID
来标记通信的两个端点,客户端和服务器可以各自选择一组 ID
来标记自己,因此即使移动设备的网络变化后,导致 IP
地址变化了,只要仍保有上下文信息(比如连接 ID
、TLS 密钥
等),就可以 无缝 地复用原连接,消除重连的成本,没有丝毫卡顿感,达到了连接迁移的功能。
1.8 总结
HTTP/3
抛弃 TCP
后,基于 UDP
实现的可靠传输 QUIC
协议,带来这四点好处:
- 降低连接耗时:在客户端有缓存的情况下实现0-RTT建立连接
- 更灵活的拥塞控制:在用户态可以为每个请求配置不同的拥塞控制策略
- 无队头阻塞的多路复用:每个请求流独立拥有滑动窗口,互不影响
- 连接迁移:网络切换不会中断数据传输
不过, HTTP/3
也面临了一些挑战,QUIC
基于 UDP
协议在用户空间实现的可靠传输协议,如果一些网络设备无法识别出 QUIC
协议,那么在这些网络设备的眼里它就是一个 UDP
协议。
而几乎所有的电信运营商都会 歧视 UDP
数据包,原因也很容易理解,毕竟历史上几次臭名昭著的 DDoS
(分布式拒绝服务攻击) 攻击都是基于 UDP
的。国内运营商即使没有封禁 UDP
,也是对 UDP
进行严格限流的。
自 2013 年 QUIC
被正式公开以来,到 2023 年已经发展了差不多 10 年,目前网上已经有了不少热门开源的项目,除去带头大哥 Google 在完成了对自身搜索引擎的支持,还同时拉上了 Gmail 、YouTube 等站点。但对于国内的绝大部分站点来说,大部分还是 HTTP/2 协议,HTTP/3 之路,似乎还停留在东土大唐