【网络】QUIC 协议和 HTTP3
面试常考,临时抱佛脚学一下。参考 https://zhuanlan.zhihu.com/p/266578819;
HTTP1.0 和 HTTP1.1
HTTP1.0 中,每一个请求必须等上一个请求收到响应了才能继续。且每一次请求都会重新建立 TCP 链接。
HTTP1.1 中可以复用 TCP 链接,但是依旧没有解决队头阻塞的问题,虽然复用了 TCP 链接,但是请求 B 依旧需要等待请求 A 的响应,才能继续发送。
换句话说,服务器发送响应必须严格按照客户端发送请求的顺序。客户端按 ABC 发送,服务端也需要按 ABC 返回响应。
HTTP2 的升级
总结就是:
- steam 并发传输;
- 哈夫曼编码 HPACK 压缩 header 为二进制后传输;
- 服务器可以主动推送资源给客户端;
并发传输就是在 HTTP 中添加一个 Steam ID,通过 ID 来标识不同的 HTTP 请求报文,这样就能在同一个 TCP 传输流中并发传输多个 HTTP 报文。而在 HTTP1.1 中,虽然可以复用同一个 TCP 链接,但是 HTTP1.1 中在某个请求没有收到响应之前,是无法传输其他请求的。
压缩 header:通过哈夫曼编码在双方建立一个动态表,其中包括 61 项常用 header 的静态表,同时可以对静态表进行动态扩展(需要使用但不在 61 项中的 header),最终发送 header 的时候,会用序号来替代 header 的字符串,同时对 header 的 value 进行压缩,从而减少 HTTP 包的长度。
服务器主动推送:请求一个资源后,服务器可以主动将其他客户端用的上的资源推送给客户端,而不需要等客户端主动请求。比如请求 index.html
后,将需要的 js/css 也推送给客户端。
QUIC 可靠性保证
其实这个问题就是 UDP 如何保证可靠性(加什么东西?),把 TCP 可靠性机制往里面加就行了。
- 重传机制
- 确认机制
- 握手机制(快速握手)
- 拥塞控制(应用层处理的)
- 多路复用(多个 stream 并发)
- 前向纠错(校验码)
QUIC 的特点
无队头阻塞
无队头阻塞:HTTP2 中用 tcp 实现多 stream 并发传输,但是 tcp 本身需要保证数据到达应用层的有序性。假设有这样的报文到达顺序
1 | stream1 tcp1 |
此时 tcp3 出现了丢包,虽然我们客户端已经收到了服务器发送的 stream2 (tcp4) 和 stream3 (tcp5),但因为 tcp 协议栈需要维护序号有序性,没有收到 tcp3 之前,它不会把后序的数据交付给上层,这就导致了 stream2/3 因为 stream1 的丢包而出现了阻塞,也就是队头阻塞
的含义。
而 QUIC 基于 UDP,因为 UDP 本身是无连接的,所以它不会出现队头阻塞的问题。
1 | stream1 udp3 # 丢包 |
即便 udp3 丢包了,也不会影响 udp4/5,此时 steam2/3 也能被正常交付给上层供 HTTP 使用。
反映到浏览器加载中,有可能这里的 udp4/5 就是 steam2/3 的最后一个分片,交付给上层后就已组成了一个完整的 HTTP 报文,表现在浏览器上就是某一个模块加载出来了,会让用户的体验更好。
更快的链接建立
在通过 TCP 实现 HTTPS 的时候,需要涉及到 TCP 三次握手和 TLS/SSL 握手。其中 TCP 三次握手和 SSL 握手都要进行 3 次数据传输,一共就是 6 次单向传输了(3RTT)。
而 UDP 实现的 QUIC 也需要握手,这个过程是 1RTT。但是 QUIC 本身就是一个应用层实现的协议,它可以直接在第一次链接的时候就携带 TLS/SSL 的相关信息,第二次链接的时候发送 QUIC 握手报文 + TLS 握手报文和数据载荷,同时完成 TLS 握手的功能,近似实现 0RTT 的握手。
这里涉及到了 TLS1.3,先暂且不去深入了解,知道大概就行。
链接迁移
在基于 TCP 的 HTTP 中,需要通过 IP 和 TCP 端口的四元组来定位客户端和服务端。
假设我们从 WIFI 切换成 4G/5G,此时客户端的 ip 和端口肯定会发生变化,而在旧版本中,这就需要使用 TCP 重新和服务器进行三次握手和 TLS 链接建立,才能重新开始数据传输。
而 QUIC 中,除了 IP/UDP 的四元组外,还有个独特的 QUIC 会话 ID 来标识两边的传输。
假设一个客户端从 WIFI 切换成 5G,发生了 IP 和端口的变化,但它发送的 QUIC 请求报文中,依旧包含了之前的 QUIC 会话 ID,此时服务器可以通过这个会话 ID 定位之前的传输,并继续传输之前中断的时候的数据。
再加上前文提到的 QUIC 握手次数少,这两个相结合,客户端切换 IP 和端口的时间消耗就更少了!
HTTP3 的 QPACK
在 HTTP2 中,会用 HPACK 来将更新后的动态 header 表发送给对方,但是它的 HPACK 并没有确认机制。也就是说,如果你发送的一个新的 HPACK 更新丢包了,对方没有收到,也就不认识某个新的 header 序号,此时客户端发送的信息就没有办法被正常解码了,此时会出现阻塞。
而 QPACK 就是在 HPACK 的基础上加入了确认机制。有两个链接来进行
- 一个叫 QPACK Encoder Stream,用于将一个字典(Key-Value)传递给对方,比如面对不属于静态表的 HTTP 请求头部,客户端可以通过这个 Stream 发送字典;
- 一个叫 QPACK Decoder Stream,用于响应对方,告诉它刚发的字典已经更新到自己的本地动态表了,后续就可以使用这个字典来编码了。
这两个特殊的单向流是用来同步双方的动态表,编码方收到解码方更新确认的通知后,才使用动态表编码 HTTP 头部。
当 A 给 B 发送的动态表没有被收到时,A 还不会对这个新的 header 进行压缩(以原始形式进行发送)。直到收到 B 对这个动态表的响应了,才会压缩传输。
这样就避免了 HPACK 中 A 会压缩发送一个 B 还不知道的动态表中的 header 的情况。
- 最新
- 最热
- 最早
- 作者
点击重新获取 | 打开控制台