看了一篇 cloudflare 讲 http/3 的文章,http/3 on quic,底层都不再依赖 tcp 协议了而是用 quic 协议。

以下是三代 http 协议的对比,使用中遇到的问题以及解决方案。

http/1 每个 tcp 连接只能承载一个 http 请求,而建立 tcp 的代价很高,三次握手、拥塞控制、加密协议握手,这就导致 http 通信成本很高。

http/1.1 通过 connection:keep-alive 多个请求复用同一个 tcp 连接,可以将握手及慢启动耗费的时间平摊到多个请求中,但问题是,一个 tcp 连接中的多个 http 请求是串行的,所以对每个连接来说,客户端和服务端依旧只能处理单个请求,而现代的网站通常会并行的向同一个服务器请求多个文件,css、js 等,于是需要建立多个 tcp 连接,问题回到 http/1。

http1.1vshttp2

http/2 解决方案是可以在一个 tcp 连接中可以并行传输多个 http 请求,但是依然存在的问题是,tcp 的责任是以正确的顺序传输数据,出现丢包时,tcp 协议会进行重发,由于 tcp 对承载的 http 请求是没有解析的,这时丢包数据后面的字节就无法被正常发送到客户端,即使他们本身并没有丢失数据而且和丢包所包含的数据是独立的 http 请求。最终结果是,这些请求被无意义地延迟,因为 tcp 协议无法得知丢包会不会对客户端的数据重建产生影响,这被称作 head-of-line blocking。

http/3 on quic 就是为了解决这个问题而诞生,它依赖的传输层协议不再 tcp 协议而是 quic,quic 将 stream 当作一等公民对待,quic streams 共享一个连接,因此减少了额外的握手和慢启动所使用的时间。http/3 可以简单的将 http 请求映射到 quic 的 stream 上。quic 的 stream 是独立传输的,所以在大多数丢包的场景中,只会影响一个 stream 而不会产生连锁效应,这可能是 quic 协议是基于 udp 报文的缘故。而专门再设计一个 http3 协议而不复用之前的 http2,是因为 http2 的 header 中的压缩 scheme HPACK 设计上,会严重依赖 http 请求的顺序,这是 quic 协议传输中所不能满足的,quic 只能保证单个 stream 中的字节是有序的,而不同 stream 的到达顺序则不保证。这就导致需要重新设计 http 协议中的 header 压缩 scheme,新的叫做 QPACK。而且一些 http/2 协议中的功能比如 per-stream 流控制已经由 quic 协议所实现,http/3 就将这些功能去掉了,减少协议中无意义的复杂度。使用 udp 协议还有一个好处是, quic 协议完全可以在用户空间实现,不再像 tcp 那样与操作系统绑定,这就给了 quic 协议实现极大的自由度。


原文链接:https://blog.cloudflare.com/http3-the-past-present-and-future/

cloudflare 对 quic 的一个 rust 实现:https://github.com/cloudflare/quiche