《HTTP/2: the Future of the Internet》 是 Akamai 公司建立的一个官方的演示,用以说明 HTTP/2 相比于之前的 HTTP/1.1 在性能上的大幅度提升。 同时请求 379 张图片,从Load time 的对比可以看出 HTTP/2 在速度上的优势。
HTTP2.0性能增强的核心,全在于新增的二进制分帧层,它定义了如何封装HTTP消息并在客户端与服务器之间传输。这里所谓的“层”,指的是位于套接字接口与应用可见高层HTTP API之间的一个新机制:HTTP语义,包括各种动词、方法、首部,都不受影响,不同的是传输期间对它们的编码方式变了。HTTP1.x以换行符作为纯文本的分隔符,而HTTP2.0将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式的编码。
这样一来,客户端和服务器为了相互理解,必须都使用新的二进制编码机制:HTTP1.x客户端无法理解只支持HTTP2.0的服务器,反之亦然。不过不要紧,现有的应用不必担心这些变化,因为客户端和服务器会替它们完成必要的分帧工作。
HTTPS是二进制分帧的另一个典型示例:所有HTTP消息都以透明的方式为我们编码和解码,从而实现客户端与服务器安全通信,但不必对应用进行任何修改。HTTP2.0的工作原理差不多也是这样。
新的二进制分帧机制改变了客户端与服务器之间交互数据的方式。为了说明这个过程,我们需要了解HTTP2.0的几个新概念:
流 已建立的连接上的双向字节流。
消息 与逻辑消息对应的完整的一系列数据帧。
帧 HTTP2.0通信的最小单位,每个帧包含帧首部,至少也会标识出当前帧所属的流。
HTTP2.0通信都在一个连接上完成,这个连接可以承载任意数据量的双向数据流。相应地,每个数据流以消息的形式发送,而消息由一或多个帧组成,这些帧可以乱序发送,然后再根据每个帧首部的流标识符重新组装。HTTP2.0的所有帧都采用二进制编码,所有首部数据都会被压缩。
这简简单单的几句话里浓缩了大量的信息:
- 所有通信都在一个TCP连接上完成;
- 流是连接中的一个虚拟信道,可以承载双向的消息。每个流都有一个唯一的整数标识符;
- 消息是指逻辑上的HTTP消息,比如请求、相应等,由一或多个帧组成;
- 帧是最小的通信单位,承载这特定类型的数据,如HTTP首部、负荷等;
简言之,HTTP2.0把HTTP协议通信的基本单位缩小为一个一个的帧,这些帧对应着逻辑流中的消息。相应地,很多流可以并行的在同一个TCP连接上交换消息。
在HTTP1.x中,如果客户端想发送多个并行的请求以及改进性能,那么必须使用多个TCP连接。这是HTTP1.x交付模型的直接结果,该模型会保证每个连接每次只交付一个响应(多个响应必须排队)。更糟糕的是,这种模型也会导致队首阻塞,从而造成底层TCP连接的效率低下。
HTTP2.0中新的二进制分帧层突破了这些限制,实现了多向请求和响应:客户端和服务器可以把HTTP消息分解为互不依赖的帧,然后乱序发送,最后再在另一端把它们重新组合起来。
把HTTP消息分解为独立的帧,交错发送,然后在另一端重新组装是HTTP2.0最重要的一项增强。事实上,这个机制会在整个Web技术栈中引发一系列连锁反应,从而带来巨大的性能提升,因为:
- 可以并行交错的发送请求,请求之间互不影响;
- 可以并行交错的发送响应,响应之间互不干扰;
- 只使用一个连接即可并行发送多个请求和响应;
- 消除不必要的延迟,从而减少页面加载的时间;
- 不必再为绕过HTTP1.x限制而多做很多工作。
总之,HTTP2.0的二进制分帧机制解决了HTTP1.x中存在的队首阻塞问题,也消除了并行处理和发送请求及响应时对多个连接的依赖。结果就是应用速度更快、开发更简单、部署成本更低。
支持多向请求和响应,可以省掉对HTTP1.x限制所费的那些工作,比如拼接文件、图片精灵、域名分区。类似地,通过减少TCP连接的数量,HTTP2.0也会减少客户端和服务器的CPU及内存占用。
把HTTP消息分解为很多独立的帧之后,就可以通过优化这些帧的交错和传输顺序,进一步提升性能。为了做到这一点,每个流都可以带有一个31比特的优先值:
0表示最高优先级; (2^31)-1表示最低优先级。
有了这个优先值,客户端和服务器就可以在处理不同的流时采用不同的策略,以最优的方式发送流、消息和帧。具体来讲,服务器可以根据流的优先级,控制资源分配(CPU、内存、带宽),而在响应数据准备好之后,优先将高优先级的帧发送给客户端。
浏览器在渲染页面时,并非所有资源都具有相同的优先级:HTML文档本身对构建DOM不可或缺,CSS对构建CSSOM不可或缺,而DOM和CSSOM的构建都可能会受到JavaScript资源的阻塞,其他资源(如图片)的优先级都可以降低。为加快页面加载的速度,所有现代浏览器都会基于资源的类型以及它在页面中的位置排定请求的优先次序,甚至通过之前的访问来学习优先级模式–比如,之前的渲染如果被某些资源阻塞了,那么同样的资源在下一次访问时可能就会被赋予更高的优先级。
有了新的分帧机制后,HTTP2.0不再依赖多个TCP连接去实现多流并行了。现在,每个数据流都拆分成很多帧,而这些帧可以交错,还可以分别优先级。于是,所有HTTP2.0连接都是持久化的,而且客户端与服务器之间也只需要一个连接即可。
每个来源一个连接显著减少了相关资源的占用:连接路径上的套接字管理工作量少了,内存占用少了,连接的吞吐量大了。此外,从上到下所有层面上也都获得了相应的好处:
- 所有话剧流的优先次序始终如一;
- 压缩上下文单一使得压缩效果更好;
- 由于TCP连接减少而使网络拥塞状况得以改观;
- 慢启动时间减少,拥塞和丢包回复速度更快。
在同一个TCP上传输多个数据流,就意味着要共享带宽。标定数据流的优先级有助于按序交付,但只有优先级还不足以确定多个数据流或多个连接间的资源分配。为解决这个问题,HTTP2.0为数据流和连接的流量控制提供了一个简单的机制:
- 流量控制基于每一跳进行,而非端到端的控制;
- 流量控制基于窗口更新帧进行,即接收方广播自己准备接收某个数据流的多少字节,以及整个连接要接收多少字节;
- 流量控制窗口大小通过WINDOW_UPDATE帧更新,这个字段指定了流ID和窗口大小递增值;
- 流量控制有方向性,即接收放可能根据自己的情况为每个流乃至整个连接设置任意窗口大小;
- 流量控制可以由接收方禁用,包括针对个别的流和针对整个连接。
HTTP2.0建立连接之后,客户端与服务器交换SETTINGS帧,目的是设置双向的流量控制窗口大小。除此之外,任何一端都可以选择禁用个别流或整个连接的流量控制。
HTTP2.0新增的一个强大的新功能,就是服务器可以对一个客户端请求发送多个响应。换句话说,除了对最初请求的响应外,服务器还可以额外想客户端推送资源,而无需客户端明确的请求。
建立HTTP2.0连接后,客户端与服务器交换SETTINGS帧,借此可以限定双向并发的流的最大数量。因此,客户端可以限定推送流的数量,或者通过设置为0而完全禁用服务器推送。
所有推送的资源都遵守同源策略。换句话说,服务器不能随便将第三方资源推送给客户端,而必须是经过双方确认才行。
HTTP的每次通信都会携带一组首部,用于描述传输的资源及其属性。在HTTP1.x中这些元数据都是以纯文本形式发送的,通常会给每个请求增加500-800字节的负担。如果算上Cookie,增加的负担更重。为减少这些,HTTP2.0会压缩首部元数据。 HTTP2.0在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键值对,对于相同的数据,不再通过每次请求和响应发送; 首部表在HTTP2.0的连接存续期内始终存在,有客户端和服务器共同更新; 每个新的首部键值对要么被追加到当前表的末尾,要么替换表中之前的值。
于是,HTTP2.0连接的两端都知道已经发送了哪些首部。请求与响应首部的定义在HTTP2.0中基本没有改变,只是所有的首部健必须全部小写。
HTTP2.0其实可以支持非HTTPS的,但是现在主流的浏览器像chrome,firefox表示还是只支持基于 TLS 部署的HTTP2.0协议,所以要想升级成HTTP2.0还是先升级HTTPS为好。
当你的网站已经升级HTTPS之后,那么升级HTTP2.0就简单很多,如果你使用NGINX,只要在配置文件中启动相应的协议就可以了,可以参考NGINX白皮书,NGINX配置HTTP2.0官方指南 https://www.nginx.com/blog/nginx-1-9-5/。
使用了HTTP2.0那么,原本的HTTP1.x怎么办,这个问题其实不用担心,HTTP2.0完全兼容HTTP1.x的语义,对于不支持HTTP2.0的浏览器,NGINX会自动向下兼容的。
HTTP/1.* 一次请求-响应,建立一个连接,用完关闭;每一个请求都要建立一个连接;
HTTP/1.1 Pipeling解决方式为,若干个请求排队串行化单线程处理,后面的请求等待前面请求的返回才能获得执行机会,一旦有某请求超时等,后续请求只能被阻塞,毫无办法,也就是人们常说的线头阻塞;
HTTP/2多个请求可同时在一个连接上并行执行。某个请求任务耗时严重,不会影响到其它连接的正常执行。