有些地方后续补充完整
HTTP/1 的问题
1. 队头阻塞
HTTP/1 有个特性叫管道化(pipelining),允许一次发送一组请求,但是只能按照发送顺序依次接受。
因此,如果某个请求应答出现状况,那么剩下的工作都会被阻塞在那次请求应答之后,即队头阻塞。它会阻碍网络传输和 Web 页面渲染。
目前措施:现代浏览器会针对单个域名开启 6 个连接,通过各个连接分别发送请求。(某种程度上的并行)
2. 低效的 TCP 利用
HTTP/1 并不支持多路复用,所以浏览器一般会针对指定域名开启 6 个并发连接,这意味着拥塞窗口波动也会并行发生 6 次。TCP 协议保证那些连接都能正常工作,但是不能保证它们的性能上最优的。
3. 臃肿的消息首部
虽然 HTTP/1 提供了压缩被请求内容的机制,但是消息首部却无法压缩。
4. 受限的优先级设置
5. 第三方资源
HTTP/2
大致分为两部分:
- 分帧层,即 h2 多路复用能力的核心部分;
- 数据或 http 层
特性
-
二进制协议
h2 的分帧层是基于帧的二进制协议。
-
首部压缩
-
多路复用
-
加密传输
帧
HTTP/2 是基于帧的协议,采用分帧是为了将重要信息都封装起来,让协议的解析方可以轻松阅读、解析并还原信息。
HTTP/1 不是基于帧的,而是以文本分隔的。问题:
- 速度慢且容易出错
- 一次只能处理一个请求或响应,完成之前不能停止解析
- 无法预判解析需要多少内存
有了帧,处理协议的程序能预先知道会收到什么。(因为帧会有固定的字节去表达信息)
前 9 个字节对于每个帧是一致的。(通过读取这些字节,可以准确知道在整个帧中期望的字节数)
帧的英文是 frame,在我的理解有一种结构体的意思,相对于基于文本的要有结构有规律一些
名称(帧字段) | 长度 | 描述 |
---|---|---|
Length | 3 字节 | 表示帧负载的长度 |
Type | 1 字节 | 当前帧类型 |
Flags | 1 字节 | 具体帧类型的标识 |
R | 1 bit | 保留位,不要设置 |
Stream Identifier | 31 bit | 每个流的唯一 ID |
Frame Payload | 长度可变 | 真实的帧内容,长度在 Length 字段设置 |
名称(帧类型) | ID | 描述 |
---|---|---|
DATA | 0x0 | 传输流的核心内容 |
HEADERS | 0x1 | 包含 HTTP 首部 |
PRIORITY | 0x2 | 指示或者更改流的优先级和依赖 |
RST_STREAM | 0x3 | 允许一端停止流 |
SETTINGS | 0x4 | 协商连接级参数 |
PUSH_PROMISE | 0x5· | 提示客户端,服务器要推送些东西 |
PING | 0x6 | 测试链接可用性和往返时延(RTT) |
GOAWAY | 0x7 | 告诉另一端,当前端已结束 |
WINDOW_UPDATE | 0x8 | 协商一端将要接受多少字节(用于流量控制) |
CONTINUATION | 0x9 | 用以扩展 HEADER 数据块 |
优势
h1 由于是依靠分隔符的,因此只能处理完一个请求或响应,才能处理下一个;h2 是分帧的,所以请求和响应可以交错甚至多路复用。
(个人理解就是,h1 中多个请求或者响应连接在一起,由于只是靠分隔符解析,因此无法判断是多个内容连接起来的;而 h2 中多个帧连接在一起,由于帧更像个结构体,并且有长度信息,解析程序能够知道解析到哪里算一个帧的结束。)
流
定义:HTTP/2 连接上独立的、双向的帧序列交换。
如果客户端想要发出请求,它会开启一个新的流,然后服务器将在这个流上回复。
**因为有分帧,所以多个请求和响应可以交错,而不会互相阻塞。**流 ID 用来标识帧所属的流。
服务端推送
推送使服务器能够主动将对象发给客户端。
首部压缩
HPACK 是种表查询压缩方案,利用霍夫曼编码获得接近 GZIP 的压缩率。
参考
- 《HTTP/2 基础教程》