把HTTP消息分解为很多独立的帧之后,就可以通过优化这些帧的交错和传输顺序,进一步提升性能。为了做到这一点,每个流都可以带有一个31比特的优先值:
0 表示最高优先级。
231 -1表示最低优先级。
有了这个优先值,客户端和服务器就可以在处理不同的流时采取不同的策略,以最优的方式发送流、消息和帧。具体来讲,服务器可以根据流的优先级,控制资源分配(CPU、内存、带宽),而在响应数据准备好之后,优先将最高优先级的帧发送给客户端。
浏览器在渲染页面时,并非所有资源都具有相同的优先级:HTML文档本身对构建DOM不可或缺,CSS对构建CSSOM不可或缺,而DOM和CSSOM的构建都可能受到JavaScript资源的阻塞(参见10.1节的附注栏“DOM、CSSOM和JavaScript”),其他资源(如图片)的优先级都可以降低。
为加快页面加载速度,所有现代浏览器都会基于资源的类型以及它在页面中的位置排定请求的优先次序,甚至通过之前的访问来学习优先级模式——比如,之前的渲染如果被某些资源阻塞了,那么同样的资源在下一次访问时可能就会被赋予更高的优先级。
在HTTP 1.x中,浏览器极少能利用上述优先级信息,因为协议本身并不支持多路复用,也没有办法向服务器通告请求的优先级。此时,浏览器只能依赖并行连接,且最多只能同时向一个域名发送6个请求。于是,在等连接可用期间,请求只能在客户端排队,从而增加了不必要的网络延迟。理论上,HTTP管道可以解决这个问题,只是由于缺乏支持而无法付诸实践。
HTTP/2 一举解决了所有这些低效的问题:浏览器可以在发现资源时立即分派请求,指定每个流的优先级,让服务器决定最优的响应次序。这样请求就不必排队了,既节省了时间,也最大限度地利用了每个连接。
HTTP/2 没有规定处理优先级的具体算法,只是提供了一种赋予数据优先级的机制,而且要求客户端与服务器必须能够交换这些数据。这样一来,优先值作为提示信息,对应的次序排定策略可能因客户端或服务器的实现而不同:客户端应该明确指定优先值,服务器应该根据该值处理和交付数据。
在这个规定之下,尽管你可能无法控制客户端发送的优先值,但或许你可以控制服务器。因此,在选择HTTP/2 服务器时,可以多留点心!为说明这一点,考虑下面几个问题。
如果服务器对所有优先值视而不见怎么办?
高优先值的流一定优先处理吗?
是否存在不同优先级的流应该交错的情况?
如果服务器不理睬所有优先值,那么可能会导致应用响应变慢:浏览器明明在等关键的CSS和JavaScript,服务器却在发送图片,从而造成渲染阻塞。不过,规定严格的优先级次序也可能带来次优的结果,因为这可能又会引入队首阻塞问题,即某个高优先级的慢请求会不必要地阻塞其他资源的交付。
服务器可以而且应该交错发送不同优先级别的帧。只要可能,高优先级流都应该优先,包括分配处理资源和客户端与服务器间的带宽。不过,为了最高效地利用底层连接,不同优先级的混合也是必需的。
有了新的分帧机制后,HTTP/2 不再依赖多个TCP连接去实现多流并行了。现在,每个数据流都拆分成很多帧,而这些帧可以交错,还可以分别优先级。于是,所有HTTP/2 连接都是持久化的,而且客户端与服务器之间也只需要一个连接即可。
实验表明,客户端使用更少的连接肯定可以降低延迟时间。HTTP/2 发送的总分组数量比HTTP差不多要少40%。而服务器处理大量并发连接的情况也变成了可伸缩性问题,因为HTTP/2 减轻了这个负担。——HTTP/2.0 Draft 2”
每个来源一个连接显著减少了相关的资源占用:连接路径上的套接字管理工作量少了,内存占用少了,连接吞吐量大了。此外,从上到下所有层面上也都获得了相应的好处:
所有数据流的优先次序始终如一;
压缩上下文单一使得压缩效果更好;
由于TCP连接减少而使网络拥塞状况得以改观;
慢启动时间减少,拥塞和丢包恢复速度更快。
大多数HTTP连接的时间都很短,而且是突发性的,但TCP只在长时间连接传输大块数据时效率才最高。HTTP/2 通过让所有数据流共用同一个连接,可以更有效地使用TCP连接。
HTTP/2 不仅能够减少网络延迟,还有助于提高吞吐量和降低运营成本!
等一等,我听你说了一大堆每个来源一个TCP连接的好处,难道它就一点坏处都没有吗?有,当然有。
虽然消除了HTTP队首阻塞现象,但TCP层次上仍然存在队首阻塞(参见2.4节“队首阻塞”);
如果TCP窗口缩放被禁用,那带宽延迟积效应可能会限制连接的吞吐量;
丢包时,TCP拥塞窗口会缩小。