HTTP/2.x

HTTP/2.x

官方文档:HTTP/2.0规范文档

官网 PPT:HTTP/2 is here, let's optimize! - Velocity SC 2015 - Google 幻灯片,好多图都是从这里截取的

官方出的书:HTTP: HTTP/2 - High Performance Browser Networking (O'Reilly)

个人博客:深入理解 http2.0 协议,看这篇就够了!

关于 HTTP 协议 2.x 版本的基本概念,以及其解决的问题,请看《HTTP 协议基础》的 HTTP/2.0 小节,这里就不重复了。

HTTP/1.x 给人的感觉像是一个一开始就没怎么好好设计的玩具,HTTP/2.0 才开始像是一个经过设计的面向工业界的解决方案,在兼容 HTTP/1.x 的同时做到了性能的提升,而且从 HTTP/1.x 切换到 HTTP/2.x 的成本也很低,非常建议升级。

HTTP/2.x 目前就只有 HTTP/2.0 这一个版本,而且已经开始大规模应用了,后文的讲解主要也是集中于 HTTP/2.0

多路复用

更详细的解释,请看《HTTP 协议基础》的 HTTP/2.0 小节,这里只是留个引子。

HTTP/1.x 中,每次请求都会建立一次 HTTP 连接,也就是我们常说的 3 次握手 4 次挥手,这个过程在一次请求过程中占用了相当长的时间,即使开启了 Keep-Alive,解决了多次连接的问题,但是依然有两个效率上的问题:

HTTP/2.x 的多路复用就是为了解决上述的两个性能问题。

HTTP/2.x 中,有两个非常重要的概念,分别是帧(frame)和流(stream)。帧代表着最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流。

多路复用,就是在一个 TCP 连接中可以存在多条流。换句话说,也就是可以发送多个请求,对端可以通过帧中的标识知道属于哪个请求。通过这个技术,可以避免 HTTP 旧版本中的队头阻塞问题,极大的提高传输性能。

HTTP/2.x 性能提升的本质,就是从一开始通过多个 tcp 连接实现并发请求。到通过在单个 tcp 连接中的多个流来实现并发请求。提高了 tcp 的利用率。

启用 HTTP/2.0

启用 HTTP/2.0 的方式也很简单,以 Nginx 为例,首先 Nginx 的版本需要满足以下条件

首先需要支持配置 SSL/TSL,然后就是加上一点配置即可

listen       443 ssl http2 ;

然后重启

nginx -s stop
nginx

真的非常简单。

帧的格式

HTTP/2帧格式简介_http帧格式-CSDN博客

帧格式 - HTTP2 学习笔记

+-----------------------------------------------+
|                 Length (24)                   |
+---------------+---------------+---------------+
|   Type (8)    |   Flags (8)   |
+-+-------------+---------------+-------------------------------+
|R|                 Stream Identifier (31)                      |
+=+=============================================================+
|                   Frame Payload (0...)                      ...
+---------------------------------------------------------------+

帧首部的字段定义如下:

具体类型的帧,具体的标记,以及具体的帧负载,我们有需要再去学。

一个简单的 HTTP/2.0 的请求过程

参考博客:HTTP/2 HEADERS and DATA Frames

消息(message)这个概念,是一个或两个 HEADER 帧 (携带 HTTP 消息头)、零个或多个 DATA 帧和一个可选的终结 HEADER 帧的组合。一条消息可以代表一个 HTTP 请求或者一个 HTTP 响应,一条消息是一个 HTTP/2.0 流的一半。

一个 HTTP/2.0 流是请求消息和响应消息的组合。注意,不要将消息与 flag_end_stream 混淆,后者表示任何一端为特定流发送的最后一帧。

(假设 stream ID 为 13)一个典型的浏览器发起的 GET 请求将有:

一个典型的 GET 请求的响应将有:

注意,由于多路复用,帧可以交错在一起(interleave); 这意味着,如果您有两个并发响应 (例如流 13 和流 15),则可以使用此序列

HEADERS(13) HEADERS(15) DATA(15) DATA(13) DATA(13] DATA(15) DATA(15]

其中 ] 表示这个帧是这个流中的最后一个帧。

我们需要注意,请求和其对应的响应的帧使用的是同一个 streamID,一个请求用一个流 id,多个请求并行,就用多个流 id 进行区分即可。所以说二进制分帧和流是多路复用的基础。

HTTP/2.0 是否必须基于 HTTPS

参考博客:谈谈 HTTP/2 的协议协商机制 | JerryQu 的小站

h2c,代表 HTTP/2 ClearText,在 HTTP Upgrade 机制中会用到,而 h2,则代表基于 TLS 的 HTTP/2

HTTP/2 协议本身并没有要求它必须基于 HTTPS(TLS)部署,但是当前主流浏览器,都只支持基于 HTTPS 部署的 HTTP/2;如果你的 HTTP/2 服务要支持浏览器访问,那就必须基于 HTTPS 部署;如果只给自己客户端用,可以不部署 HTTPS(这个页面 列举了很多支持 h2c 的 HTTP/2 服务端、客户端实现)。

服务端推送

相比于 HTTP/1.1 的性能优化,请看 HTTP/2.0引入服务端推送

简单实践

这个值得研究,可以提升页面的加载速度,TODO

既然 http/2.0 支持服务端推送,那么 Websocket 和 SSE 技术还有必要存在吗?

参考博客:有了 HTTP/2,Websocket 还有市场吗?

关于 Websocket,请看《WebSocket 理论知识》等相关博客

关于 SSE,请看《SSE 简单实践》

HTTP/2.0 服务器推送让服务器能够主动推送响应到客户端缓存中。在典型的 HTTP/1.x 工作流中,浏览器请求一个页面,服务器在响应中返回一个 HTML,然后就是等待浏览器解析响应并发送额外请求来获取额外的内嵌资源(JavaScript、CSS 等)。服务器推送使服务器能够试探性地向客户端发送资源。此时,浏览器不必解析 HTML 页面并找到需要加载的其它资源;而是服务器能够立即开始发送它们。

有了这些改进和类似的能力,很自然地就会问:HTTP/2.0 是 Websocket 或者 SSE 这类推送技术的替代品吗?

答案显然是否定的,理由很简单:正如我们上面所看到的,HTTP/2.0 引入了服务器推送,让服务器能够主动地推送资源到客户端缓存。然而它并没有允许推送数据到客户端应用本身。服务器的推送只是由浏览器来处理,并不会让应用代码介入,这也就意味着应用程序无法使用 API 来获取这些事件的通知

由于 SSE 是基于 HTTP 的,其天然适配于 HTTP/2.0,这样 SSE 就可以集两者之长:HTTP/2.0 可以基于多路复用流形成一个高效传输层,同时 SSE 给应用提供了 API 使之能够进行推送。

帧优先级

因为数据流是并行的,而且相互之间不影响,因此,数据流可以有优先级,也就是说我们可以优先处理某一个数据流的请求并返回

消息头压缩 - HPACK 算法

有需要再去研究

从 HTTP/1.1 到 HTTP/2.0 性能优化方式的变化

参考内容:HTTP/2 is here, let's optimize! - Velocity SC 2015 - Google 幻灯片 第 14 张开始的内容

个人博客:从 view source 说说 http 性能优化 - 掘金

网路优化的方向

上面这张图,表示是从两个方向来降低网页的加载时间:

结论很清晰:为了加快整个互联网的速度,我们应该更多的在降低 RTT 这个方向上寻找方法。如果我们能把跨大西洋的 RTT 时间从 150 毫秒减少到 100 毫秒,这将比将用户的带宽从 3.9 Mbps 增加到 10 Mbps 甚至 1 Gbps 产生更大的影响

HTTP/2 是为了在互联网上低延迟地内容传输而设计的协议。

经典的优化 Web 应用传输速度的方式

一些在 HTTP/1.0 时代的优化措施,在 HTTP/2.0 时代会降低性能

域名分片

HTTP/1.x 以纯文本方式传输,虽然一个 TCP 连接用于多个 HTTP 请求,但是收到请求的服务器必须按照请求收到的顺序发送响应,也就是说一个 TCP 连接在同一时刻只能处理一个 http 请求,如果一个 TCP 连接种处理的多个请求种,排在前面的请求处理的很慢,那么后面的请求就得等着,这就是队头阻塞(Head-of-line blocking)

那我创建多个 TCP 连接是不是就可以了,是的,但是浏览器都有单域名的 TCP 并发量限制(Chrome 限制 6 个 TCP 并发)。

那既然是单域名限制并发量,那把资源分散在不同的域名不就行了,所以 HTTP/1.x 时代经常采用域名分片(domain shard)做性能优化。

然而不合理的域名分片,会导致一系列问题,比如:

1,每一个 TCP 连接在传输中会有头部字节开销,越多的 TCP 连接会产生大量重复数据,实际吞吐量(goodput)比率就会减少;

2,如果 TCP 连接数量超过网络负载,会造网络拥塞和 TCP 重连(重新握手增加延时)

所以说域名分片的关键在数量上的限制,否则会严重影响性能。

HTTP/2.0 时代,通过引入帧还有流的概念实现了 TCP 的多路复用,在单个 TCP 连接中就可以实现多个请求的并发,因此也就不再需要通过域名分片来绕过浏览器的 TCP 数量限制,而且,在使用 HTTP/2.x 的时候进行域名分片还会对 HTTP 的性能造成负面印象:

因此,在使用 HTTP/2.x 的时候不要使用域名分片

对需要请求多个小资源的场景的优化

小资源整合成大资源

HTTP/1.x 时代为了减少网络请求,我们会将多个对小资源的请求合并成一个对一个大的资源的请求,最典型的就是图片,通过类似雪碧图这种方式压缩合并多个小资源为一个大资源,

这样做的好处是:

然而同时也产生了新的问题,

资源内联

资源内联就是将外部资源合并在网页文件中来减少 http 请求,<style> 或是 <script> 这样文本资源可以直接内联在网页中,但非文本资源必须使用 base64 编码,base64 与原始资源相比,有字节上的增加(字节用 base64 转码后字节数增加 33%),这样就增加了额外的传输数据。

比如原本是通过 http 从服务器获取一张图片,现在可以把它转化为 base64 格式内嵌在网页中,这样一来就减少了 http 请求次数

例如:

内联前:

<img src="http://www.example.com/img/xxx.gif" alt="1x1 transparent (GIF) pixel" />

内联后

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==" alt="1x1 transparent (GIF) pixel" />

缺点同样明显:

HTTP/2.0 引入服务端推送

HTTP/2.0 中通过服务端推送解决了小资源的请求问题,当客户端请求一个网页的时候,服务端知道这个网页还依赖其他的静态资源,比如 xxx.jsxxx.cssxxx.png,此时,服务端会主动将这些网页依赖的资源主动推送给客户端,

服务端推送具有同源限制。