HTTTP 2.0原理解析

HTTP2.0官方网站:https://http2.github.io/
HTTP2.0英文文档:https://httpwg.org/specs/rfc7540.html
HTTP2.0中英文对照版:HTTP2.0中英文对照版
HTTP2.0流量调试:https://imququ.com/post/intro-to-nghttp2.html

HTTP2.0扫盲

什么是HTTP 2.0

HTTP 2.0(超文本传输协议第2版,最初命名为HTTP 2.0),是HTTP协议的的第二个主要版本,使用于万维网。HTTP 2.0是HTTP协议自1999年HTTP 1.1发布后的首个更新,主要基于SPDY协议(是Google开发的基于TCP的应用层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验)。

HTTP2.0和1.0的区别

  • HTTP 2.0采用二进制格式而非文本格式
  • HTTP 2.0是完全多路复用的,而非有序并阻塞的——只需一个连接即可实现并行
  • 使用报头压缩,HTTP 2.0降低了开销
  • HTTP 2.0让服务器可以将响应主动“推送”到客户端缓存中

HTTP 2.0为什么是二进制?

比起像HTTP/1.x这样的文本协议,二进制协议解析起来更高效、更紧凑,更重要的是错误更少。

为什么 HTTP 2.0 需要多路传输?

HTTP/1.x 有个问题叫线端阻塞(head-of-line blocking), 它是指一个连接(connection)一次只提交一个请求的效率比较高, 多了就会变慢。 HTTP/1.1 试过用流水线(pipelining)来解决这个问题, 但是效果并不理想(数据量较大或者速度较慢的响应, 会阻碍排在他后面的请求). 此外, 由于网络媒介(intermediary )和服务器不能很好的支持流水线, 导致部署起来困难重重。而多路传输(Multiplexing)能很好的解决这些问题, 因为它能同时处理多个消息的请求和响应; 甚至可以在传输过程中将一个消息跟另外一个掺杂在一起。所以客户端只需要一个连接就能加载一个页面。

消息头为什么需要压缩?

假定一个页面有80个资源需要加载(这个数量对于今天的Web而言还是挺保守的), 而每一次请求都有1400字节的消息头(着同样也并不少见,因为Cookie和引用等东西的存在), 至少要7到8个来回去“在线”获得这些消息头。这还不包括响应时间——那只是从客户端那里获取到它们所花的时间而已。这全都由于TCP的慢启动机制,它会基于对已知有多少个包,来确定还要来回去获取哪些包 – 这很明显的限制了最初的几个来回可以发送的数据包的数量。相比之下,即使是头部轻微的压缩也可以是让那些请求只需一个来回就能搞定——有时候甚至一个包就可以了。这种开销是可以被节省下来的,特别是当你考虑移动客户端应用的时候,即使是良好条件下,一般也会看到几百毫秒的来回延迟。

服务器推送的好处是什么?

当浏览器请求一个网页时,服务器将会发回HTML,在服务器开始发送JavaScript、图片和CSS前,服务器需要等待浏览器解析HTML和发送所有内嵌资源的请求。服务器推送服务通过“推送”那些它认为客户端将会需要的内容到客户端的缓存中,以此来避免往返的延迟。

HTTP2.0具体有哪些不一样

二进制分帧(Binary Framing)

二进制分帧层,是HTTP 2.0性能增强的核心。
HTTP 1.x在应用层以纯文本的形式进行通信,而HTTP 2.0将所有的传输信息分割为更小的消息和帧,并对它们采用二进制格式编码。这样,客户端和服务端都需要引入新的二进制编码和解码的机制。

如下图所示,HTTP 2.0并没有改变HTTP 1.x的语义,只是在应用层使用二进制分帧方式传输。

我们先了解几个概念:

  • 帧(Frame):HTTP 2.0通信的最小单位,每个帧包括帧首部、流标识符、优先值和帧净荷等。
  • 消息(Message):由一个或多个帧组合而成,比如一系列DATA帧和一个HEADERS帧组成了请求消息。
  • 连接(Connection):与 HTTP/1 相同,都是指对应的 TCP 连接;
  • 流(Stream):已建立的连接上的双向字节流。在HTTP 2.0中,数据流以消息的形式发送,而消息由一个或多个帧组成,帧可以在数据流上乱序发送,然后再根据每个帧首部的流标识符重新组装。二进制分帧是HTTP 2.0的基石,其他优化都是在这一基础上来实现的。

其中,帧类型又可以分为:

  • DATA:用于传输HTTP消息体;
  • HEADERS:用于传输首部字段;
  • SETTINGS:用于约定客户端和服务端的配置数据。比如设置初识的双向流量控制窗口大小;
  • WINDOW_UPDATE:用于调整个别流或个别连接的流量
  • PRIORITY: 用于指定或重新指定引用资源的优先级。
  • RST_STREAM: 用于通知流的非正常终止。
  • PUSH_ PROMISE: 服务端推送许可。
  • PING: 用于计算往返时间,执行“ 活性” 检活。
  • GOAWAY: 用于通知对端停止在当前连接中创建流。

多路复用(Request and Response Multiplexing)

基于二进制分帧层,HTTP 2.0可以在共享TCP连接的基础上,同时发送请求和响应。HTTP1.x中,如果想并发多个请求,必须使用多个TCP链接,且浏览器为了控制资源,还会对单个域名有6-8的个数限制,如下图,红色圈出来的请求就因域名链接数已超过限制,而被挂起等待了一段时间。

针对这一问题,在HTTP1.x的基础上,我们需要做很多优化,例如合并请求、图片精简、散列域名等

在 HTTP 2.0 中,有了二进制分帧之后,HTTP 2.0不再依赖TCP链接去实现多流并行了,在HTTP 2.0协议下:

  • 同域名下所有通信都在单个连接上完成。
  • 单个连接可以承载任意数量的双向数据流。
  • 数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装。

有了这些特性,性能会有极大的提升,因为:

  • 同个域名只需要占用一个TCP连接,消除了因多个TCP连接而带来的延时和内存消耗。
  • 单个连接上可以并行交错的请求和响应,之间互不干扰。

我们来对比下在Http1.x和http2.0,在传输三张图片时的流程

HTTP 1.x发起请求是串行的,image1返回后才能再发起image2,image2返回后才能再发起image3。

HTTP 2.0建立一条TCP连接后,并行传输着3个数据流,客户端向服务端乱序发送stream1~3的一系列的DATA帧,与此同时,服务端已经在返回stream 1的DATA帧

流优先级( Stream priority)

在HTTP 2.0中,每个请求都可以带一个31bit的优先值,0表示最高优先级, 数值越大优先级越低。有了这个优先值,客户端和服务器就可以在处理不同的流时采取不同的策略,以最优的方式发送流、消息和帧。例如按照css > js > img的优先级来加载资源,对于用户来说,呈现会更加友好。

服务器推送(Server push)

Server push是HTTP 2.0中一个很强大的功能:

  • 服务器除了响应客户端的请求外,还可以向客户端额外推送资源。
  • 服务器推送的资源有自己独立的URL, 可以被浏览器缓存,可以达到多页面共享。
  • 资源推送遵守同源策略,服务器不可随便推送第三方资源给客户端。
  • 客户端可以拒绝推送过来的资源。

有了这一特性,我们可以做什么?

  • 应用可以通过额外的http头部,列出需要服务器推送哪些资源。
  • 服务器可以解析请求的html,推测出客户端接下来需要请求的资源,然后提前向客户端推送。

头部压缩(Header Compression)

HTTP每一次通信都会携带一组头部,用于描述这次通信的的资源、浏览器属性、cookie等,例如

在HTTP 1.x中,这些信息都是以纯文本协议发送的,给每个请求增加了不小的负荷。
为了减少这块的开销并提升性能, HTTP 2.0会压缩这些首部:

  • HTTP 2.0在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送;
  • 首部表在HTTP 2.0的连接存续期内始终存在,由客户端和服务器共同渐进地更新;
  • 每个新的首部键-值对要么被追加到当前表的末尾,要么替换表中之前的值。

另外,HTTP 2.0使用了首部压缩技术,压缩算法使用HPACK。可让报头更紧凑,更快速传输,有利于移动网络环境。
需要注意的是,HTTP 2.0关注的是首部压缩,而我们常用的gzip等是报文内容(body)的压缩。二者不仅不冲突,且能够一起达到更好的压缩效果。

例如:下图中的两个请求, 请求一发送了所有的头部字段,第二个请求则只需要发送差异数据,这样可以减少冗余数据,降低开销。

应用层协商协议(APLN:Aplication Layer Protocol Negotiation)

客户端、服务器都需要升级才能支持HTTP 2.0,升级过程中就存在HTTP1.1、HTTP 2.0并存的情况,然而他们都使用的80端口,那么如何来选择使用什么协议通信呢?

APLN就是为了解决这个问题的,通过协商来选择通信的协议:

1、客户端发起请求,如果支持HTTP 2.0,则带upgrade头部:

1
2
3
4
5
GET /page HTTP/1.1
Host: server.example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: HTTP 2.0.0
HTTP2-Settings: (SETTINGS payload)

2、服务器不支持,则拒绝升级,通过HTTP1.1返回响应

1
2
3
4
HTTP/1.1 200 OK
Content-length: 243
Content-type: text/html
(... HTTP 1.1 response ...)

3、服务器支持,则接受升级,切换到新分帧,使用HTTP 2.0通信。

1
2
3
4
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: HTTP 2.0.0
(... HTTP 2.0 response ...)

使用协商协议,无论是哪一种情况,都不需要额外的往返,如果客户端通过记录或者其他方式,知道服务器支持HTTP 2.0,则直接使用HTTP 2.0通信,无需再协议协商。

最后

简而概之,HTTP 2.0的通过支持请求与响应的多路复用来减少延迟,通过压缩HTTP首部字段将协议开销降至最低,同时增加对请求优先级和服务器端推送的支持。HTTP 2.0性能得到了极大的提升,我们在HTTP 1.1时代做的有些优化反而成了鸡肋,在升级过程中,如何让HTTP 2.0 和 HTTP 1.x的用户都能得到最优的性能,这是对于我们的另外一大挑战。