内容概览
- http/1.1相对于http/1.0做的优化(持久化连接、管线化、域名分片)
- 现有http/1.1存在的问题(队头阻塞、线头阻塞)
- http/2特性
- http/2中的问题
- http/3
- http/3的挑战
扩展问题
- HTTP2中的帧和流
- http2 怎么确保文件同时传输不会报错?
一、http/1.1相对于http/1.0做的优化
HTTP/1.0每进行一次HTTP通信,都需要经历建立TCP连接、传输HTTP数据和断开TCP连接三个阶段(如下图)。
1、持久化连接
为了解决这个问题,HTTP/1.1中增加了持久连接的方法,它的特点是在一个TCP连接上可以传输多个HTTP 请求,只要浏览器或者服务器没有明确断开连接,那么该TCP连接会一直保持。
持久连接在HTTP/1.1中是默认开启的,所以你不需要专门为了持久连接去HTTP请求头设置信息,如果你不想要采用持久连接,可以在HTTP请求头中加上**Connection: close(默认为Connection:keep-alive)****。**目前浏览器中对于同一个域名,默认允许同时建立6个TCP持久连接。
2、管线化
持久化连接虽然能减少TCP的建立和断开次数,但是它需要等待前面的请求返回之后,才能进行下一次请求。如果TCP通道中的某个请求因为某些原因没有及时返回,那么就会阻塞后面的所有请求,这就是著名的队头阻塞的问题。
HTTP/1.1中试图通过管线化的技术来解决队头阻塞的问题。HTTP/1.1中的管线化是指将多个HTTP请求整批提交给服务器的技术,虽然可以整批发送请求,不过服务器依然需要根据请求顺序来回复浏览器的请求。FireFox、Chrome都做过管线化的试验,但是由于各种原因,它们最终都放弃了管线化技术。
但是管线化存在诸多问题
- 第一个响应慢还是会阻塞后续响应
- 服务器为了按序返回相应需要缓存多个响应占用更多资源
- 浏览器中途断连重试服务器可能得重新处理多个请求
- 必须客户端 - 代理 - 服务器都支持管线化
3、提供虚拟主机的支持
在HTTP/1.0中,每个域名绑定了一个唯一的IP地址,因此一个服务器只能支持一个域名。但是随着虚拟主机技术的发展,需要实现在一台物理主机上绑定多个虚拟主机,每个虚拟主机都有自己的单独的域名,这些单独的域名都公用同一个IP地址。
因此,HTTP/1.1的请求头中增加了Host字段,用来表示当前的域名地址,这样服务器就可以根据不同的Host值做不同的处理。
总结
我们知道HTTP/1.1为网络效率做了大量的优化,最核心的有如下三种方式:
1. 增加了持久连接;
2. 浏览器为每个域名最多同时维护6个TCP持久连接;
3. 使用CDN的实现域名分片机制。
二、现有http/1.1存在的问题
1、线头阻塞
TCP连接上只能发送一个请求,前面的请求未完成前,后续的请求都在排队等待。
2、多个TCP连接
虽然HTTP/1.1管线化可以支持请求并发,但是浏览器很难实现,chrome、firefox等都禁用了管线化。所以1.1版本请求并发依赖于多个TCP连接,建立TCP连接成本很高,还会存在慢启动的问题。
3、头部冗余,采用文本格式
HTTP/1.X版本是采用文本格式,首部未压缩,而且每一个请求都会带上cookie、user-agent等完全相同的首部。
4、只能客户端主动请求
5、队头阻塞
6、明文传输
7、建立TCP连接成本比较大
三、http/2特性
1、二进制分帧层
HTTP2性能提升的核心就在于二进制分帧层。HTTP2是二进制协议,他采用二进制格式传输数据而不是1.x的文本格式。
2、多路复用
上面提到HTTP/1.1的线头阻塞和多个TCP连接的问题,HTTP2的多路复用完美解决。HTTP2让所有的通信都在一个TCP连接上完成,真正实现了请求的并发
3、头部压缩 + 维护头部字典(HPACK 算法)
头部压缩也是HTTP2的一大亮点。在1.X版本中,首部用文本格式传输,通常会给每个传输增加500-800字节的开销。现在打开一个网页上百个请求已是常态,而每个请求带的一些首部字段都是相同的,例如cookie、user-agent等。HTTP2为此采用HPACK压缩格式来压缩首部。头部压缩需要在浏览器和服务器端之间:
- 维护一份相同的静态字典,包含常见的头部名称,以及常见的头部名称和值的组合
- 维护一份相同的动态字典,可以动态的添加内容
- 通过静态Huffman编码对传输的首部字段进行编码
所以我们在传输首部字段的时候,例如要传输method:GET,那我们只需要传输静态字典里面method:GET对应的索引值就可以了,一个字节搞定。像user-agent、cookie这种静态字典里面只有首部名称而没有值的首部,第一次传输需要user-agent在静态字典中的索引以及他的值,值会采用静态Huffman编码来减小体积。
第一次传输过user-agent 之后呢,浏览器和服务器端就会把它添加到自己的动态字典中。后续传输就可以传输索引了,一个字节搞定。
4、服务器端推送
服务器端推送使得服务器可以预测客户端需要的资源,主动推送到客户端。
例如:客户端请求index.html,服务器端能够额外推送script.js和style.css。 实现原理就是客户端发出页面请求时,服务器端能够分析这个页面所依赖的其他资源,主动推送到客户端的缓存,当客户端收到原始网页的请求时,它需要的资源已经位于缓存。
针对每一个希望发送的资源,服务器会发送一个PUSH_PROMISE帧,客户端可以通过发送RST_STREAM帧来拒绝推送(当资源已经位于缓存)。这一步的操作先于父响应(index.html),客户端了解到服务器端打算推送哪些资源,就不会再为这些资源创建重复请求。当客户端收到index.html的响应时,script.js和style.css已经位于缓存。
四、http/2中的问题
虽然 HTTP/2 解决了很多之前旧版本的问题,但是它还是存在一个巨大的问题,主要是底层支撑的 TCP 协议造成的。HTTP/2的缺点主要有以下几点:
- TCP 以及 TCP+TLS 建立连接的延时
- TCP 的队头阻塞并没有彻底解决
- 多路复用导致服务器压力上升
- 多路复用容易 Timeout
- 出现了丢包时,HTTP/2 的表现反倒不如 HTTP/1 了。因为TCP为了保证可靠传输,有个特别的“丢包重传”机制,丢失的包必须要等待重新传输确认,HTTP/2出现丢包时,整个 TCP 都要开始等待重传,那么就会阻塞该TCP连接中的所有请求(如下图)。而对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反到只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。
五、http/3
QUIC的新功能
- 实现了类似TCP的流量控制、传输可靠性的功能
- 实现了快速握手功能
- 集成了TLS加密功能
- 多路复用,彻底解决TCP中队头阻塞的问题
六、http/3的挑战
通过上面的分析,我们相信在技术层面,HTTP/3是个完美的协议。不过要将HTTP/3应用到实际环境中依然 面临着诸多严峻的挑战,这些挑战主要来自于以下三个方面
- 浏览器没有提供完整的支持
- 操作系统、中间设备:对udp的支持远没有tcp完善
- 丢包率较高3%-7%
扩展问题
- 帧和流
- http2 怎么确保文件同时传输不会报错?
1、HTTP2中的帧和流
在 HTTP2.0 中,有两个非常重要的概念:帧和流
帧(frame)
+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31) |
+=+=============================================================+
| Frame Payload (0...) ...
+---------------------------------------------------------------+
HTTP2.0 中数据传输的最小单位,因此帧不仅要细分表达 HTTP1.x 中的各个部分,也优化了 HTTP1.x 表达不好的地方,同时还增加了 HTTP1.x 表达不了的方式。每一帧都包含几个字段:length、type、flags、stream identifier、frame playload等等,其中 type 代表帧的类型,在 HTTP2 的标准中定义了 10 中类型。
HEADERS frame
DATA frame
PRIORITY (设置流的优先级)
RST_STREAM (终止流)
SETTINGs (设置此连接的参数)
PUSH_PROMISE (服务器推送)
PING (测量RTT)
GOAWAY (终止连接)
WINDOW_UPDATE (流量控制)
CONTINUATION (继续传输头部数据)
在 HTTP2.0 中,它把数据报的两大部分分成了 header frame 和 data frame,也就是头部帧和数据体帧。
流(stream)
流:存在于连接中的一个虚拟通道。流可以承载双向消息,每个流都有一个唯一的整数 ID。HTTP2 长连接中的数据是不按请求响应顺序发送的,一个完整的请求或响应可能会分成非连续多次发送,有以下几个特点:
- 双向性:同一个流内,可以同时发送和接收数据
- 有序性:流中被传输的数据就是二进制帧。帧在流上的被发送与被接收都是按照顺序进行的
- 并行性:流中的二进制帧都是被并行传输的,无需按照顺序等待
- 流的创建:流可以被客户端或服务器单方面建立、使用或共享
- 流的关闭:流也可以被任意一方关闭
- HEADERS 帧在 DATA 帧前面
- 流的 ID 都是奇数,说明是由客户端发起的,这是标准规定的,那么服务端发起的就是偶数啦
2、http2 怎么确保文件同时传输不会报错?
stream Identifier
多路复用就是在一个 tcp 连接中可以存在多个 stream,也就是多个请求,每个stream又包含了多个frame,通过stream Identifier就可以识别出每个frame分别对应的stream,当frame到达服务端之后,就可以根据stream Identifier 来重新组合得到完整的请求了,这就在提高传输性能的同时保证了传输的正确性。