HTTP/2 核心特性

前言

HTTP/1.1服役的这二十余年,互联网发生了翻天覆地的变化,由于各种业务场景的复杂度今非昔比,HTTP/1.1越来越容易遭受“嫌弃”,此时HTTP/2闪亮登场,本文主要列举描述其核心特性。

HTTP/2 的大多数实用特性归功于 Google 在 SPDY 协议上开展的工作。在 HTTPbis 工作组开始起草 HTTP/2 RFC 的第一个版本时,SPDY 已证明一个主要 HTTP 版本更新切实可行。因为已经部署并开始采用 SPDY,所以有证据表明更新的协议在自然环境下具有更高的性能。

HTTP/2 成功的关键在于,它实现了显著的性能改善,同时保持了 HTTP 范例,以及 HTTP 和 HTTPS 模式。该工作组规定,向 HTTP/2 的迁移必须透明,而且使用者不会受到任何影响。

该协议最吸引人的特性包括:

  • 新升级路径
  • 二进制分帧
  • 多路复用
  • 服务器推送
  • 报头压缩
  • 数据流优先级
  • 流控制

新升级路径(基于HTTPS)

HTTP/2 升级路径与标准路径稍有不同,省去了一些协商。对于基于 HTTP/2 的安全连接,无法通过升级标头请求切换协议,并收到一条让人安心的“101 switching”HTTP 状态。相反,通过使用一个名为应用层协议协商 (ALPN) 的新扩展,客户端向服务器告知它能理解的通信协议(按偏好排序)。服务器然后使用该列表中它也理解的第一个协议作为响应。

SPDY 需要一个安全连接,虽然社区迫于压力会建立这样的连接,但 HTTP/2 规范没有强制要求这么做。但是,所有主要浏览器供应商都仅在 TLS 上实现 HTTP/2,而且不支持不安全的连接。这实际上会迫使 Web 应用程序实现者对所有 HTTP/2 流量使用 TLS(来源:caniuse.com)。curl 用户仍可采用通过 HTTP 升级标头的升级路径,因为它将实现既明确又安全的连接。

所以虽然RFC规范并没有约束HTTP/2必须基于HTTPS才能使用,但业界浏览器实现都依赖HTTPS前提条件,如果要升级到HTTP/2,第一步是升级到HTTPS。

二进制分帧

或许 HTTP/2 的最重要改变是转换为二进制协议。对于开发人员,这可以说是性能增强的焦点。新协议称为二进制分帧层(binary framing layer),它重新设计了编码机制,而没有修改方法、动词和标头的熟悉语义。

最重要的是,所有通信都在单个 TCP 连接上执行,而且该连接在整个对话期间一直处于打开状态。这可能得益于二进制协议将通信分解为帧的方式:这些帧交织在客户端与服务器之间的双向逻辑流中。

连接的拓扑结构

正如我提到的,在 HTTP/2 的新范例中,仅在客户端与服务器之间建立了一个 TCP 连接,而且该连接在交互持续期间一直处于打开状态。在此连接上,消息是通过逻辑流进行传递的。一条消息包含一个完整的帧序列。在经过整理后,这些帧表示一个响应或请求。

下图演示了连接组件之间的关系,展示了一个用于建立多个流的连接。在流 1 中,发送了一条请求消息,并返回了相应的响应消息。

图. HTTP/2 连接的拓扑结构
在这里插入图片描述
我们将分别查看每个概念。

连接和流

仅与一个对等节点建立一个连接,并在该连接上传输多个流。因为流可以交织,所以可以同时快速的传输多个流。

消息

消息是一组帧。在对等节点上重建这些帧时,它们形成一个完整的请求或响应。特定消息的帧在同一个流上发送,这意味着一个请求或响应只能映射到一个可识别的流。

帧是通信的基本单位。每个帧有一个标头,其中包含帧的长度和类型、一些布尔标志、一个保留位和一个流标识符,如下图所示。
图. 帧分解
在这里插入图片描述
上图括号中的数字表示固定位长,Frame Payload的长度由Length的值决定,此“火车”各车厢具体含义参见下表。

描述 长度 含义
Length 24 Frame Payload的长度
Type 8 帧类型,参见表二
Flags 8 帧标志位,配合帧类型使用,帧类型不同含义不同
R 1 保留位,目前不允许使用
Stream Identifier 31 流标志,流是一系列帧组成的抽象概念,此ID表明帧所在的流
Frame Payload Length Value 数据负载

长度

length 字段记录帧的大小,它最多可在一个 DATA 帧中携带 224 个字节(约 16 MB),但默认的最大值设置为 214 个字节 (16 KB)。帧大小可以通过协商调得更高一点。

类型

type 字段标识帧的用途,可以是以下 10 种类型之一:

Type 作用
SETTINGS 接收者向发送者通告己方设定,设置项参见下表
HEADER 用于发送HTTP请求或响应头并打开一个流,HTTP/1.1中的请求行与响应行被拆分到标头中表示
CONTINUATION 协助HEADERS/PUSH_PROMISE等单帧无法包含完整的报头剩余部分数据
DATA 请求与响应的内容载体
PUSH_PROMISE 服务器端通知对端初始化一个新的推送流准备稍后推送数据
PING 发送者测量最小往返时间,心跳机制用于检测空闲连接是否有效
PRIORITY 表达发送方对流优先级权重的建议值
WINDOW_UPDATE 通知对方流量控制端口大小变化
RST_STREAWM 允许立即终止流,表明请求取消或出现错误
GOAWAY 正常关闭流
SETTINGS设置项 作用
SETTINGS_HEADER_TABLE_SIZE 接收者报头表的字节数最大值,默认为4096字节
SETTINGS_ENABLE_PUSH 0禁止服务器推送,1允许推送
SETTINGS_MAX_CONCURRENT_STREAMS 发送者允许可打开流的最大值,建议值100,0表示不能打开新的流
SETTINGS_INITIAL_WINDOW_SIZE 发送端流量控制窗口大小,用于控制整个连接的传输速度
SETTINGS_MAX_HEADER_LIST_SIZE 发送端通告自己准备接收的报头集合最大字节数

标志

flag 字段是一个布尔值,指定帧的状态信息:

  • DATA 帧可定义两个布尔标志:END_STREAM 和 PADDED,前者表示数据流结束,后者表示存在填充数据。
  • HEADERS 帧可以将相同的标志指定为 DATA 帧,并添加两个额外的标志:END_HEADERS 和 PRIORITY,前者表示标头帧结束,后者表示设置了流优先级。
  • PUSH_PROMISE 帧可以设置 END_HEADERS 和 PADDED 标志。

所有其他帧类型都无法设置标志。

流标识符

流标识符用于跟踪逻辑流的帧成员关系。成员每次仅属于一条消息和流。流可以提供优先级建议,这有助于确定分配给它的网络资源。我稍后会更详细地解释流优先化。

示例

在HTTP/2之前,请求与响应的内容都是直接明文传输,比如客户端发起一个请求,请求的起始行内容GET /res HTTP/1.1直接传输给服务器,服务器得到的数据就是GET /res HTTP/1.1

到了HTTP/2,所有的通信数据都经过了二进制分帧处理,帧就是火车,而数据就是乘客。针对上面的起始行数据,假设其二进制表示为10000000000,那么我们的火车可能可能是这样的:

|火车|控制室|乘务员|餐车|10000000000|

一列装不下,那就两列:

|火车1号|控制室|乘务员|餐车|100000|
|火车2号|控制室|乘务员|餐车|00000|

到达目的地之后,服务器再从火车中提取“乘客”还原成一个整体10000000000

多路复用(请求/响应复用)

如果用火车比喻帧那么可用车次比作流,不同车次上的乘客互不影响。假设有三个旅行团的人在远方等待被接回到旅行社,且旅行团的人一旦混在一起就无法区分,这时我们可选择同时派出三个不同车次的火车把三批人分别接回旅行社,这就是多车次复用。

车次跑在铁轨上对应流建立在TCP连接上,铁轨上可同时存在不同的车次且可重复使用,TCP连接上的流也一样,同一个连接中可以有大量同时处于传输中的流,这就是多路复用概念。

在HTTP/1.1中,一个TCP连接上只能同时存在一个进行中的HTTP事务,假设客户端与服务器只打开一个TCP连接,如果需要完成10个HTTP事务,只能one-by-one逐个处理,其中任何一个出现网络异常,都会阻塞其后面的请求,这种现象称为队头阻塞,而HTTP/2利用多路复用完美解决了HTTP/1.1的队头阻塞问题。

多路复用还体现出HTTP/2与HTTP/1.1的一个重要差异:

  • HTTP/2只打开一个TCP连接然后通过多个(SETTINGS_MAX_CONCURRENT_STREAMS)虚拟流与服务器通信
  • HTTP/1.1浏览器端针对单域名默认使用6个TCP连接与服务器进行通信

在做网站优化时,使用6个TCP连接的并发能力,还是使用单个TCP连接的多路复用特性,不同的情况下可能各具优势,比如极端情况“网站只展示6张1M的图片”,6个TCP连接的方式可能更有利,这主要是因为TCP的慢启动、拥塞机制,由于内容过多本文不做讨论。


单一 TCP 连接的问题在于,一次只能发出一个请求,所以客户端必须等到收到响应后才能发出另一个请求。这就是 “线头阻塞” 问题。正如之前讨论的,典型的变通方案是打开多个连接;每个请求一个连接。但是,如果可以将消息分解为更小的独立部分并通过连接发送,此问题就会迎刃而解。

这正是 HTTP/2 希望达到的目标。将消息分解为帧,为每帧分配一个流标识符,然后在一个 TCP 连接上独立发送它们。此技术实现了完全双向的请求和响应消息复用,如下图所示。
在 TCP 连接上交织的帧
在这里插入图片描述
图中的图解显示在一个连接上快速传输了 3 个流。服务器发送两个响应,客户端发送一个请求。

在流 1 中,服务器为一个响应发送 HEADERS 帧;在流 2 中,它为另一个响应发送 HEADERS 帧,随后为两个响应发送 DATA 帧。两个响应按如图所示的方式交织。在服务器发送响应的过程中,客户端发送一条新消息的 HEADERS 和 DATA 帧作为请求。这些帧也与响应帧交织在一起,如下图所示。
HTTP/2 将请求/响应帧交织在一起
在这里插入图片描述
所有帧在另一端重新组装,以形成完整的请求或响应消息。

帧交织有许多好处:

  • 所有请求和响应都在一个套接字上发生。
  • 所有响应或请求都无法相互阻塞。
  • 减少了延迟。
  • 提高了页面加载速度。
  • 消除了对 HTTP 1.1 工具的需求。

将 HTTP 请求映射到 HTTP/2 帧
在这里插入图片描述
我们将左侧的一个 HTTP 请求映射到右侧的一个 HEADERS 帧。

在 HEADERS 帧中,设置了两个标志。第一个是 END_STREAM,它设置为 true(由加号表示),表明该帧是给定请求的最后一帧。END_HEADERS 标志也设置为 true,表明该帧是流中最后一个包含标头信息的帧。

HEADERS 帧中的标头属性反映了 HTTP 1.1 请求中设置的属性。因为 HTTP/2 一定要保持 HTTP 协议的语义,所以必须这么做。

接下来,让我们来看看该请求的响应。

将 HTTP 请求映射到帧

下图的左侧是一个 HTTP 1.1 标头响应。右侧是使用两个 HTTP/2 帧表示的同一个响应:HEADERS 和 DATA。

将 HTTP 响应映射到 HTTP/2 帧
在这里插入图片描述
HEADERS 帧中,END_STREAM 表明该帧不是流中的最后一帧,而 END_HEADER 表明它是最后一个包含标头信息的帧。在 DATA 帧中,END_STREAM 表明它是最后一帧。

服务器推送

预留车次从服务器端发车,这种功能就称为服务器推送,这个特性利用的好可以让部分请求节省半个RTT时长。假设只考虑报文的传输耗时,如果没有服务器推送,客户端获取资源耗时至少是数据包在网络上的一次往返时延(RTT),借助服务器推送可以省下数据包由客户端传输到服务器的时延。

假设用户访问页面index.html包含index.js,如果没有服务器推送,需要先获取index.html再获取index.js,需要2个RTT时长,而借助服务器推送可以做到用户访问index.html的同时返回index.js内容,较前者可节省1个RTT时长。前面之所以称主动推送可以让部分请求只节省半个RTT时长,是因为该例子中所节省的1个RTT时长中有半个本来消耗在顺序依赖的等待上,没有把这半个看成主动推送节省的时间,而是主动推送时机所带来的优化。

要用好服务器推送,关键问题在于决定推送时机与推送内容。推送内容一般是可缓存资源,不能用户一访问index.html就推送index.js,在缓存有效的情况下这种推送就是在浪费带宽,也不能在用户一访问网站,就把可能用到的所有资源都推送过去,因为用户可能用不上。


服务器推送使服务器能预测客户端请求的资源需求。然后,在完成请求处理之前,它可以将这些资源发送到客户端。

要了解服务器推送的好处,可以考虑一个包含图像和其他依赖项(比如 CSS 和 JavaScript 文件)的网页。客户端发出一个针对该网页的请求。服务器然后分析所请求的页面,确定呈现它所需的资源,并主动将这些资源发送到客户端的缓存。在执行所有这些操作的同时,服务器仍在处理原始网页请求。客户端收到原始网页请求的响应时,它需要的资源已经位于缓存中。

那么 HTTP/2 如何管理服务器推送而不会让客户端过载?针对希望发送的每个资源,服务器会发送一个 PUSH_PROMISE 帧,但客户端可通过发送 RST_STREAM 帧作为响应来拒绝推送(例如,如果浏览器的缓存中已包含该资源)。重要的是所有 PUSH_PROMISE 都在响应数据之前发送,所以客户端知道它需要请求哪些资源。

报头压缩

由于不同HTTP事务请求与响应中Header部分重复内容过多,导致网络上传输了过多重复信息,影响到传输效率,所以在HTTP/2中引入了标头压缩以减少数据传输大小。比如要传送cache-control: public, max-age=600,在HTTP/2中可能只需要传送1:2, 3=600即可,此方法针对大cookie的情况,优化效果更为显著。

实现原理基于标头静态表+动态表,比如上述例中的1:2, 3=600按照下表还原即可得到原始内容,当然这里只是随意举例,并不严谨。

code value
1 cache-control
2 public
3 max-age

HTTP/2 协议拥有配套的 HPACK。HPACK 的目的是减少客户端请求与服务器响应之间的标头信息重复所导致的开销。报头压缩的实现方式是,要求客户端和服务器都维护之前看见的标头字段的列表。未来在构建引用了已看见标头列表的消息时可以使用此列表。

压缩同一个连接上的两个请求的标头
在这里插入图片描述
在图中的两个请求中,标头信息是重复的。唯一的不同在请求的资源上(已采用黄色突出显示)。HPACK 报头压缩可以在这里派上用场。在第一个请求后,它仅需发送与前一个标头的不同之处,因为服务器保留着以前看见的标头的列表。除非设置了标头值,否则会假设后续请求拥有与之前的请求相同的标头值。

数据流优先级

将HTTP消息分解为很多独立的帧之后,我们就可以复用多个数据流中的帧,客户端和服务器交错发送和传输这些帧的顺序就成为关键的性能决定因素。为了做到这一点,HTTP/2标准允许每个数据流都有一个关联的权重和依赖关系:

  • 可以向每个数据流分配一个介于 1 至 256 之间的整数
  • 每个数据流与其他数据流之间可以存在显式依赖关系

数据流依赖关系和权重的组合让客户端可以构建和传递“优先级树”,表明它倾向于如何接收响应。反过来,服务器可以使用此信息通过控制CPU、内存和其他资源的分配设定数据流处理的优先级,在资源数据可用之后,带宽分配可以确保将高优先级响应以最优方式传输至客户端。

本小段内容摘录自Google HTTP/2简介,这个特性要用好,感觉比服务器推送更有挑战


消息帧通过流进行发送。每个流都分配了一个优先级,用于确定它的处理顺序,以及它将收到的资源量。

将该优先级输入到给定流的标头帧或优先级帧中,优先级可以是 0 到 256 之间的任何数字。

可以定义依赖关系,允许在一个资源之前加载另一个资源。也可以将优先级组合到一个依赖树中,让开发人员对分配给每个流的重要性有更多控制权。

用于流优先化的依赖树
在这里插入图片描述
在图中,字母表示流标识符,数字表示分配给每个流的权重。树的根是流 A,首先会向它分配资源,然后才向依赖它的流 B 和 C 分配资源。为流 B 分配了 40% 的可用资源,流 C 收到了 60% 的可用资源。流 C 是流 D 和 E 的父流,二者分别从其父流收到相同的资源配额。

流优先级仅是对服务器的建议,可以动态更改或完全忽略。在起草 HTTP/2 协议的过程中,工作组认为允许客户端强迫服务器遵守特定资源分配是不对的。相反,服务器可以自由调整优先级,使其与自己的能力匹配。

流控制

流控制,是指可以控制对端发送数据的速度使负载处于自己的处理能力范围内。在HTTP/1.1中,请求或响应数据一当开始发送,接收端只有选择全盘接收或丢弃,而在HTTP/2中,通过流控制接收方可以与发送方协商合理的发送速率,避免接收端处理不过来。


流控制管理数据的传输,使发送者不会让接收者不堪重负。它允许接收者停止或减少发送的数据量。例如,参阅一个提供点播视频的流媒体服务。观看者观看一个视频流时,服务器正在向客户端发送数据。如果视频暂停,客户端会通知服务器停止发送视频数据,以避免耗尽它的缓存。

打开一个连接后,服务器和客户端会立即交换 SETTINGS 帧来确定流控制窗口的大小。默认情况下,该大小设置为约 65 KB,但可通过发出一个 WINDOW_UPDATE 帧为流控制设置不同的大小。

HTTP/2 的普及情况

供应商几乎都采用了 HTTP/2。在浏览器领域,所有主要浏览器目前都只支持基于 TLS 的新协议。在编写本文时,全球的支持率已超过 80%。

服务器支持率也有所增加,所有主要服务器系列的当前版本都支持 HTTP/2。您的托管服务提供商很可能已支持 HTTP/2。可以在 HTTP/2 规范的 Wiki 页面上跟踪它的所有已知服务器实现。

工具支持也很丰富,所有您最喜欢的实用工具都支持 HTTP/2。Wireshark 对希望调试服务器与客户端之间的 HTTP/2 通信的开发人员最重要。

HTTP/2 与您的关系

Web 用户不关心您使用何种协议来提供内容,只要它速度够快就行。您可能已通过优化网站加载资源的方式,努力为客户提供他们想要的资源。借助 HTTP/2,您不再需要串联文件,将图标整理到一个图像中,设置大量域,或者内联资源。

简言之,HTTP/2 避免了对变通方案的需求。事实上,继续使用我在本文中介绍的性能工具,可能阻碍您的网站从 HTTP/2 性能增强中受益。

所以对大多数开发人员而言,最重要的问题是:现在是否适合针对 HTTP/2 重构我的网站?在我看来,这很大程度上取决于与应用程序组成和所使用的浏览器相关的因素。以下是一个平衡法则:您不希望不公平对待使用旧浏览器的用户,但希望提供更快的整体用户体验。

针对 HTTP/2 的优化是一个未知领域,尤其是在最佳实践方面。它不仅仅是消除变通方案并期待获得最佳成果的一种途径。我们每个人都必须亲自研究。在此过程中,我们会发现提升性能的新方法、HTTP/2 在自然环境下的运行效果,哪个服务器拥有最高性能的实现,等等。

对 Web 开发而言,HTTP/2 代表着一个美好的新世界。大胆的开发人员在接受它带来的挑战的同时也将获得收益。

参考

https://amsimple.com/blog/article/50.html
https://www.ibm.com/developerworks/cn/web/wa-http2-under-the-hood/index.html

发布了162 篇原创文章 · 获赞 131 · 访问量 83万+

猜你喜欢

转载自blog.csdn.net/u013474436/article/details/105038682
今日推荐