一 复习与目标
1 复习
- 简单密码学、对称加密与非对称加密
- 数字签名、数字证书
- SSL/TLS
- HTTPS = HTTP + SSL/TLS,SSL/TLS为HTTP提供了保密性、完整性和鉴别性
2 目标
- HTTP1.1的问题
- HTTP2.0设计关键
- HTTP2.0的详情
注1:本文并不会讲解h2的所有内容,只会包含重要(主观)的内容。
注2:下一篇再讲解具体每个报文类型并进行报文分析。
二 HTTP1.1的问题
(1)HTTP管线化的问题
- 简单说就是在等待上一个请求响应的同时,发送下一个请求。
- 多个HTTP请求放到一个TCP连接中一一发送,而在发送过程中不需要等待服务器对前一个请求的响应;但是客户端要按照发送请求的顺序来接收响应。
- 队首阻塞(Head-of-line blocking):如果前一个请求很慢,就会导致后续的请求都受到影响。
- 队首阻塞的存在,导致浏览器一般禁用该功能,服务器也应该禁用掉。
(2)多个TCP连接(6个)
- 由于现在一个网站的请求变多,文件变大,所以HTTP1.1为了提升下载速度,将支持最大6个TCP(Chrome)连接进行HTTP请求。
- 管理一个TCP连接是非常消耗客户端和浏览器的资源,所以需要优化。
(3)分片(多域名)
- 网站的资源进行划分多域名,这样可以摆脱客户端对一个域名最大只能打开6个TCP连接的限制。
- 缺点是多个DNS解析的消耗和更多TCP连接的管理
(4)首部太大
- HTTP1.1每次请求都会带上完整的首部
三 HTTP2设计关键
- 降低协议对延迟的敏感
- 修复pipelining和head of line blocking的问题
- 并行操作无需与服务器建立多个连接,从而改进 TCP 的利用率,特别是拥塞控制方面;
- 保持 HTTP 1.1 的语义,利用现有文档,包括(但不限于)HTTP 方法、状态码、URI,以及首部字段
- 明确指出所有新的可扩展机制以及适当的扩展策略。
四 HTTP2协议
(1)二进制分帧层
- 分帧层位于套接字接口与应用可见的高层HTTP API 之间的一个新机制:HTTP 的语义,包括各种动词、方法、首部,都不受影响,不同的是传输期间对它们的编码方式变了。
- HTTP 1.x 以换行符作为纯文本的分隔符,而HTTP2.0 将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式的编码。
(2)流、消息和帧
-
流:已建立的连接上的双向字节流。
-
消息:与逻辑消息对应的完整的一系列数据帧
-
帧:通信的最小单位,每个帧包含帧首部,流标识符。
-
总结:
- 所有通信都在一个 TCP 连接上完成。
- 流是连接中的一个虚拟信道,可以承载双向的消息;每个流都有一个唯一的整数标识符(1、2…N)。
- 消息是指逻辑上的 HTTP 消息,比如请求、响应等,由一或多个帧组成。
- 帧是最小的通信单位,承载着特定类型的数据,如 HTTP 首部、负荷、流标识符,等等。
(3)多向请求和响应
- 概述:客户端和服务器可以把HTTP 消息分解为互不依赖的帧,然后乱序发送,最后再在另一端把它们重新组合起来。
- 总结:
- 可以并行交错地发送请求,请求之间互不影响;
- 可以并行交错地发送响应,响应之间互不干扰;
- 只使用一个TCP连接即可并行发送多个请求和响应;
- 消除不必要的延迟,从而减少页面加载的时间;
- 不必再为绕过 HTTP 1.x 限制而多做很多工作;
(4)请求优先级
- 每个流都可以带有一个31 比特的优先值,0~2^31-1,0为最高优先级
- 服务器可以根据流的优先级,控制资源分配(CPU、内存、带宽),而在响应数据准备好之后,优先将最高优先级的帧发送给客户端。
注:不同的服务器可能忽略优先级值或者优先级值的处理策略不同。
(5)流量控制
- 由于多个数据流公用同一个TCP连接,那么多个数据流的资源分配尤其重要,于是出现了流的流量控制。
- 流量控制基于每一个虚拟流进行,而非端到端的控制(更细粒度的流量控制);
- 流量控制基于窗口更新帧进行,即接收方广播自己准备接收某个数据流的多少字节,以及对整个连接要接收多少字节;
- 流量控制窗口大小通过 WINDOW_UPDATE 帧更新,这个字段指定了流 ID 和窗口大小递增值;
- 流量控制有方向性,即接收方可能根据自己的情况为每个流乃至整个连接设置任意窗口大小;
- 流量控制可以由接收方禁用,包括针对个别的流和针对整个连接。
- 客户端与服务器交换SETTINGS 帧,目的是设置双向的流量控制窗口大小。
(6)服务器推送
-
服务器可以对一个客户端请求发送多个响应。
- 建立HTTP 2.0 连接后,客户端与服务器交换SETTINGS 帧,借此可以限定双向并发的流的最大数量。
- 如果并发流最大数量设置为0,代表关闭服务器推送功能。
-
服务器推送类似于资源直接插入到文档中,就是把资源直接推送给客户端,而无需客户端请求。
-
HTTP 2.0 中,唯一的不同就是可以把这个过程从应用中拿出来,放到HTTP 协议本身来实现。
-
好处:
- 客户端可以缓存推送过来的资源;
- 客户端可以拒绝推送过来的资源;
- 推送资源可以由不同的页面共享;
- 服务器可以按照优先级推送资源
-
HTPP1.1嵌入资源都应该改为服务器推送,唯一有必要直接在网页中插入资源的情况,就是该资源只供那一个网页使用,而且编码代价不大。
-
HTTP 2.0服务器推送策略:
- 在自身的代码中明确发起服务器推送
- 通过额外的 HTTP 首部向服务器发送信号,列出希望推送的资源
- 可以不依赖应用而自动学习相关资源。服务器可以解析文档,推断出要推送的资源,或者可以分析流量,然后作出适当的决定。
注:使用上请参考:HTTP/2 服务器推送(Server Push)教程
(7)首部压缩
- HTTP1.1在每一次通信都会携带一组首部,用于描述传输的资源及其属性,长度甚至高达上千字节。
- 为减少这些开销并提升性能,HTTP 2.0 会压缩首部元数据:
- HTTP 2.0 在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键值对,对于相同的数据,不再通过每次请求和响应发送;
- 首部表在HTTP 2.0的连接存续期内始终存在,由客户端和服务器共同渐进地更新;
- 每个新的首部键值对要么被追加到当前表的末尾,要么替换表中之前的值。
(8)HTTP 2.0升级与发现
- 通过TLS 和 ALPN 发起新的 HTTPS 连接;
- 根据之前的信息发起新的 HTTP 连接:HTTP Upgrade 机制通过协调确定适当的协议。
- 没有之前的信息而发起新的 HTTP 连接:如果客户端自己保存有或通过其他手段(如DNS 记录、手工配置等)获得了服务器支持HTTP 2.0,可以直接发送HTTP 2.0 分帧,而不必依赖Upgrade 机制。
五 二进制分帧
1 固定首部
- 长度:16位,代表帧净荷最大可达65535字节(64KB)
- 类型:8位,帧的类型
- DATA:用于传输 HTTP 消息体。
- HEADERS:用于传输关于流的额外的首部字段。
- PRIORITY:用于指定或重新指定引用资源的优先级。
- RST_STREAM:用于通知流的非正常终止。
- SETTINGS:用于通知两端通信方式的配置数据。
- PUSH_PROMISE:用于发出创建流和服务器引用资源的要约。
- PING:用于计算往返时间,执行“活性”检查。
- GOAWAY:用于通知对端停止在当前连接中创建流。
- WINDOW_UPDATE:用于针对个别流或个别连接实现流量控制。
- CONTINUATION:用于继续一系列首部块片段。
- 标志:8位,允许不同的帧类型定义特定于帧的消息标志
- R:保留字段,1位
- 流标志符:31位,唯一标识 HTTP 2.0 的流
2 流的建立
(1)流的建立:
- 客户端通过发送 HEADERS 帧来发起新流,这个帧里包含带有新流 ID 的公用首部、可选的31 位优先值,以及一组HTTP键值对首部;
- 服务器通过发送 PUSH_PROMISE 帧来发起推送流,这个帧与HEADERS帧等效,但它包含“要约流ID”,没有优先值。
(2)特点
- 流计数器偏置:客户端发起的流具有奇数ID,服务器发起的流具有偶数ID。
- HEADERS帧或者PUSH_PROMISE帧用于沟通新流的元数据,净荷会在DATA 帧中单独发送
(3)完整报头区块
- 一个包含报头终止标记集合的HEADERS 或PUSH_PROMISE帧
- 一个不包含报头终止标记的HEADERS 或PUSH_PROMISE帧以及一个或多个CONTINUATION帧,最后一个CONTINUATION帧由报头终止标记
3 流与多路复用
3.1 特点:
- 一个单独的HTTP/2连接能够保持多个同时打开的流,各个端点间从多个流中交换帧;
- 流可以被客户端或者服务端单方面建立、分享或关闭;
- 帧的顺序很重要,特别是报头及数据帧的顺序语义上是有意义的;
- 流标识符由启动流的终端分配。
3.2 流的状态
+--------+
send PP | | recv PP
,--------| idle |--------.
/ | |
v +--------+ v
+----------+ | +----------+
| | | send H / | |
,------| reserved | | recv H | reserved |------.
| | (local) | | | (remote) | |
| +----------+ v +----------+ |
| | +--------+ | |
| | recv ES | | send ES | |
| send H | ,-------| open |-------. | recv H |
| | / | | | |
| v v +--------+ v v |
| +----------+ | +----------+ |
| | half | | | half | |
| | closed | | send R / | closed | |
| | (remote) | | recv R | (local) | |
| +----------+ | +----------+ |
| | | | |
| | send ES / | recv ES / | |
| | send R / v send R / | |
| | recv R +--------+ recv R | |
| send R / `----------->| |<-----------' send R / |
| recv R | closed | recv R |
`----------------------->| |<----------------------'
+--------+
send: endpoint sends this frame
recv: endpoint receives this frame
H: HEADERS frame (with implied CONTINUATIONs)
PP: PUSH_PROMISE frame (with implied CONTINUATIONs)
ES: END_STREAM flag
R: RST_STREAM frame
3.2 流的标志符
无符号的31位整数
- 客户端发起的流具有奇数ID,服务器发起的流具有偶数ID。
- 0x0用来标识连接控制信息流且绝对不能用来建立一个新流。
- HTTP/1.1升级到HTTP/2的请求将收到一个0x1标识的流的响应
- 流标识符不能被重复使用,耗尽后发送一个GOAWAY帧强制客户端对新流使用新连接(TCP连接)
3.3 流的并发
- SETTINGS_MAX_CONCURRENT_STREAMS:限制流的并发量(SETTINGS帧)
- 客户端能指定服务端能启动的流最大并发量,服务端能指定客户端能启动的流最大并发量
- 计入并发量:“打开”、任意一种“半封闭”状态的流
4 流的优先级
4.1 概述
新建流的终端可以在报头帧中包含优先级信息来对流标记优先级;对于已存在的流,优先级帧可来改变流优先级。
4.2 流依赖
- 每个流可以显式依赖其他流;不依赖任何流的流的流依赖为0x0(虚拟根流)。
- 共有相同父节点的依赖流之间顺序是不固定的。
- 专用标志导致流成为其父节点流唯一的子节点流,其他依赖流变成依赖此优先流。
4.3 依赖权重
- 所有有依赖的流以[1,256]的整数来标识权重。
- 具有相同父节点的流应该根据权重比例来分配资源。
5 发送应用数据(DATA帧)
- 应用数据可以分为多个DATA 帧,最后一帧要翻转帧首部的END_STREAM 字段。
- 可是,为减少队首阻塞,HTTP 2.0 标准要求DATA 帧不能超过2^14-1(16383)字节。长度超过这个阀值的数据,就得分帧发送。
参考:
- http2详解
- HTTP/2 服务器推送(Server Push)教程
- 《Web性能权威指南》
- RFC 7540