http

来源:互联网 发布:济南市 优化政务服务 编辑:程序博客网 时间:2024/06/08 01:27
作者:Leozhang
链接:https://www.zhihu.com/question/24774343/answer/96586977


毫无疑问,Web 性能的终极目标是减少到用户端的延迟,让用户能够尽快的打开前端网页并进行相关交互。
就 HTTP 而言,理想的协议应该看起来是这样的:
<img src="https://pic4.zhimg.com/2524d6b5a20ab03ca1b0bf2117055c47_b.png" data-rawwidth="323" data-rawheight="269" class="content_image" width="323">

而对于客户端来说,应该尽可能发送少的数据给服务器,从服务端下载尽可能少的数据,尽可能减少往返 (Round Trips) ,客户端与服务器无论是哪一边,额外的数据流都会带来额外的延迟开销,与此同时也更容易出现拥塞和丢包问题,这无疑严重影响了性能。
多余的 Round Trip 同样会增加延迟,尤其是在移动网络下(100ms 是让用户感觉到系统立即做出响应的时间上限)
那么究竟理想中的 HTTP 是什么样的,而我们又该如何去优化?



HTTP/1.1

HTTP/1.1 这个协议本身有很多的优点,不幸的是性能并不在其中
一个典型的页面请求是这样的:

<img src="https://pic2.zhimg.com/9a238eee5cef89192b6afe360166dd41_b.png" data-rawwidth="662" data-rawheight="482" class="origin_image zh-lightbox-thumb" width="662" data-original="https://pic2.zhimg.com/9a238eee5cef89192b6afe360166dd41_r.png">

显然这并不理想


HTTP/1 是 chatty 类的协议 ,因为需要不断的去向服务器请求新东西,首先是 HTML 其次是 CSS 和 JavaScript,每一次的交换都增加了额外或者是更多的 Round Trip 延迟,明显和“尽可能减少 Round Trips”的目标背道而驰。

其次,页面上的请求增加了很多的数据,违背了「应该尽可能发送少的数据给服务器」的原则,请求中增加了诸如 Referer、User-Agent 等啰嗦的首部,当然 Cookie 是每一次请求都要带上的。



最后因为 HTTP/1 的线头阻塞 (Head of line blocking),使得过去合并多个请求为一个请求成为公共实践,诸如 CSS Sprite、inline image 等其他系列的实践,看似华丽的 HTTP/1 性能优化,实则有一定的开销。
它下载的数据远远超过客户端展示页面所需要的数据,违背了理想化的原则,意味着我们无法尽可能快速的展示页面 ,好在 HTTP/1.1 也不是一无是处,在性能方面,它提供了缓存。这就使得你可以重用新鲜的缓存,而无需重新发起额外的请求,当缓存中存在不新鲜的 copy 时,能很好的避免传输大量的数据。


HTTP/2

HTTP/2 试图从以下几个方面,解决 HTTP/1.1 中的几个历史遗留问题:


多路复用 (Multiplexing)

多路复用意味着线头阻塞将不在是一个问题,允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息,合并多个请求为一个的优化将不再适用。


首部压缩 (Header Compression)

首部压缩移除了请求中的一些啰嗦首部,你可以通过很少的 IP package,承载数十个乃至上百个的请求,更符合最小数据量的理想化原则。


服务端推送(Server Push)

服务器可以向客户端推送所需要的资源,避免不必要的 Round Trips,所以一个典型的 HTTP/2 请求交互是这样的:


<img src="https://pic4.zhimg.com/7b459bdade1ff5b7eba39b58e8da0d8b_b.png" data-rawwidth="459" data-rawheight="301" class="origin_image zh-lightbox-thumb" width="459" data-original="https://pic4.zhimg.com/7b459bdade1ff5b7eba39b58e8da0d8b_r.png">这里你会发现,服务端会向客户端发送 CSS,JavaScript 以及图片文件,而无需通知客户端,服务端知道客户端可能会需要什么。所以它使用 Server Push 发送多个响应给客户端。节省了 Round Trip ,此时就变成了一个这里你会发现,服务端会向客户端发送 CSS,JavaScript 以及图片文件,而无需通知客户端,服务端知道客户端可能会需要什么。所以它使用 Server Push 发送多个响应给客户端。节省了 Round Trip ,此时就变成了一个 Less chatty的协议,同时也充分的利用了网络资源。

友情提示,并不是说这些都已经是唾手可得很容易的,关于 HTTP/2 依旧是有许多的问题,尤其是何时去 Push,我会分段在后期进行解释。



HTTP/2 缓存策略 + Cache Digests

关于 Server Push ,一个常见的问题是:

「如果客户端早已在缓存中有了一份 copy 怎么办?」

因为 Push 本身具有投机性,所以肯定会出现推送过去的东西浏览器不需要的情况。

这种情况下,HTTP/2 允许客户端通过 RESET_STREAM 主动取消 Push ,然而这样的话,原本可以用于更好方向的 Push 就白白的浪费掉数据往返的价值。


对此,一个推荐的解决方案是,客户端使用一个简洁的 Cache Digest 来告诉服务器,哪些东西已经在缓存,因此服务器也就会知道哪些是客户端所需要的。

<img src="https://pic3.zhimg.com/220e5f58c712ae45e629fe9e811257ba_b.png" data-rawwidth="694" data-rawheight="334" class="origin_image zh-lightbox-thumb" width="694" data-original="https://pic3.zhimg.com/220e5f58c712ae45e629fe9e811257ba_r.png">

因为 Cache Digest 使用的是 Golumb Compressed Sets,浏览器客户端可以通过一个连接发送少于 1K 字节的 Packets 给服务端,通知哪些是已经在缓存中存在的。

现在,我们已经解决了 chattiness、额外的 Round Trip 开销、啰嗦首部的数据浪费、inline 以及过去的其他优化行为、最后则是 Push 重复资源带来数据浪费,这和我们理想化的目标越来越近了。

Cache Digests 只是其中一个提案之一, 在 HTTP 社区有着更多其他的解决方案,我们也希望在不久的将来看到他们的身影。


TCP

至此,我还没有谈到的其他协议对浏览器加载网页时的性能影响。在 HTTP 开始之前 TCP 需要进行三次握手,来协商新连接的参数。这意味着在建立连接的时候,一个最简单的 HTTP 请求也需要 2 个 Round-Trip Time 才能完成 。

<img src="https://pic3.zhimg.com/050682be95718c8c72b2ed0b580701ba_b.png" data-rawwidth="727" data-rawheight="330" class="origin_image zh-lightbox-thumb" width="727" data-original="https://pic3.zhimg.com/050682be95718c8c72b2ed0b580701ba_r.png">

TCP Fast Open 允许应用在 TCP 握手期间(SYN 和 SYN+ACK packets)交换数据,这样可以减少一次 RTT,不幸的是目前只支持 Linux 以及 OSX,未来 TFO 在 HTTP上将会有更多巧妙的运用。


TFO 不保证与 SYN 数据包发送的数据将只出现一次;这是容易重复(由于重传),甚至被恶意的重放攻击。 因此,在一个 TFO 连接上使用 HTTP POST 并不是建立第一个请求的好主意。 并且,使用 GET 也依然会有很大的副作用,浏览器也没有好的办法来检测哪个 URL 可以做到这一点的。


TLS

在传输应用数据之前,客户端必须与服务端协商密钥、加密算法等信息,服务端还要把自己的证书发给客户端表明其身份,这些环节构成 TLS 握手过程,还没把客户端和服务端处理时间算进去。光是 TLS 握手就需要消耗两个 RTT(Round-Trip Time,往返时间)

<img src="https://pic2.zhimg.com/cfb2f2fc8f2e2251085f205eb7b6bb01_b.png" data-rawwidth="568" data-rawheight="394" class="origin_image zh-lightbox-thumb" width="568" data-original="https://pic2.zhimg.com/cfb2f2fc8f2e2251085f205eb7b6bb01_r.png">

而 Session Tickets 可以将 TLS 握手所需 RTT 减少到 1 个:


<img src="https://pic4.zhimg.com/f5ee2ff45dc42b770ede800ab893bb5b_b.png" data-rawwidth="526" data-rawheight="369" class="origin_image zh-lightbox-thumb" width="526" data-original="https://pic4.zhimg.com/f5ee2ff45dc42b770ede800ab893bb5b_r.png">

不久的未来,TLS 1.3 将会支持 Zero Round Trip 握手,HTTP 可以在第一个 Round Trip 就发送数据。

0 0