HTTP/2 in Action

《HTTP/2 in Action》by Barry Pollard 的读书笔记。

Part 1. 向 HTTP/2 靠拢

Chapter 1. 万维网与 HTTP

1.1 万维网的原理

因特网使用 IP(Internet Protocol)协议,万维网(World Wide Web)有三项核心技术,HTTP、URL、HTML。

1.2 什么是 HTTP

HTTP 代表超文本传输协议。基于可靠的网络连接,通常为 TCP/IP。是一个请求-响应协议。

1.3 HTTP 的语法和历史

  • HTTP/0.9: 没有 header、body,每个请求关闭连接,错误用 HTML 响应文本。

  • HTTP/1.0: 更多请求方法,添加版本号字段,首部,整数响应状态码

  • HTTP/1.1: 对 1.0 的标准化和完善。强制添加 Host header,持久连接(默认),管道(不流行,且支持较差),cookie。

1.4 HTTPS

HTTP 的安全版本,加密、完整性校验、身份验证。使用 TLS(Transport Layer Security)加密,TLS 的前身是 SSL(Secure Sockets layer)。

HTTPS的一个重大问题是,它只保证你正在连接到该服务器,而不能保证服务器值得信任。

1.5 查看、发送和接收 HTTP 消息的工具

浏览器开发者。

Postman,curl,wget。

Chapter 2. 通向 HTTP/2 之路

2.1 HTTP 和当前的万维网

在一个连接中,请求 1-> 响应 1 -> 请求 2 -> 响应 2 的工作方式是 HTTP 慢的根本原因,即为了等待响应会阻塞发送,在当前请求完成之前,无法发送另一个请求。

HTTP/1.1 引入管道化,连续发出多个请求,并按照顺序接收响应。但是很难实现,没有一个主流的 Web 浏览器支持管道化技术。而且存在队头(HOL)阻塞问题。

2.2 解决 HTTP/1.1 性能问题的方案

大致分为两类:

  • 使用多个 HTTP连接

  • 合并 HTTP 请求

使用多连接与管道化相比,不会有 HOL 阻塞问题。多数浏览器可以为每个域名分配 6 个连接。域名分片技术可以开启更多连接。但是也有缺点,建立连接需要时间,维护连接需要 CPU 和内存,TCP 的慢启动算法导致每个连接初始的拥塞窗口都很小。

发送更少的请求,精灵图,CSS 和 JavaScript 文件合并,最小化文件。缺点是引入了复杂度;合并文件会导致浪费,因为不是所有的网站都需要合并文件的所有内容;缓存。

2.3 HTTP/1.1 的其他问题

请求和首部是文本形式,对人类友好,但对机器不友好。

首部内容重复,尤其是 cookie。

2.5 从 HTTP/1.1 到 HTTP/2

SPDY:流多路利用,请求优先级,HTTP 首部压缩,服务器推送。二进制协议。

HTTP/2 是基于 SPDY 的标准化版本。

2.6 HTTP/2 对 Web 性能的影响

大多数网站可以带来很大性能提升。但不能解决所有的性能问题。

Chapter 3. 升级到 HTTP/2

3.1 HTTP/2 的支持

几乎每个现代浏览器都支持 HTTP/2,仅支持基于 HTTPS 的 HTTP/2 是浏览器厂商遵循的事实上的标准。但是中间代理若不支持,可能降级为 1.1。

几乎所有的服务器也都支持 HTTP/2 了,但是服务器升级比较麻烦,且升级 SSL/TLS 库更麻烦。

如果中间有一环不能支持 HTTP/2,可以降级到 1.1。

3.2 网站开启 HTTP/2 的方法

Web 服务器开启 HTTP/2。

在 Web 服务器前放置一个 HTTP/2 的反向代理服务器,将 HTTP/2 转换为 HTTP/1.1 发送给 Web 服务器。

通过 CDN 实现 HTTP/2。

3.3 常见问题

Web 服务器未启用 ALPN 支持,有些浏览器仅支持 ALPN。ALPN 是 TLS 的扩展。

Part 2. 使用 HTTP/2

Chapter 4. HTTP/2 协议基础

4.1 为什么是 HTTP/2 而不是 HTTP/1.2

因为增加了很多概念,是根本上的变化,不向前兼容。

  • 二进制的、基于数据包的协议,而 HTTP/1 是完全基于文本的。

  • 多路复用替代同步请求,每个帧分配一个流标识符表名属于哪个流。客户端发起的请求使用奇数流 ID,服务器使用偶数流 ID。ID 为 0 的流为管理连接的控制流。

  • 流有优先级。

  • 流可以流量控制。TCP 在连接层限流,HTTP/2 在流的层面限流。

  • 首部压缩,cookie、user-agent、host、accept-encoding 等基本不变。HTTP/1 仅允许压缩 body。HTTP/2 还能跨请求压缩首部。

  • 服务端推送。

4.2 如何创建一个 HTTP/2 连接

浏览器仅支持基于 HTTPS(h2) 建立 HTTP/2,服务器之间可以基于 HTTP(h2c) 的 HTTP/2。

  • 使用 HTTPS 协商,在 HTTPS 握手过程中,同时完成 HTTP/2 协商。ALPN 不会引入额外的消息往返。

  • 使用 HTTP Upgrade header,仅适用于 h2c。客户端发起升级请求,服务端响应是否支持,客户端决定是否升级。

  • 使用先验知识。

不管使用哪种方法启用HTTP/2连接,在HTTP/2连接上发送的第一个消息必须是HTTP/2连接前奏,或者说是“魔法”字符串。

4.3 HTTP/2 帧

每个HTTP/2帧由一个固定长度的头部和不定长度的负载组成。

标志位的含义和值取决于帧类型。

  • SETTING 帧:服务器和客户端必须发送的第一个帧(除了魔法字符串),只包含键值对。流 ID 为 0。标志位 0 表示发起设置,标志位 1 表示确认设置。

  • WINDOW_UPDATE 帧:用于流量控制。流量控制仅用于 DATA 帧。

  • PRIORITY 帧:优先级设定。

  • HEADERS 帧:冒号开头是严格定义的伪首部,不能自己随便添加。可以自定义非冒号开头的普通 header。有四个标志位:END_STREAM, END_HEADERS, PADDED, PRIORITY

  • DATA 帧:消息体。

  • GOAWAY 帧:错误或关闭连接。

  • CONTINUATION 帧:太大的首部需要。必须紧跟在 HEADERS 帧后面。降低了多路复用性。

  • PING 帧:计算消息往返时间。保持一个不使用的连接。

  • PUSH_PROMISE 帧:服务端通知客户端要发起推送。

  • RST_STREAM 帧:直接取消一个流。

  • ALTSVC 帧:允许服务端宣告获取资源时可用的其他服务。

  • ORIGIN 帧:服务器使用它来宣告自己可以处理哪些源(比如域名)的请求。

  • CACHE_DIGEST 帧:客户端可以使用这个帧来表明自己缓存了哪些资源。

Chapter 5. 实现 HTTP/2 推送

5.1 什么是 HTTP/2 服务端推送

HTTP/2服务端推送(以下称为HTTP/2推送)允许服务器发回客户端未请求的额外资源。

若服务端知道此页面需要哪些关键资源,不需要等客户端请求而是直接推送,则能减少往返次数。

需要注意的一个关键点是,仅在响应初始请求时才会发送推送的资源。HTTP/2中不是真正的双向流。推送资源是为了响应初始请求而做出的额外响应。完成该初始请求后,流会被关闭,除非客户端发起其他请求,否则不能推送其他资源。

5.2 如何推送

  • 使用HTTP link首部通知Web服务器推送资源。

可以从下游系统推送:

可以更早推送:

不需要在整个链路中都支持推送。

5.3 HTTP/2 推送在浏览器中如何运作

资源不是被直接推到网页中,而是被推到缓存中。

HTTP/2推送缓存被绑定到连接,而不是页面。当资源从连接的推送缓存中被“认领”并拿出后,就不能再从推送缓存中使用它了。推送缓存还有一点和HTTP缓存不同,那就是不缓存的资源(在HTTP cache-control首部中设置了no-cache和no-store)也可以被推送,并可以从推送缓存中读取它们。

客户端可以通过发送RST_STREAM帧来拒绝推送资源。

5.4 如何实现条件推送

  • 服务器可以记录它在一个客户端的连接上推送过哪些资源。

  • 客户端发送一个if-modified-since或者etag首部。

  • 在客户端记录哪些资源已经被推送。cookie是做这个的理想载体。

  • 缓存摘要是一个提议,浏览器用它来告诉服务器缓存里有什么内容。

5.5 推送什么

推送需要的最少资源,来“填充空闲的网络时间,不要做多余的操作。

5.6 HTTP/2 推送的常见问题

很多原因可能导致没有 HTTP/2 推送。

5.7 HTTP/2 推送对性能的影响

高效使用HTTP/2推送的关键是利用连接未被使用时的空闲带宽。

5.8 对比推送和预加载

预加载没有HTTP/2推送那么快,但是它是一个浏览器发起的请求,有一些优势:

  • 浏览器知道缓存里有什么,然后它就知道是否要发出对应的请求。

  • 没有那么多复杂性。

  • 可以从其它域名加载资源。

5.9 HTTP/2 推送的其它应用场景

Chapter 6. HTTP/2 优化

6.1 HTTP/2 对 Web 开发者的影响

HTTP/2在设计上是向后兼容的。

6.2 一些 HTTP/1.1 中的优化方法是否成了反模式

压缩大文件比压缩小文件效果好。总的来说,HTTP/2可能不需要像HTTP/1.1那样有那么多的资源合并,但是在拆分文件之前要考虑一下带来的压缩率下降问题。

在HTTP/2的世界里,域名分片的意义没有那么大,并且设置和管理这些额外的基础设施也是一种负担。

HTTP/2推送要消除对内联资源的需求。

6.3 在 HTTP/2 下依然有效的性能优化技术

不管HTTP/2提供了哪些方法来优化请求和响应,少发送数据总是好的。比如使用合适的文件格式和大小,压缩文本数据。

使用缓存防止重复发送数据。

Service Worker可以大幅减少网络加载。

6.4 同时对 HTTP/1.1 和 HTTP/2 做优化

连接合并。HTTP/2规范允许多个域名使用同一个HTTP/2连接,这些域名被解析到同一个IP地址。

Part 3. HTTP/2 进阶

Chapter 7. 高级 HTTP/2 概念

7.1 流状态

流不会被重用。流是一个虚拟的概念,它是在每个帧上标示的一个数字,也就是流ID。

7.2 流量控制

在HTTP/1.1下流量控制不是必需的,因为不管何时最多只会有一个消息在传输。因此,可以使用连接层的TCP流量控制。

在HTTP/2下,使用由多个流组成的多路复用的连接,所以只使用连接层的流量控制就不够了。不仅需要连接层的控制,还需要流层级的控制。

7.3 流优先级

优先级是一种建议或提示,完全由响应方决定要不要忽略优先级。

有两种方式设置优先级:

  • 流依赖

  • 流权重

使用流优先级的目的是尽量高效利用连接,而不是作为一种阻塞机制。

新增的流还可以是排他的。

为了使优先级模型更简单,一些客户端会使用PRIORITY帧预设置一些假流,并为它们设置对应的优先级,然后将请求挂在这些流下面。在HTTP/2标准制定的后期才添加了支持假PRIORITY帧的概念,它是更灵活的方案,支持更轻量的优先级模型。这些假流仅用于优先级排序,永远不会被用来直接发送请求。

规定一定要支持流优先级策略,很多客户端和服务端的实现都选择不实现优先级策略。

7.4 HTTP/2 一致性测试

H2spec是一个HTTP/2一致性测试工具,它发送不同的消息到HTTP/2服务器,并检查服务器是否正确遵守规范。

很多HTTP/2的实现不严格遵守规范。

Chapter 8. HPACK 首部压缩

8.1 为什么需要首部压缩

首部重复很多。有些首部很长,如 cookie。

8.2 压缩的运作方式

有损压缩常用于媒体。

无损压缩常见三种方法:

  • 查表法。

  • 更高效的编码技术:Huffman编码是更进一步的可变长度编码。它的工作原理是根据每个值的使用频率为其分配一个唯一代码,并且保证没有一个代码是其他代码的前缀。

  • lookback(反查)压缩。

8.3 HTTP 正文压缩

浏览器通过accept-encodingHTTP首部告诉服务器它所支持的压缩算法。HTTP正文压缩通常用于文本数据,gzip。

8.4 HTTP/2 的 HPACK 首部压缩

HPACK(不是简写),它基于查询表和Huffman编码,但(关键)不是基于反查的压缩方法。

HPACK格式被有意保持简单且死板。这两个特征都会减少因为实现错误所带来的互操作风险,或者安全问题。没有定义扩展机制,如要改变当前格式,只能使用一个完整的替代品。

HPACK有一个静态表,包含61个常见的HTTP首部名称(有些还有值)。对于表中首部名/首部值组合(比如:method: GET)这种条目,可以只使用其中的首部名部分(如:method)。

除了静态表以外,HPACK还有一个连接级的动态表,从位置62开始(跟在静态表之后),在会话过程中创建。

没有引用索引表的首部值可以使用ASCII编码或者Huffman编码来传输。

Huffman编码通常占用更少的空间。

8.5 HPACK 压缩实例

8.6 客户端和服务端对 HPACK 的实现

在HPACK中可以使用多种方法来发送HTTP首部,浏览器可能使用不同的方式来编码HTTP首部。

8.7 HPACK 的价值

Part 4. HTTP 的未来

Chapter 9. TCP、QUIC 和 HTTP/3

9.1 TCP 的低效因素,以及 HTTP

TCP 连接和 HTTPS 的建立导致延时。

TCP 慢启动算法,初始的窗口很小。

实际上,TCP的拥塞避免阶段的增长更慢。6个连接的话,每个连接都要经历这个过程。

TCP比较小心谨慎,在闲置一段时间后,网络情况可能发生变化,所以TCP将拥塞窗口大小降低,重新进行慢启动流程。

TCP还把丢包当成极端事件。它认为这个事件是由容量限制造成的,因而它会做出激烈反应,直接将拥塞窗口大小减半。认为丢包完全是由拥堵造成的,然后大幅降低网速,这是不正确的。新的TCP拥塞控制算法对网络丢包的处理不像之前的示例中那么极端,它们可能将拥塞窗口调小一些(不到一半),在丢包之后让其快速增长(类似慢启动),或者使用其他指标来决定要不要调整容量(比如把平均的往返时间当作更好的指标)。

认为丢包完全是由拥堵造成的,然后大幅降低网速,这是不正确的。新的TCP拥塞控制算法对网络丢包的处理不像之前的示例中那么极端,它们可能将拥塞窗口调小一些(不到一半),在丢包之后让其快速增长(类似慢启动),或者使用其他指标来决定要不要调整容量(比如把平均的往返时间当作更好的指标)。

HTTP/2在HTTP层解决了队头阻塞(HOL)的问题,因为有多路复用,单个响应的延迟不会影响其他资源使用当前的HTTP连接。但是,在TCP层队头阻塞依然存在。一个流的丢包会直接影响到其他所有的流,尽管它们可能不需要排队。在严重的丢包情况下,HTTP/2的性能会更糟。

TCP有了很多优化和创新。然而不幸的是,TCP通常由底层操作系统控制,我们在操作系统之外很少有机会更改它。所以,最佳使用TCP的方法就是使用最新版本的操作系统。

CP可以使用SACK(Selective Acknowledgment,选择性响应)响应乱序的数据包,以避免丢包时重传。

可以配置禁止重启慢启动。

TFO(TCP Fast Open,TCP快速打开)允许使用TCP三次握手的初始SYN部分发送初始数据包。出于安全的原因,这个数据包只能在TCP重连时使用,而不能在初次连接时使用。

TFO带来的提升真的很显著。Google曾经表示:“通过对网络流量的分析和网络仿真,我们得出TFO能够将HTTP网络延迟降低15%,网页整页加载时间平均降低10%,有时候能降低40%。

使用拥塞控制算法,PRR和BBR。BBR(Bottleneck Bandwidth and Round-trip propagation time,瓶颈带宽和往返时间),已经有数据表明它可以大幅度提升性能[17],特别是对于HTTP/2连接。

9.2 QUIC

QUIC(发音同quick)是Google(又是Google)发明的一个基于UDP的协议。

FEC(Forward Error Correction,前向纠错)试图通过在邻近的数据包中添加一个QUIC数据包的部分数据来减少数据包重传的需求。这个想法是,如果只丢了一个数据包,那应该可以从成功传送的数据包中重新组合出该数据包。这个方法可以类比于“网络世界的RAID 5。

连接迁移旨在减少连接创建的开销,它通过支持连接在网络之间迁移来实现。

多路径的技术同时使用Wi-Fi和移动网络进行一次QUIC连接。

QUIC取代了TCP提供的大多数功能(创建连接、可靠性和拥塞控制部分),取代了HTTPS的全部(降低了创建延迟),甚至还有HTTP/2的一部分(流量控制和首部压缩)功能。

QUIC不会替代HTTP/2,但它会接管传输层的一些工作,在上层运行较轻的HTTP/2实现。

UDP是一种基础协议,也在内核中实现。在它之上的任何东西都需要在应用层中构建,也就是所说的用户空间。

UDP并非没有问题。它是一种常见的协议,但不像TCP使用那么普遍。UDP的另一个问题是,用户空间并不总是与高度优化的内核空间一样高效。

QUIC旨在成为一种通用的协议,HTTP只是它的一种用途。虽然HTTP目前是QUIC的主要用例,也是工作组目前正在关注的焦点,但该协议的设计考虑了潜在的其他应用场景。

HTTPS内置于QUIC中,与HTTP/2不同,QUIC不能用于未加密的HTTP连接。

Chapter 10. HTTP 将何去何从

10.1 关于 HTTP/2 的争议,以及它没有解决的问题

HTTP/2的隐私问题饱受争议。

10.2 HTTP/2 的实际应用

10.3 HTTP/2 的未来版本,HTTP/3 或者 HTTP/4 会带来什么

10.4 将 HTTP 当做一个更通用的传输协议

基于HTTPS的DNS(DoH)。

Last updated