要想讲清楚HTTP3是什么,就不得不先说下HTTP2,而要说清楚什么是HTTP2,就不得不介绍HTTP1。正所谓:计算机软件没有银弹,每项新技术都是解决了旧技术的问题,而又引入了新的问题。所以我们从HTTP1开始讲的话,才能知道为什么是这样,做到知其然,也知其所以然。
HTTP/1.0
1996年出现的HTTP1.0协议规范,其具有以下特点:
- 底层传输协议是TCP
- 采用请求-响应的应答模型
- 无状态的协议
- 每次请求都需要建立新的TCP链接
- 以文本的方式传输数据
提供了基础的HTTP功能,也为后来的HTTP奠定了基础 - 支持GET,POST,HEAD等基本方法
- 引入了HTTP Header的概念
- 支持多种文件类型(MIME)
- 支持缓存机制
- 支持基本认证
HTTP/1.1
HTTP1.0 有很多显而易见的问题,但最大的问题在于,每次的请求响应都需要使用单独的TCP链接,随着互联网的发展,页面开始变得越来越复杂,一个页面加载完成可能需要上百个请求响应过程。而TCP建立连接其实是一个很高成本的过程,明显的耗时点有:
- TCP建立连接的三次握手
- 如果是HTTPS的话,还需要进行TLS层的四次握手(TLS1.3)
- CUBIC等传统拥塞控制算法的慢启动过程
所以HTTP1.1引入了keep-alive机制来复用TCP连接,复用TCP使应用等的多个请求可以复用TCP的控制参数,提高带宽的利用率,这可能是HTTP1.1最重要的升级内容。为了行文完整,这里再总结下HTTP/1.1的特点: - 引入keep-alive参数,默认开启持久连接
- 管道机制,多个请求可以在服务器未响应前发出,但要求服务端支持,响应的顺序与发送请求的顺序相同
此外还新增了一些功能: - 新增PUT,DELETE,OPTION等方法
- 支持虚拟主机 (Host头部)
- 增强的缓存机制 (E-tag,If-None-Match等)
HTTP/2
HTTP1.1虽然通过TCP连接复用提高了网络带宽利用率,但是依然有其缺点:
- 队头阻塞问题,多个请求复用的TCP流上无法有效的区分请求响应的对应关系,所以依赖严格的顺序对应,这导致前一个请求未完成,后一个请求需要排队等待
- 协议开销大,无状态的协议传输,需要每次都携带完整的头部信息
- 文本协议压缩率低,传输低效
这是HTTP2.0的出场契机,2015年5月HTTP/2正式发布为RFC 7540标准,其具备以下特点: - 多路复用:与HTTP/1.1的复用TCP方式不同,HTTP/2在TCP流上又抽象出了一层流的概念,多个流复用一个TCP连接,通过流ID来标识不同的流,从而解决了HTTP/1.1请求排队的问题
- 二进制分帧:HTTP/2不再用文本进行网络传输,而是定义了不同作用的帧,帧序列化为二进制数据在流上传输
- 头部压缩:通过静态表,动态表和Huffman编码进行传输头部压缩,减小网络传输内容
此外还新增了一些功能: - 服务器主动推送:这里的推送并不是类似Websocket的双向传输,而是服务端在收到客户端请求的时候,可以创建流来主动发送客户端可能会后续请求的数据,这需要在连接的过程中进行协商
- 流的优先级:可以为不同的资源设定优先级,但HTTP/2的优先级设置过程因其复杂度而被颇为诟病
HTTP/3
HTTP/2已经很强大,但依然有其问题,当然这可能已经不仅仅是HTTP的问题:
- TCP的队头阻塞,此队头阻塞非HTTP/1.1的队头阻塞,因为TCP提供可靠的流传输,所以TCP包丢失后,内核协议栈会等待重传的包,直到数据排序完整,才能供应用层读取
- TCP协议栈靠五元组{原IP,原端口,目标IP,目标端口,协议类型}来定位一个socket,也即一个连接,这导致网络切换需要重新建立网络连接,而移动端的场景,频繁切换网络是常态
- HTTP/2以及HTTP/1都是建立在TCP之上,如果要请用加密传输,则需要在TCP,HTTP之间加一层TLS,这使得每次建连都需要引入额外的四次握手(TLS1.3)
上述问题正是HTTP/3要解决的主要问题。2022年6月正式发布(RFC 9114)的HTTP/3具有以下特点: - 底层传输不再使用TCP,而是定义了全新的QUIC(RFC 9000)协议,传输层网络协议使用UDP,可靠性完由QUIC在用户层实现
- 握手过程集成TLS,不再将TLS视为独立的,可插拔的中间层,而是深入契合TLS,将QUIC作为TLS的传输层,握手过程即协商QUIC层的配置参数,也协商TLS层的密钥
- 不再使用五元组来定位一个连接,而是使用一个用户层协商的连接ID,这使得客户端即使切换了网络,变更了IP,依然可以复用之前的连接
- 使用TLS的会话恢复(Session Resumption)和PSK(Pre-Shared Key)机制实现快速建连,部分情况下可以实现0-RTT建连
从这几个方面也可以看出来HTTP/3的野心很大,其对四层协议的改动甚至深于七层协议的改动,其解决的主要问题大多是针对TCP的,据相关统计,现在整个网络中TCP流量占比超过80%,可以说是触动了计算机网络中核心的核心。
什么是HTTP/3
通过简单的回顾HTTP协议的发展历史,我们知道了为什么会出现HTTP/3。接下来再完整的总结一下,什么是HTTP/3?
HTTP/3可以分为两层来讲:
- HTTP 层,主要是HTTP语义的实现,其大部门语义几乎与HTTP/2类似
- QUIC 层,TCP的对等协议,其内部实现了可靠传输,并提供了HTTP/2 流的语义
我们分别展开来看下每个层的功能和特点。
HTTP层
HTTP层的实现,几乎等同与HTTP/2,其实现了
- 继承了HTTP/2帧和流的概念,帧类型包括SETTING,DATA等
- 流的语义实现在QUIC层,并不像HTTP/2实现在HTTP层
- 头部压缩,通过QPACK(RFC 9204)压缩头部,支持动态表,静态表以及Huffman编码
- 强制使用HTTPS,所有的流量都是加密的
- 版本协商,从实现上就可以发现其与HTTP/2,HTTP/1是不兼容的,所以需要缜密的版本协商机制,以方便回退到旧版本的HTTP上
- 与HTTP/2相同的服务器主动推送机制,可以将部分资源主动推送给客户端,减少请求网络耗时
QUIC层
其实QUIC层才是HTTP/3的根本改动,其要完全替换TCP来实现完整的可靠传输控制,也必然需要在用户层实现很多类似TCP的机制
- 连接建立的过程,集成TLS1.3,通过四次握手甚至零次握手建立安全通信
- 通过用户层的连接ID定位一个连接,不再依赖底层的socket地址信息,可以实现连接迁移
- 通过TLS1.3提供的ALPN(application layer protocol negotiation)进行上层协议协商,从这里也可以看出QUIC层可以供给其他的应用层协议,不一定是HTTP
- 提供类似HTTP/2的流的语义实现,多路复用,互不阻塞,一个流的数据传输不会影响其他流
- 流量控制,有流级别的流量控制,也有连接级别的流量控制
- 通过SACK,拥塞通知(ENC)更精准的控制丢包和重传
- 定义QUIC包作为网络传输单元,其序号单调递增,避免了TCP传输字节序号的二义性问题
- 可插拔的拥塞控制实现,更灵活的拥塞控制策略,支持BBR等现代的拥塞控制算法
- 用户层实现的协议,更灵活的错误处理,升级相对内核协议栈会方便很多
以上的总总实现,其实每一条展开讲都是极其复杂的,甚至需要一整篇RFC来描述和定义,但也正因为其变革的根本性,解决了传统 TCP 的诸多限制,提供了更好的性能、安全性和用户体验,尤其适用于现在移动网络以及弱网的传输场景。
小生不才,历时三年有余,终于将自己的一个HTTP/3库写的初有眉目:
quicX: GitHub - caozhiyi/quicX: A http3 library with c++11.
其实现了大部分RFC文档中定义的功能,我也还在一直完善。在这里分享出来,与诸君共勉!
参考文献:
- 《HTTP权威指南》
- 《HTTP2 in Action》
- RFC 9114: RFC 9114 - HTTP/3
- RFC 9204: RFC 9204 - QPACK: Field Compression for HTTP/3
- RFC 9000: Information on RFC 9000 >> RFC Editor
- RFC 9001: https://datatracker.ietf.org/doc/html/rfc9001