msforest / notebook

好记性不如烂笔头,记录知识的点点滴滴。
https://github.com/msforest/notebook/wiki
0 stars 0 forks source link

http/2 - High Performance Browser Networking #33

Open msforest opened 5 years ago

msforest commented 5 years ago

http/2

翻译自:http2

http2使web应用程序变得更快、更轻量、更强壮。更好的是,它还为优化我们的应用程序和提高性能开辟了许多全新的机会!

http/2主要目标:为了减少延迟,开启multiplexing;使用头部压缩,优化传输协议的负载量;支持请求优先级和服务器推送功能。为了实现这些功能,加入了许多其他的增强协议,如new flow control, error handling, and upgrade mechanisms;最关键的一点还是我们在实践中如何理解和使用这些功能。

http/2不改变原有的http语义,所有核心概念都保留,像method, status codes, URIs and header。http/2仅仅修改数据在C/S之间如何转换和传输,管理中间的整个过程,使用一个新的层来隐藏所有的复杂度。这样,对于现有的应用程序就不要任何修改,对于使用者来说,这是非常棒的好消息。

我们不应该只是对提供有效的应用程序感兴趣;我们的目标是提供最佳性能!http2开发了大量的优化功能,我们的工作就是充分利用好这些。

why not http/1.2?

为了实现HTTP工作组设置的性能目标,HTTP/2引入了一个新的二进制成帧层,它与以前的HTTP/1.x服务器和客户端不兼容, 因此主要协议版本增加到HTTP/2

也就是说,除非您通过使用原始TCP套接字实现Web服务器(或自定义客户端),否则您将看不到任何区别:所有新的低级框架都由客户端和服务器代表您执行. 唯一可观察到的差异将是提高性能和新功能的可用性,如请求优先级,流量控制和服务器推送.

Brief History of SPDY and HTTP/2

spdy是一个试验性协议,由Google开发并于2009年中发布,它的目的是为了减少web加载时延,实现目标大纲如下:

Design and Technical Goals

HTTP/0.9是一个引导万维网的单线协议; HTTP/1.0记录了信息标准中HTTP/0.9的流行扩展; HTTP/1.1引入了官方的IETF标准;因此,HTTP/0.9-1.x完全按照它的目的提供:HTTP是互联网上最普遍和广泛采用的应用程序协议之一。

不幸的是,简单的实现是以应用程序性能为代价的:http/1.x使用多个连接来实现并发和减少时延;而且,请求响应信息头在多次传输过程中是重复发送;也没有高效的优先级策略等,所以就导致了tcp连接性能低下。

对于简单的应用程序,那些缺陷还不足以影响使用;但随着应用程序的扩大、复杂度的提高,使用http/1.x就暴露了一些问题。这也是http/2设计的意图:

HTTP/2 enables a more efficient use of network resources and a reduced perception of latency by introducing header field compression and allowing multiple concurrent exchanges on the same connection… Specifically, it allows interleaving of request and response messages on the same connection and uses an efficient coding for HTTP header fields. It also allows prioritization of requests, letting more important requests complete more quickly, further improving performance.

The resulting protocol is more friendly to the network, because fewer TCP connections can be used in comparison to HTTP/1.x. This means less competition with other flows, and longer-lived connections, which in turn leads to better utilization of available network capacity. Finally, HTTP/2 also enables more efficient processing of messages through use of binary message framing.

大体意思是http/2采用单个连接通道下使用头部压缩、多路复用、异步发送request/response、允许设置优先级等功能,使用二进制消息帧高效处理message。这一过程,减少了tcp连接数,二进制传输等解决了http/1.x存在的一些缺陷。

http/2没有替代http/1.x,同样的语义、同样的功能、同样的概念;即API用法不变,重要的是理解底层的一些变化是如何提升性能的。

先了解一下二进制帧层。

Binary Framing Layer

HTTP/2的所有性能增强的核心是引入了新的二进制成帧层,下图显示了message是如何在C/S之间进行包装和传输的。 image

选择在套接字接口和HTTP的API之间引入新的优化编码机制,是为了保持原有的使用不变,如:http1/x词法、方法、头信息毫无变化,变化的是以一种被转码的方式进行传输。所有http/2交互信息被拆分成被二进制格式转码的message和frame,而http/1.x是以换行符分隔的明文传输。

所以,C/S之间必须使用新的二进制转码机制来解析传输数据,庆幸的是,C/S已经帮我们做好了这些工作

二进制协议的优缺点

ASCII协议容易使用和检测。但是正确的解析实现是很难的、效率低下:因为可选的空格,不同的终止序列和其他怪癖使得很难区分有效负载的协议,然后就导致安全解析问题。相反,使用二进制协议会带来更高性能、更强大和正确的实现。

Streams, Messages, and Frames

新的二进制帧机制改变了C/S之间数据交换方式。要描述这个过程,让我们熟悉几个关键术语:

Stream
建立连接内的双向字节流,可以携带一个或多个消息

Message
映射到逻辑请求或响应消息的完整帧序列

Frame
http/2最小的通信单位,每个单位包含一个帧头,它至少有一个所属的Stream ID

1. 所有通信都通过单个TCP连接执行,该连接可以承载任意数量的双向流(Stream)。
2. 每个流都有唯一的标识符和可选的优先级信息,用于携带双向消息(Message)
3. 每条message都是一条逻辑HTTP消息,例如请求或响应,它由一个或多个帧组成
4. 帧是承载特定类型数据的最小通信单元,例如HTTP标头,消息有效负载等。来自不同流的帧可以被交错发送,然后通过每帧的流标识符重新组装。

image

简而言之,HTTP/2将HTTP协议通信分解为可交换的二进制帧,然后将其映射到特定流的消息中,并且所有这些帧都在单个TCP连接中复用。这是支持HTTP/2协议提供的所有其他功能和性能优化的基础。

Request and Response Multiplexing

使用HTTP/1.x,如果客户端想要进行多个并行请求以提高性能,则必须使用多个TCP连接;此行为是HTTP/1.x传递模型的直接结果,它确保每个连接一次只能传递一个响应(响应排队)。这就导致线头阻塞和底层TCP连接的低效使用。

HTTP/2中的新二进制成帧层消除了这些限制,并允许客户端和服务器将HTTP消息分解为独立的帧,交错传输,然后重新组合它们,从而实现完整的请求和响应多路复用。 image

图显示了同一个连接中正在传输的多个流:客户端正在向服务器发送DATA帧(流5),而服务器正在把流1和3以乱序的帧序列向客户端发送。因此,有三个并行流在传输。

将HTTP消息分解为独立的帧,交错发送,然后在另一端重新组装是HTTP/2最重要的增强功能。这种机制在所有Web技术的整个堆栈中引入了众多性能优势的连锁反应,使我们能够:

HTTP/2中的新二进制成帧层解决了HTTP/1.x中发现的行头阻塞问题,并消除了因并发请求打开多个连接的需要。因此,这使我们的应用程序更快,更简单,部署成本更低。

Stream Prioritization

一旦HTTP消息可以被分成许多单独的帧,并且我们允许来自不同流的帧被多路复用,那么客户端和服务器识别帧的顺序成为关键的性能考虑因素。为了实现这一点,HTTP/2标准允许每个流具有相关的权重和依赖关系:

流依赖性和权重的组合允许客户端构造和传递“prioritization tree”,该树表示它希望如何接收响应。反过来,服务器可以使用此信息控制cpu,内存和其他资源的分配来确定处理流的优先级,而且一旦响应数据可用,就分配带宽确保向客户端最佳响应。 image

HTTP/2通过引用另一个流的唯一标识符作为其父级来声明流依赖性;如果省略,则称该流依赖于“root stream”。流依赖性表示,应该在其依赖性之前为父流分配资源, 例如,响应C之前需先处理并传递响应D.

共享相同父级(即兄弟级流)的流应按其权重的比例分配资源。例如,如果流A的权重为12,而其中一个兄弟B的权重为4,那么要确定每个流应该接收的资源的比例:

  1. 权重和:4+12=16
  2. 每个流的权重除以总权重:A=12/16, B=4/16

因此,A分配3/4,B分配1/4的可用资源;现在我们来依次分析上图中例子,从左到右:

  1. A和B没有明确的依赖关系,即它们被依赖一个隐式“root stream”;A的权重是12,B的权重是4,因此,B应该被分配A资源的1/3。
  2. D依赖于“root stream”, C依赖于D;因此,D应该在C之前接收完整的资源分配。权重是无关紧要的,因为依赖性的优先级高于权重。
  3. D应该在C之前获得全部资源分配; C应该在A和B之前获得全部资源分配;流B应该接收分配给流A的资源的三分之一
  4. D应该在E和C之前获得全部资源分配; E和C应在A和B之前获得相等的分配; A和B应根据其权重获得比例分配。

如上面的示例所示,流依赖性和权重的组合为资源优先级提供了一种策略,这是用于改进浏览器性能的关键特征,其中不同的资源类型有不同的依赖性和权重。更好的是,HTTP/2协议还允许客户端在任何时候更新这些首选项,这样可以在浏览器中进一步优化。例如,我们可以改变依赖性并重新分配权重以响应用户交互和其他信号。

PS: 流依赖性和权重表示传输偏好,而不是必须存在的一种策略,因此不保证特定的处理或传输顺序。也就是说,客户端不能强制服务器使用流优先级按特定顺序处理流。虽然这看似违反直觉,但事实上它是理想的行为:如果阻止了更高优先级的资源,我们不希望阻止服务器在较低优先级资源上取得进展。

Browser Request Prioritization and HTTP/2

在浏览器中呈现页面时,并非所有资源都具有相同的优先级: HTML文档对于构建DOM至关重要;CSS是构建CSSOM所必需的;而JavaScript资源上是可以阻止DOM和CSSOM构造;并且通常以较低优先级获取诸如图像之类的剩余资源。

为了加快页面的加载时间,所有现代浏览器根据资源类型、页面上的位置,甚至先前访问了解的优先级来确定请求的优先级;例如,如果在先前访问中某个资源上的渲染被阻止,那么同一资源在未来可能会被优先考虑更高。

使用HTTP/1.x,浏览器对于有优先级数据的能力有限:协议不支持多路复用,并且无法将请求优先级传递给服务器。相反,它必须依赖于并行连接的使用,这使得每个源最多可以有六个请求的有限并行性。因此,请求在客户端上排队,直到连接可用,这会增加不必要的网络延迟。HTTP Pipelining解决了客户端部分并发请求的问题,没有解决服务器并发响应的问题。

HTTP/2解决了这些低效问题:消除了请求排队和线头阻塞,因为浏览器可以在发现所有请求时调度它们,并且浏览器可以通过流依赖性和权重来传达其流优先级首选项,从而允许服务器进一步优化响应交付。

One Connection Per Origin

使用新的二进制帧层机制,HTTP/2不再需要多个TCP连接来并行多路复用流;每个流被分成许多帧,可以乱序发送和设置优先级。因此,所有HTTP/2连接都是持久的,并且每个源只需要一个连接,这提供了许多性能优势。

对于SPDY和HTTP/2,它们的杀手级特性是在单拥塞控制信道上的任意多路复用。

大多数HTTP传输都是短而突发的,而TCP则针对长期的批量数据传输进行了优化。通过重用相同的连接,HTTP/2能够更有效地使用每个TCP连接,并且还可以显着降低整体协议开销。此外,使用更少的连接减少了完整连接路径(即客户端,中介服务器和源服务器)的内存和处理占用空间,这降低了总体操作成本并提高了网络利用率和容量。因此,迁移到HTTP/2不仅应该减少网络延迟,还应该有助于提高吞吐量并降低运营成本。

PS: 减少连接数是提高HTTPS部署性能的一项特别重要的功能:这可以转化为更少的昂贵TLS握手,更好的会话重用以及所需客户端和服务器资源的整体减少。

Packet Loss, High-RTT Links, and HTTP/2 Performance

等等,我们列出了同一个域名使用一个TCP连接的好处,难道就没有一点点缺点吗?是的,人无完人 :scream:

此列表中的每个清单都可能会对HTTP / 2连接的吞吐量和延迟性能产生负面影响。然而,尽管有这些限制,转向多个连接将导致其自身的性能权衡:

上述优点和缺点不全面,然而在构建特定场景,其中一个或多个连接可能被证明是有益的。但是,在部署HTTP/2的实验证据表明,单个连接是首选的部署策略:

到目前为止,在测试中,压缩和优先级的好处超过了线头阻塞的负面影响(特别是在存在丢包的情况下)。-- Hypertext Transfer Protocol version 2, Draft 2

与所有性能优化过程一样,当您消除一个性能瓶颈时,您将解锁下一个性能瓶颈。在HTTP/2的情况下,TCP可能就是这样。这就是为什么服务器上经过良好调整的TCP堆栈再次成为HTTP/2的关键优化标准的原因。

目前正在进行研究以解决这些问题并提高TCP性能:TCP快速开放,增加初始拥塞窗口等等。话虽如此,重要的是要承认HTTP/2与其前身一样,并不强制要求使用TCP。其他传输,例如UDP,在我们展望未来时并不属于可能性范围。

Flow Control

流量控制是一种机制,可以防止发送方丢弃或不处理来自接收方的数据:接收方可能忙,负载过重,或者可能只愿意为特定的资源分配固定数量的资源流。例如,客户端可能已经请求具有高优先级的大视频流,但是用户已暂停视频,并且客户端现在想要暂停或限制其从服务器的传递以避免获取和缓冲不必要的数据。或者,代理服务器可以具有快速下游和慢速上游连接,并且类似地希望调节下游传送数据的速度以匹配上游速度以控制其资源使用;等等。

上述要求是否想起TCP流量控制?参见流量控制 但是,由于HTTP/2流在单个TCP连接中进行多路复用,因此TCP流控制不够精细,并且不提供必要的应用程序级API来管理各个流的传送。为了解决这个问题,HTTP/2提供了一组简单的构建块,允许客户端和服务器实现自己的流和连接级流控制:

HTTP/2没有指定实现流控制的任何特定算法。相反,它提供了简单的构建块,并将实现推迟到客户端和服务器,客户端和服务器可以使用它来实现自定义策略来规范资源使用和分配,以及实现可以帮助提高实际和感知性能的新交付功能。

例如,应用层流量控制允许浏览器仅获取特定资源的一部分,持续获取直到stream减少到零,然后稍后恢复;例如,获取预览或首先扫描图像,显示它并允许其他高优先级提取继续进行,并在更多关键资源完成加载后恢复提取。

Server Push

HTTP/2另一个强大的特性是服务器能够为单个客户端请求发送多个响应。也就是说,除了对原始请求的响应之外,服务器还可以将其他资源推送到客户端,而客户端不必明确请求每个资源! image

PS: HTTP/2摆脱了请求/响应语义的限制,并实现了一对多和服务器启动的推送工作流程,从而在浏览器内外打开了一个新的交互可能性世界。这是一项启用功能,对于我们如何考虑协议以及在何处以及如何使用协议将产生重要的长期影响。

为什么我们需要在浏览器中使用这样的机制?因为Web应用程序由许多资源组成,所有这些资源都是客户端通过检查服务器提供的文档来发现的。因此,为什么不消除额外的延迟并让服务器提前推送相关资源?服务器已经知道客户端需要哪些资源;这是服务器推送。

实际上,如果您曾经通过URI获取CSS,JavaScript或任何其他文件,那么您已经拥有了服务器推送的实践经验!通过手动将资源内联到文档中,我们实际上是将该资源推送到客户端,而无需等待客户端请求它。使用HTTP/2,我们可以获得相同的结果,但具有额外的性能优势:

每个推送资源都是一个流,与内联资源不同,它允许客户端对其进行单独多路复用,优先级排序和处理。浏览器强制执行的唯一安全限制是推送的资源必须遵循同源策略:服务器必须对提供的内容具有权威性。

PUSH_PROMISE 101

Header Compression

每个HTTP传输都带有一组标头,用于描述传输的资源及其属性。在HTTP/1.x中,此元数据始终以纯文本形式发送,并且每次传输增加500-800字节的开销,如果使用HTTP cookie,有时会增加千字节数; 为了减少这种开销并提高性能,HTTP/2使用HPACK压缩格式压缩请求和响应头元数据,该格式使用两种简单但功能强大的技术:

  1. 它允许通过静态霍夫曼编码对发送的报头字段进行编码,这减少了它们各自的传输大小。
  2. 它要求客户端和服务器都维护和更新先前看到的头字段的索引列表(即,建立共享压缩上下文),然后将其用作有效编码先前发送的值的参考。

霍夫曼编码允许在传输时压缩各个值,并且先前传输的值的索引列表允许我们通过传输索引值来编码重复值(图12-6),该索引值可用于有效地查找和重建完整的头部数据。 image

为进一步优化,HPACK压缩由静态和动态表组成的上下文:静态表由rfc文档定义,提供了所有可能使用的常见HTTP头字段的列表; 动态表最初为空,由特定连接中的交换值进行更新。结果,通过对先前未见过的值使用静态霍夫曼编码,并且对已经存在于每一侧的静态或动态表中的值的索引的替换,减少了每个请求的大小。

PS: HTTP/2中的请求和响应头字段的定义保持不变,但有一些小的例外:所有标题字段名称都是小写的,请求行现在拆分为单独的:method,:scheme,:authority和:path伪标题字段。

Security and Performance of HPACK

HTTP/2和SPDY的早期版本使用带有自定义字典的zlib来压缩所有HTTP标头,从而使传输的标头数据大小减少85%-88%,并显着改善页面加载时间延迟。

然而,在2012年夏天,针对TLS和SPDY压缩算法发布了“CRIME”安全攻击,这可能导致会话劫持。因此,zlib压缩算法被HPACK取代,HPACK专门设计用于:解决发现的安全问题,高效且易于正确实现,当然,还可以实现HTTP头元数据的良好压缩。

有关HPACK压缩算法的完整详细信息,请参阅https://tools.ietf.org/html/draft-ietf-httpbis-header-compression

PS 如果你正在阅读,并发现错误的地方,请不要吝啬你的建议。谢谢