## **多路复用**
在HTTP/2中,有两个非常重要的概念:帧(frame)和流(stream)。
**1、帧(frame)**
HTTP/2中数据传输的最小单位,因此帧不仅要细分表达HTTP/1.x中的各个部份,也优化了HTTP/1.x表达得不好的地方,同时还增加了HTTP/1.x表达不了的方式。
每一帧都包含几个字段,有length、type、flags、stream identifier、frame playload等,其中type代表帧的类型,在HTTP/2的标准中定义了10种不同的类型,包括上面所说的`HEADERS` frame和 `DATA` frame。此外还有
`PRIORITY`(设置流的优先级)
`RST_STREAM`(终止流)
`SETTINGS`(设置此连接的参数)
`PUSH_PROMISE`(服务器推送)
`PING`(测量RTT)
`GOAWAY`(终止连接)
`WINDOW_UPDATE`(流量控制)
`CONTINUATION`(继续传输头部数据)
**2、流(stream)**
“流”在HTTP/2中是一个逻辑上的概念,就是说在一个TCP连接上,我们可以向对方不断发送一个个的消息,这里每一个消息看成是一帧,而每一帧有个stream identifier的字段标明这一帧属于哪个“流”,然后在对方接收时,根据stream identifier拼接每个“流”的所有帧组成一整块数据。我们把HTTP/1.x每个请求都当作一个“流”,那么请求化成多个流,请求响应数据切成多个帧,不同流中的帧交错地发送给对方,这就是HTTP/2中的多路复用。
![](https://box.kancloud.cn/2015-10-27_562ee2c9f1375.jpg)
从上图我们可以留意到:
* 不同的流在交错发送;
* `HEADERS` 帧在 `DATA` 帧前面;
* 流的ID都是奇数,说明是由客户端发起的,这是标准规定的,那么服务端发起的就是偶数了。
多路复用让HTTP连接变得很廉价,只需要创建一个新流即可,这不需要多少时间,而在HTTP/1.x时代却要经历三次握手时间或者队首阻塞等问题。而且创建新流默认是无限制的,也就是可以无限制的并行请求下载。不过,HTTP/2还是提供了 `SETTINGS_MAX_CONCURRENT_STREAMS` 字段在 `SETTINGS` 帧上设置,可以限制并发流数目,标准上建议不要低于100以保证性能。
优化Web性能有一个常用的技术,就是图片延迟加载,目的是除了节省流量外,还能避免图片资源与其他重要的脚本资源竞争下载。
HTTP/2提供了流的优先级与依赖性这种机制,可用 `HEADERS` 帧或 `PRIORITY` 帧设置,不过协议并没有提供如何处理优先级的具体算法,这可由服务器灵活应对。我用个例子来说明这个机制。
~~~
<!-- a.html -->
<html>
<body>
<script src="a.js"></script>
<img src="a.jpg">
<img src="b.jpg">
<link rel="stylesheet" type="text/css" href="style.css">
</body>
</html>
~~~
浏览器是边下载边解析的,文档解析器首先遇到a.js,它就会去下载并且阻塞页面,同时,资源探测器会继续向下扫描,发现a.jpg、b.jpg和style.css并服务器发起请求。在没有优先级机制时,a.jpg、b.jpg会跟重要的a.js、style.css竞争下载,但在HTTP/2中,浏览器可以给a.jpg、b.jpg设置较低的优先级,另外依赖关系为
![](https://box.kancloud.cn/2015-10-27_562ee2ca08fcd.jpg)
这样服务器根据优先级信息,首先吐出a.js、style.css,再吐出图片,因此页面在没有图片的情况下提前进入可交互状态。例子所说的是在浏览器层面上harcode的一个优先级策略,再比如上文提到的prefetch就可以给一个更低的优先级。在代码层面上,也许之后会提供一些控制优先级的特性,类似于目前只有IE支持的lazyload attribute。