HTTP/1.1 和HTTP/1.0 的区别?
HTTP/1.1 相比 HTTP/1.0 性能上的改进:
- 使用长连接的方式改善了1.0中的短连接所带来的性能开销
- 通过管道网络传输,发送端可以不必等接收端处理请求响应后再发第二个请求。发了第一个请求后可以直接发第二个请求
但是HTTP1.1还是有性能瓶颈:
- 请求/响应的头部未经压缩就发送,首部信息越大延迟越大
- 虽然解决了发送段的连续发送问题,但HTTP/1.1中接收端还是得按接收顺序处理请求,如果前面的请求处理时间过长,就会阻塞后面的请求响应(这种情况称为队头阻塞)
HTTP/2 做了什么优化?
HTTP/2是基于HTTPS的,所以HTTP/2是有安全保障的
HTTP/2 相比 HTTP/1.1 性能上的改进:
- 头部压缩
如果同时发送的请求中,头部信息是相同或相似的话,协议会消除重复的部分。
在客户端和服务端都会维护一张头信息表,里面记录着头部的每一个字段以及其对应的索引,用索引代替字段,依次提高性能。
- 二进制格式
HTTP/2不再向1.1中采用纯文本形式的报文,而是全面采用了二进制格式进行传输,头信息和数据体都是二进制,并且统称为帧(frame):头信息帧(Headers Frame)和数据帧(Data Frame)。
和1.1不同,接收端接收到数据后无需将明文报文转化为二进报文,直接解析接收到的二进制报文。
- 并发传输
前面提到,HTTP/1.0中,发送端虽然能连续发送请求,但接收端还是存在队头阻塞问题。
HTTP/2.0的采用Stream的方式,多个Stream复用一条TCP连接
一个TCP连接包含多个Stream,一个Stream中包含多个Message(可以是请求message也可以是响应message),一个Message包含多个Frame。
Frame是HTTP/2.0中的最小单位,一个Frame中以二进制压缩格式存放一个HTTP/1的头部和包体。
每个Frame中有各自的Stream标识,接收端可以通过 Stream ID ,将Frame有序组装成Stream,不同 Stream 的帧是可以乱序发送的,因此可以并发不同的 Stream
注意:虽然二进制分帧协议中有
message
结构,但是,这只是一种逻辑层面的结构,用于区分是请求还是响应信息片段,并不参与真正的协议实现。底层实现仅仅有stream
和frame
- 服务器推送
服务端不再是被动地响应,可以主动向客户端发送消息
客户端和服务器双方都可以建立 Stream, Stream ID 也是有区别的,客户端建立的 Stream 必须是奇数号,而服务器建立的 Stream 必须是偶数号。
HTTP/2 存在的缺陷?
HTTP/2 通过 Stream 的并发能力,解决了 HTTP/1 队头阻塞的问题,看似很完美了,但是 HTTP/2 还是存在“队头阻塞”的问题,只不过问题不是在 HTTP(应用层) 这一层面,而是在 TCP(传输层) 这一层。
HTTP/2 是基于 TCP 协议来传输数据的,TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区里的数据返回给 HTTP 应用,那么当「前 1 个字节数据」没有到达时,后收到的字节数据只能存放在内核缓冲区里,只有等到这 1 个字节数据到达时,HTTP/2 应用层才能从内核中拿到数据,这就是 HTTP/2 队头阻塞问题。
所以,一旦发生了丢包现象,就会触发 TCP 的重传机制,这样在一个 TCP 连接中的所有的 HTTP 请求都必须等待这个丢了的包被重传回来。
HTTP/3.0做了哪些优化?
HTTP/2.0因为TCP的丢包会导致队头阻塞,所以在HTTP/3.0中将TCP改成了UDP。
家都知道 UDP 是不可靠传输的,但基于 UDP 的 QUIC 协议 可以实现类似 TCP 的可靠性传输。
- 无队头阻塞
QUIC 协议也有类似 HTTP/2 Stream 与多路复用的概念,也是可以在同一条连接上并发传输多个 Stream,Stream 可以认为就是一条 HTTP 请求。
QUIC 有自己的一套机制可以保证传输的可靠性的。当某个流发生丢包时,只会阻塞这个流,其他流不会受到影响,因此不存在队头阻塞问题。这与 HTTP/2 不同,HTTP/2 只要某个流中的数据包丢失了,其他流也会因此受影响。
所以,QUIC 连接上的多个 Stream 之间并没有依赖,都是独立的,某个流发生丢包了,只会影响该流,其他流不受影响。
- 更快的连接建立
对于 HTTP/1 和 HTTP/2 协议,TCP 和 TLS 是分层的,分别属于内核实现的传输层、openssl 库实现的表示层,因此它们难以合并在一起,需要分批次来握手,先 TCP 握手,再 TLS 握手。
HTTP/3 在传输数据前虽然需要 QUIC 协议握手,但是这个握手过程只需要 1 RTT,握手的目的是为确认双方的「连接 ID」,连接迁移就是基于连接 ID 实现的。
但是 HTTP/3 的 QUIC 协议并不是与 TLS 分层,而是 QUIC 内部包含了 TLS,它在自己的帧会携带 TLS 里的“记录”,再加上 QUIC 使用的是 TLS/1.3,因此仅需 1 个 RTT 就可以「同时」完成建立连接与密钥协商,如下图:
QUIC 是新协议,对于很多网络设备,根本不知道什么是 QUIC,只会当做 UDP,这样会出现新的问题,因为有的网络设备是会丢掉 UDP 包的,而 QUIC 是基于 UDP 实现的,那么如果网络设备无法识别这个是 QUIC 包,那么就会当作 UDP包,然后被丢弃。