Day21 X Upgrade Your HTTP Connection

其实很多的效能优化技巧都拥有「用了不一定会让效能变得比较快,就算有可能也是使用者难以感知的微幅进步,甚至如果使用不当会让网页效能变的更差」的特性。

不过对於 HTTP 这个应用层协议的优化建议却是

尽可能将 HTTP 升级,因为效能真的会差很多。

传说中使用 HTTP/2 的网站效能会比 HTTP/1.1 提升非常多,今天的目标就是理解 HTTP/1.1 的限制与 HTTP/2 甚至 HTTP/3 的可以带来的改变。虽然严格来说要做到升级得透过後端的帮忙,但因为前端开发者其实会第一线接触 HTTP 的协定,HTTP 的升级也可能让一些传统的效能优化方式变的过时且失去意义,因此我还是把这个主题拉到前端优化系列文当中分享,毕竟身为一个专业的前端开发者,这些 Networking 的概念还是很重要的!

HTTP/1.1 的限制

这边先假设读者都对 TCP 协定建立连线时的三次握手与断开连线时的四次挥手都有都有基本的了解了,如果不懂的读者建议先看这篇文章

至於 HTTP/1.0 因为已经太过古老我就不再多赘述,因此会直接从现在仍然有许多网站在使用的 HTTP/1.1 开始看起。

限制ㄧ:head-of-line blocking

HTTP/1.1 只能透过串行的方式处理请求,代表每个请求必须等排在前面的请求处理完後才能发送出去。

为了提升 HTTP 1.X 的效能,曾经出现一个叫做 HTTP pipelining 的机制,允许一次发送「一组」请求,而不用像原本一样等待前一个请求回传後才能开始处理下一个请求,不过这个 HTTP pipelining 的机制,只能按照发送的顺序来接收 response,例如说用 pipelining 的技术发出两个请求,第二个请求很快只需要 300ms,第一个请求却需要花费到 5 秒,因为要按照发送的顺序来回传,第二个请求就会被第一个耗时的请求给 block,就算处理完了也不能先回传,这就是所谓的 head-of-line blocking (HOL)。除了 HOL 问题外,HTTP pipelining 机制还有一些限制例如只有 GET 请求与 HEAD 请求才能使用,又或是 proxy server 的支援度并不高...等等,导致 HTTP pipelining 机制可以说是失败的,目前各家浏览器预设也是关闭 HTTP piplining 机制。

限制二:浏览器的连线限制

因为前面提及的串行的限制,有人可能会想说那就多开几个连线就好,但是浏览器通常会设定对一个域名同时只能有一定数量的 TCP 连线,例如 Google Chrome 对一个域名同时间请求数量的限制就是 6 个,总连线数的限制是 10 个,当超过浏览器限制的数量时,请求就会被浏览器放到 queue 里面排队。在浏览器 Devtool 的 network waterfall 栏位可以看到每个请求经过 queueing 的时间。

虽然某种程度上它算是达到平行处理请求,但其他请求仍然受到了 HOL 的限制,必须等到这些请求被处理完才能离开 waiting queue 进而被处理。

限制三:header 无法压缩

在 HTTP 1.x 中,request 与 response 的 header 是没办法经过压缩的。不要认为只需要压缩 request response body 就够了,request header 的大小也有可能来到 2, 3 KB,而且许多 request 的 header 内容其实是重复的,没有压缩的状况下来回传输也会造成效能的耗损或延迟。

由这些限制所衍伸出的效能优化技巧

因为以上说明的种种限制,衍伸出了一个效能优化的法则:「尽量减少 HTTP 请求的数量。」

这边指的减少请求数量并不是指尽量避免发出不必要的请求例如 debounce 或 throttle (後面篇章会介绍)的类型,而是指像在系列文前段提过的 CSS Sprites 或是 bundling 等将一些必要资源从原本应该要分段载入的方式聚集成较少量请求的优化方式。

另外还有一种优化技巧是 Domain Sharding,为了避免刚刚提及浏览器对於相同 domain 请求的数量限制,而故意将要请求的资源分散到不同 domain 上的技巧,这样各位读者是不是更了解这些效能优化技术存在的原因了呢?

HTTP/2

其实现在 HTTP/2(简称 H2)的使用率已经非常非常高了,一些常用的网站都已经在使用 H2。

H2 相比於 HTTP 1.x,最大的提升就在於「效能」,够直接暴力了吧 ?

H2 解决了在 HTTP 1.x 会遇到的两大问题:

  • pipelining 的 head-of-line blocking
  • header 无法压缩

很多人会误以为 H2 可以解决 TCP level head-of-line blocking (HOL) 的问题,但这边大家得记住

H2 没有解决 TCP HOL 问题!
H2 没有解决 TCP HOL 问题!
H2 没有解决 TCP HOL 问题!

而是只解决了 HTTP pipelining 的 HOL 问题。

H2 可以压缩 header

我们知道在 HTTP 1.1 虽然可以用 gzip, brotil 等压缩方式,但只能针对「body」做压缩,header 并不能压缩,这个问题终於在 H2 被解决。

Multiplexing

HTTP piplining 是一个为了解决 HTTP/1.1 只能串行处理请求而诞生的机制,虽然它可以同时发出多个请求,却因为必须按照请求的顺序回传,导致仍然会有阻塞的状况而宣告失败,而 H2 的 Multiplexing 则可以解决 HTTP piplining 阻塞的问题。

在 H2 中新增了两个观念:stream 与 frame。

在谈这两个观念以前,得先讲讲 H2 对封包结构的调整,这也是 H2 性能可以提升的核心原因。

在 HTTP/1.x 中是使用文本格式,而 HTTP/2 则将所有传输的讯息分割为更小的消息和帧,并采用二进位格式进行编码,同时也将封包结构重新定义成 frame 这个概念。这个新的机制改变了 client 与 server 之间交换数据的方式。

Stream 跟大家脑中想的应该差不多,有点类似一个串流管道的概念,frame 则是在里面流动传输的单位。每一个 frame 都会有一个识别码来分别是来自哪一个 stream。

重点就在於这个 stream 的识别码,有了这个识别码,就算回应顺序跟请求顺序不一样,仍然可以根据 frame 的 stream 识别码,重新组合出请求的资料,这就解决了 HTTP Piplining 请求会因为顺序被阻塞的问题。

有了这个机制,H2 可以达到以下这些优点:

  • 并行发送多个请求,且请求之间互相不影响。
  • 并行发送多个回应(Response),且回应之间互相不影响。
  • 使用单一 connection 并行发送多个 request 与 response
  • 提高网路频宽的使用效率

从多个图片请求来看更可以看出 H2 带来的效能提升

有兴趣的读者可以到这个网站玩玩。

Server Push

以往我们熟悉的请求资源的方式应该是由 client 端发送请求,要几个资源就发几个,server 端收到请求後再回传对应的资源。

但这样其实有点没有效率,在 H2 中多了一个 Server Push 的技术,server 可以从 client 发出的请求得知它可能还会需要哪些资源,而不等 client parse HTML 後发现要什麽资源再发出请求,直接主动将这些资源传给 client。

不过关於 Server Push 这个技术其实并没有看起来那麽美好,首先是需要开发者自己定义哪些档案需要 server 主动传送给 client 端,再来是有一派说法认为用 Server Push 推送静态资源并不是一件很有意义的事,一方面是像 CSS 档案通常会放在 HTML 的前半部,很快就会被 parse 到,那麽使用 Server Push 的时间并不会相差多少,再来是目前大多数 CDN Provider 没有支援 Server Push,要使用 Server Push 除了得牺牲 CDN 的好处外,也得使用自己的服务器的资源。最後是静态资源通常都会被浏览器快取,但 Server Push 不会管这个资源是不是已经被快取,一律会进行推送,这就造成网路频宽的浪费。

Server Push 能推送的资源是有限制的,必须要可以被快取且不能带有 response body,因此只能推送 GET 与 HEAD 的请求。

在没有那麽把握可以采用 Server Push 时,也可以考虑使用之前介绍过的 resource hint 例如 prefetch,让浏览器来替我们处理这些问题。

有了 H2,一些效能优化技巧已不再需要

前面提到过因为 HTTP 1.1 的限制,衍伸出了一些富有创意的效能优化技巧,例如 CSS Sprites 或是 inline scripting 这些减少发出 HTTP Request 的方式,但这些技巧都带来一些 trade off,例如使用 Image Sprites 需要注意单一档案的大小,也会让程序变得较不好维护,inline 的写法则会导致没办法利用浏览器的快取机制。而这些技巧在使用 H2 後,因为 H2 Multiplexing 的特性,基本上已经没什麽存在的必要了。

如何升级成 HTTP 2 与困难点

要升级成 HTTP 2 其实我们不用对现有的程序码做任何改动,只需要

  • 确保使用 HTTPS 加密连线
  • Web Server 支援升级

如果觉得 Web Server 要设定 TLS 又要设定 H2 有点太麻烦,也可以偷吃步使用像 Cloudflare 这样的 CDN 服务,可以使用免费的 TLS 凭证与 H2/H3 连线,不过注意只有 client 端到 CDN domain 这段是走 H2/H3 连线,CDN 到 origin server 这段目前还是走 HTTP 1.1。有兴趣的读者可以参考这里

看起来要升级并不困难,但对於原本并不是全站使用 HTTPS 的网站来说,可能是一个看起来深不见底的大坑,因为要使用 H2 必须替换成 HTTPS 的通讯协定,这时就要注意Mixed content 问题,一不注意可能网站就因为资源请求被浏览器挡住而暂时挂掉了,至於要如何有效率解决 Mixed content 问题可以参考这篇文章

HTTP/3

我们知道在 H2 中 TCP 的 Head-of-line blocking 这个问题还是没办法被解决,後来推出的 HTTP 3 (简称 H3) 选择直接将传输层的 TCP 给替换掉来解决这个问题。

至於把 TCP 换掉後用什麽技术替代呢?答案是以 UDP 为基础的 QUIC。稍微接触过 Networking 观念的读者应该都知道 UDP 是不可靠的传输机制,而 QUIC 则是将不可靠的 UDP 改良为可靠性的传输。

关於 H3 今天就点到为止,读者可以先记住它是改为使用以 UDP 为基础的 QUIC 机制来解决 TCP head-of-line blocking 的问题,目前 H3 的支援度还没有那麽普及,不过可想而知它在未来必定会越来越普遍,目前一些网站也已经在使用 H3 罗~

本日小结

今天介绍的 Networking 观念虽然不是单纯靠前端开发者就可以处理的问题,但我觉得有一个重点是 HTTP 版本的提升可以「让网页效能有巨幅且显着的成长」。透过今天的介绍读者应该也可以了解原有版本的一些限制以及新版本尝试解决的问题,新版本的协定例如 H3 虽然还有许多网站没有采用或是设备还没有支援,但旧有版本例如 HTTP 1.1 被汰换掉只是迟早的事情,目前的建议就是如果可以替换成 H2 就尽量升级吧!身为一个常与 HTTP 共舞的前端开发者,了解这些概念是十分重要的喔!

References & 图片来源

https://ithelp.ithome.com.tw/articles/10225751
https://developers.google.com/web/fundamentals/performance/http2
https://blog.bluetriangle.com/blocking-web-performance-villain
https://zhuanlan.zhihu.com/p/26757514
https://www.section.io/engineering-education/http3-vs-http2/
https://zhuanlan.zhihu.com/p/26757514
https://tw.alphacamp.co/blog/2016-07-12-http2
https://developers.google.com/web/fundamentals/performance/http2/


<<:  Day-22 Spinner

>>:  【Day25】[演算法]-合并排序法Merge Sort

I Want To Know React - Context 语法

回顾 Context 在上一章节中,我们介绍了何谓 context。 Context 是一种利用向下...

深度学习常用程序码

之前没整理程序码的习惯 经常想到某些程序码 就要翻以前写的 觉得有点浪费时间 之後有用到就贴上来 慢...

【2022】 一键下载在线影片的六个办法(必收藏系列)

范先生推荐好物时间 本期要跟大夥介绍的是如何一键下载在线影片,适合多种网站,包括Xvideo,Por...

【Day 28】NumPy (5):sum(), power(), transpose()

前言 今天要继续来介绍一下用於数学运算的函式,NumPy 太多用途了,真的非常需要好好的了解 Num...

【Day6】 Introduction

基本语法 前一篇我们提到了输出要用cout,那如果我们今天想要输入进资料呢?这时候就要使用到cin啦...