HTTP2探析

HTTP2探析

随着Web网站的发展,在网站的界面变得越来越美观和用户友好的同时,网站的复杂度越来越大,并且涉及的Web资源也越来越多。大型网站动辄数十个资源、数十兆大小的资源需要传输到浏览器中做界面展示,如何让大型网站更快速的载入成了Web优化的重点方向。在资源的不断增多后,HTTP1.x协议已经无法提供很好的传输效率来满足性能需求,因此HTTP2被提出,且经过数年的发展,现在已经成熟并普及使用,带来了可观的性能提升。那么本文将介绍HTTP2的相关优化,来看看HTTP1.x中的性能缺陷是如何解决的。

协议演进

为了更好地了解HTTP2做出的优化改进,我们可以从一次网站访问中看看HTTP1.x协议的缺陷:

当我们在浏览器中输入网址,浏览器会向目标服务器发送HTTP请求获取网页原内容。当解析网页html后,提取html引用的资源,如css、js文件。浏览器会再向资源服务器发送http请求,而由于当前的大部分网站在一次访问中都至少要获取数十个资源,因此访问一个网站会产生数十次甚至上百次的http请求。待获取相关资源后,再对页面进行渲染展示呈现最终的效果。

http1.1中借助tcp长连接,可以在一条连接中发送、接收多个http请求,但是只有上一个http请求的响应返回后才能发起下一次http请求。为了提高tcp连接利用率,http1.1也引入了流水线传输机制。此外,为了提高并发度,浏览器通常也会与服务器建立多个tcp连接来传输更多的http包。

从上述场景中,我们可以看到 HTTP1.x 协议存在的问题

  • 严格的请求-响应模型。由于只有一个http请求被响应之后才能发出第二个http请求,导致tcp连接利用率不足。尽管HTTP1.1引入了流水线传输机制,但是由于队头阻塞、优化流程控制复杂等原因,流水线机制并未带来很好的普及以及效率提升。
  • Header内容重复率高、未压缩。一次网页访问中产生的一组HTTP请求响应中,有大部分Header中的键值对重复发送,浪费带宽与降低传输效率

针对这些问题,HTTP2提出了以下方案来提升性能,下文将逐一介绍这些方案。

  • 二进制帧传输
  • TCP连接多路复用
  • 头部压缩(HPack算法、静态霍夫曼压缩与动态更新)
  • 支持HTTP请求优先级和服务器推送

首先可以先通过此网站来直观地感受下HTTP2与HTTP1的性能差距,然后带着对HTTP2竟然如此快的惊叹来看看HTTP2中具体的优化点 Performance testing HTTP/1.1 vs HTTP/2 vs HTTP/2 + Server Push for REST APIs

二进制帧传输

HTTP2中的性能改进基于将原先的文本协议优化为二进制的传输协议,并且通过将一个HTTP包划分为多个二进制帧来做更进一步的优化。

HTTP1.1中使用\r\n来作为文本行的分隔符,HTTP2则直接通过二进制编码来使得编码更加紧凑,来提升编码传输效率。尽管改变了底层的编码方式,但是上层的HTTP语义并未改变,例如HTTP方法、资源路径、常用的HTTP Header等,因此底层编码的改变对上层应用无感,只需要HTTP协议栈程序作出相应的改造。

TCP连接多路复用

现代浏览器往往会通过多个TCP连接来提高并发度和传输效率,然而HTTP连接大多数是短暂的,在多TCP连接的使用下,HTTP1.x中的持久连接属性并未带来很好的效率提升。并且多个TCP连接在HTTPS协议下,也需要多次TLS的握手,也会带来不小的开销。此外,原先HTTP1.x中为了提升连接利用率的持久连接在严格的请求-响应模型下也表现不佳。因此,如果能将多个http包在同一个tcp连接中传输,那么这种多路复用的模型就能提升tcp连接的利用率。

新协议中的多路复用模型允许多个请求、响应报文通过切分的二进制帧在连接中交错的传递,作为HTTP2的性能改进基础,带来了更多优秀的传输特性,核心包括:

  • 允许多报文同时传输,不会出现一个报文阻塞另一个报文(队头阻塞)的问题
  • 以单条TCP连接中的并发特性来提高连接的利用率,以此避免以往需要新建多条连接的连接维护成本

帧、消息、流

基于二进制帧传输和TCP连接的多路复用,为了更好地描述HTTP包传输的过程,HTTP2引入了新的概念对象。

  • 帧:帧代表着新协议中传输的最小实体,每个帧通过帧头来标明所属的流
  • 消息:消息对应着逻辑的HTTP请求、响应报文,由多个二进制帧组成
  • 流:流对应着TCP连接之上的双向二进制流,包含多个消息,并可支持按优先级传输。

流优先级

一般来说,web页面的所需的资源都有一定的优先级区分。例如需要马上给用户展示的资源有着最高的优先级,而用户暂时看不到的地方所需的资源是相对低的。以往的HTTP协议中,无法直接定义资源的优先级,因此在资源都是多连接并发获取的情况下,也无法确保某个资源被优先获取。

在HTTP2引入单连接多流的模型下,每条流中的资源可以定义优先级,形成资源优先级树的形式,使得高优先级的资源可以由服务器优先分配相关的资源来返回。资源的优先级可以定义为1-256之间的数值,作为树节点的权重,如下图所示:

同一层的资源节点会通过优先级权重来计算。例如在第一个例子中,A资源占据 12/16 的权重,B资源则是 4/16 的权重。

HTTP2的资源优先级不仅支持静态的定义,也支持客户端动态的定义,以支持更好的浏览器优化。

流量控制

HTTP2引入了类似于TCP流量控制的机制来控制流之间的资源传输。这样的机制允许客户端和服务器更好的控制流量传输速率,例如客户端请求了大量高优先级的视频资源,但是此时用户停止了视频播放,那么就可以通过流量控制来减少暂时不必要的传输量。

那么为什么不使用TCP的流量控制来完成这个功能呢?这是因为TCP流量控制粒度不足,并且TCP报文为内核协议栈控制,无法提供应用层API控制。因此为了更好地支持上层应用的控制需求,在应用层处也实现了基于窗口的流控机制。

服务器推送机制

一般的网页访问机制都需要加载一系列的额外资源才能展示整个页面,而这些资源对于服务器来说都是明确的。因此服务器可以将这些资源直接地推送给客户端,而不用客户端重复的发送请求,以此来避免带宽的浪费。

服务器推送的机制是比较简单的,推送流会以PUSH_PROMISE的帧头开始,并在之后将需要推送的资源的Header Frame推送给客户端。客户端接收到资源列表之后,可以根据资源缓存、资源需要与否的情况来拒绝某些资源的推送。

头部压缩

在HTTP1.x协议中,Header中的元数据常会在同一资源域重复传输,并在在加上Cookie之后Header所占的数据量会相对较大,通常在600-800byte。尽管这些元信息在传输的过程中变化较少,但每次传输中都重新全量发送Header无疑是一种带宽传输的浪费。

由此HTTP2使用了基于静态哈夫曼编码的HPACK算法来对头部进行压缩,传输的过程中,连接双方会维护Header的元信息表,在需要更新的时候,双方进行增量更新来减少传输量。这一优化措施减少了Header所需的传输量,较好的提高了传输效率。

总结

HTTP2的前身基于Google的SPDY协议,在此基础上由委员会持续优化并制定相关的规范而推出,有效的优化了HTTP1.x中传输效率低下的问题,并在实际环境中取得了较好的优化成果。HTTP3.0则会将底层协议替换为QUIC协议,并做出更多的优化,后续的博文中也会研究一下HTTP3.0优化。

Reference

HTTP/2 简介  |  Web Fundamentals  |  Google Developers