乾貨HTTP/2.0,騙你是小狗

三妹不是佩奇啊發表於2019-10-12

面試官:http/1.0和http/2.0區別是什麼?

你:

  • 解決 HTTP 中的隊頭阻塞問題;

  • 二進位制協議

  • http/2.0 的首部還會被深度壓縮。這將顯著減少傳輸中的冗餘位元組

  •  多路複用

你以為你備幾條答案就完事了嗎?,我們能不能有點出息,稍微詳細一點可以不?又沒讓你現場除錯http/2.0慌個毛線啊~?
老規矩啊,一下子沒看懂的,或者腦袋放空不能吸收知識的時候,別來打我啊,請你收藏下來慢慢看,耐心!!!耐心!!!耐心!!!

三言兩語http1.0和http/2.0

?http/0.9 :只支援get方法,不支援多媒體內容的 MIME 型別、各種 HTTP 首部,或者版本號,只是為了獲取html物件。

?http/1.0 :新增了版本號、各種 HTTP 首部、一些額外的方法,以及對多媒體物件的處理。

?http/1.0+ :keep-alive 連線、虛擬主機支援,以及代理連線支援都被加入到 HTTP 之中等等。

?http/1.1: 重點關注的是校正 HTTP 設計中的結構性缺陷,明確語義,引入重要 的效能優化措施,並刪除一些不好的特性:如Entity tag,If-Unmodified-Since, If-Match, If-None-Match;請求頭引入了range頭域,它允許只請求資源的某個部分(206);新增了更多的狀態碼;Host頭處理(400)

?HTTP/2.0 被寄予瞭如下期望:

  • 相比於使用 TCP 的 HTTP/1.1,終端使用者可感知的多數延遲都有能夠量化的顯 著改善;

  • 解決 HTTP 中的隊頭阻塞問題;

  • 並行的實現機制不依賴與伺服器建立多個連線,從而提升 TCP 連線的利用率,

    特別是在擁塞控制方面;

  • 保留 HTTP/1.1 的語義,可以利用已有的文件資源(如上所述),包括(但不限於)

    HTTP 方法、狀態碼、URI 和首部欄位;

  • 明確定義 HTTP/2.0 和 HTTP/1.x 互動的方法,特別是通過中介時的方法(雙向);

  • 明確指出它們可以被合理使用的新的擴充套件點和策略。

http/2.0到底有什麼?

http/2分層

http/2 大致可以分為兩部分:分幀層,即 h2 多路複用能力的核心部分;資料或 http 層,其中包含傳統上被認為是 HTTP 及其關聯資料的部分。 

二進位制協議:

 h2 的分幀層是基於幀的二進位制協議。這方便了機器解析,但是肉眼識別起來比較困難。

首部壓縮:

僅僅使用二進位制協議似乎還不夠,h2 的首部還會被深度壓縮。這將顯著減少傳輸中的冗餘位元組 

 多路複用 :

在你喜愛的除錯工具裡檢視基於 h2 傳輸的連線的時候,你會發現請求和響應交織在一起。

 加密傳輸:

線上傳輸的絕大部分資料是加密過的,所以在中途讀取會更加困難。

連線

連線是所有 HTTP/2 會話的基礎元素,其定義是客戶端初始化的一個 TCP/IP socket,客戶端 是指傳送 HTTP 請求的實體。這和 h1 是一樣的,不過與完全無狀態的 h1 不同的是,h2 把它所承載的幀(frame)和流(stream)共同依賴的連線層元素捆綁在一起,其中既包含連線層設定也包含首部表。 

判斷是否支援http/2.0
?協議發現——識別終端是否支援你想使用的協議——會比較棘手。HTTP/2 提供兩種協
議發現的機制。
?在連線不加密的情況下,客戶端會利用 Upgrade 首部來表明期望使用 h2。如果伺服器 也可以支援 h2,它會返回一個“101 Switching Protocols”(協議轉換)響應。這增加了 一輪完整的請求-響應通訊。
?如果連線基於 TLS,情況就不同了。客戶端在 ClientHello 訊息中設定 ALPN (Application-Layer Protocol Negotiation,應用層協議協商)擴充套件來表明期望使用 h2 協 議,伺服器用同樣的方式回覆。

 為了向伺服器雙重確認客戶端支援 h2,客戶端會傳送一個叫作 connection preface(連線 前奏)的魔法位元組流,作為連線的第一份資料。這主要是為了應對客戶端通過純文字的 HTTP/1.1 升級上來的情況。該位元組流用十六進位制表示如下:

0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a複製程式碼

解碼為 ASCII 是:

PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n 複製程式碼

這個字串的用處是,如果伺服器(或者中間網路裝置)不支援 h2,就會產生一個顯式錯誤。這個訊息特意設計成 h1 訊息的樣式。如果執行良好的 h1 伺服器收到這個字串,它會阻塞這個方法(PRI)或者版本(HTTP/2.0),並返回錯誤,可以讓 h2 客戶端明確地知道發生了什麼錯誤。 

這個魔法字串會有一個 SETTINGS 緊隨其後。伺服器為了確認它可以支援 h2,會宣告收到客戶端的 SETTINGS 幀,並返回一個它自己的 SETTINGS 幀(反過來也需要確認), 然後確認環境正常,可以開始使用 h2。 (注意⚠️:SETTINGS 幀是個非常重要的幀,http/2.0有十幾個幀,不同的幀代表不一樣的狀態功能)

HTTP/2 是基於幀(frame)的協議。採用分幀是為了將重要資訊都封裝起來,讓協議的解析方可以輕鬆閱讀、解析並還原資訊。 相比之下,h1 不是基於幀的,而是以 文字分隔。 看看下面的簡單例子:

GET / HTTP/1.1 <crlf>
Host: www.example.com <crlf>
Connection: keep-alive <crlf>
Accept: text/html,application/xhtml+xml,application/xml;q=0.9... <crlf>
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4)... <crlf>
Accept-Encoding: gzip, deflate, sdch <crlf>
Accept-Language: en-US,en;q=0.8 <crlf>
Cookie: pfy_cbc_lb=p-browse-w; customerZipCode=99912|N; ltc=%20;...<crlf> 複製程式碼

解析這種資料用不著什麼高科技,但往往速度慢且容易出錯。你需要不斷讀入位元組,直到 遇到分隔符為止(這裡是指 <crlf>),同時還要考慮一些不太守規矩的客戶端,它們會只 傳送 <lf>。

解析 h1 的請求或響應可能出現下列問題:

 • 一次只能處理一個請求或響應,完成之前不能停止解析。

• 無法預判解析需要多少記憶體。這會帶來一系列問題:你要把一行讀到多大的緩衝區裡。

如果行太長會發生什麼;應該增加並重新分配記憶體,還是返回 400 錯誤。為了解決這些問題,保持記憶體處理的效率和速度可不簡單。 

從另一方面來說,有了幀,處理協議的程式就能預先知道會收到什麼。基於幀的協議,特別是 h2,開始有固定長度的位元組,其中包含表示整幀長度的欄位。

來,我們看一下幀結構?乾貨HTTP/2.0,騙你是小狗

  • Length: 3 位元組   表示幀負載的長度(取值範圍為 2^ 14~2^24-1 位元組)。 請注意,2^14 位元組是預設的最大幀大小,如果需要更大的幀,必須在 SETTINGS 幀中設定 。

  • Type 1 位元組  當前幀型別

  • Flags1 位元組  具體幀型別的標識

  • R 1 位  保留位,不要設定,否則可能帶來嚴重後果

  •  Stream Identifier: 31 位  每個流的唯一 ID 

  • Frame Payload長度可變   真實的幀內容,長度是在 Length 欄位中設定的 

 因為規範嚴格明確,所以解析邏輯大概是這樣:

loop
   Read 9 bytes off the wire                          //讀前9個位元組
   Length = the first three bytes                     //長度值為前3位元組
   Read the payload based on the length.              //基於長度讀負載
   Take the appropriate action based on the frame type. // 根據幀型別採取對應操作
end loop 			複製程式碼

http/1有個特性叫管道化(pipelining),允許一次傳送一組請求,但是隻能按照傳送順序依次接 收響應。而且,管道化備受互操作性和部署的各種問題的困擾,基本沒有實用價值。 在請求應答過程中,如果出現任何狀況,剩下所有的工作都會被阻塞在那次請求應答之 後。這就是“隊頭阻塞”.它會阻礙網路傳輸和 Web 頁面渲染,直至失去響應。為了防止 這種問題,現代瀏覽器會針對單個域名開啟 6 個連線,通過各個連線分別傳送請求。

它實 現了某種程度上的並行,但是每個連線仍會受到“隊頭阻塞”的影響。由於 h2 是分幀的, 請求和響應可以交錯甚至多路複用。多路複用有助於解決類似隊頭阻塞的問題。 
說了半天隊頭阻塞的過程是怎樣的呢?
我怎麼會讓你們一臉懵逼的去百度呢,來來來,往下看

乾貨HTTP/2.0,騙你是小狗

借老哥一張圖:Head of line blocking

乾貨HTTP/2.0,騙你是小狗

  • 圖中第一種請求方式,就是單次傳送request請求,收到response後再進行下一次請求,顯示是很低效的。
  • 於是http1.1提出了管線化(pipelining)技術,就是如圖中第二中請求方式,一次性傳送多個request請求。
  • 然而pipelining在接收response返回時,也必須依順序接收,如果前一個請求遇到了阻塞,後面的請求即使已經處理完畢了,仍然需要等待阻塞的請求處理完畢。這種情況就如圖中第三種,第一個請求阻塞後,後面的請求都需要等待,這也就是隊頭阻塞(Head of line blocking)。
  • 為了解決上述阻塞問題,http2中提出了多路複用(Multiplexing)技術,Multiplexing是通訊和計算機網路領域的專業名詞。http2中將多個請求複用同一個tcp連結中,將一個TCP連線分為若干個流(Stream),每個流中可以傳輸若干訊息(Message),每個訊息由若干最小的二進位制幀(Frame)組成。也就是將每個request-response拆分為了細小的二進位制幀Frame,這樣即使一個請求被阻塞了,也不會影響其他請求,如圖中第四種情況所示。

HTTP/2 規範對流(stream)的定義是:“HTTP/2 連線上獨立的、雙向的幀序列交換。”你可以將流看作在連線上的一系列幀,它們構成了單獨的 HTTP 請求和響應。如果客戶端想 要發出請求,它會開啟一個新的流。然後,伺服器將在這個流上回復。這與 h1 的請求 / 響應流程類似,重要的區別在於,因為有分幀,所以多個請求和響應可以交錯,而不會互相阻塞。 

看到這裡,是不是開始對之前看到的幀,多路複用模糊了,怎麼又來了個流?阿西吧,別慌,讓我給你解釋解釋?

流(stream),一個完整的請求-響應資料互動過程,具有如下幾個特點:

  1. 雙向性:同一個流內,可同時傳送和接受資料;
  2. 有序性:幀(frames)在流上的傳送順序很重要. 接收方將按照他們的接收順序處理這些frame. 特別是HEADERS和DATA frame的順序, 在協議的語義上顯得尤為重要.

流的建立:流可以被客戶端或伺服器單方面建立, 使用或共享;
流的關閉:流也可以被任意一方關閉;

敲黑板,畫重點!!!別再混淆了!

多路複用:一個連線同一時刻可以被多個流使用。
流的併發性:某一時刻,連線上流的併發數。

借老哥一張圖:http/2.0流

乾貨HTTP/2.0,騙你是小狗

我們再強調一下多路複用的好處:

  1. 減少服務端連線壓力,減少佔用記憶體,提升連線吞吐量;
  2. 連線數的減少改善了網路擁塞狀況,慢啟動時間減少,擁塞和丟包恢復速度更快;
  3. 避免連線頻繁建立和關閉(三次連線、四次揮手);

連線、流和幀的關係

  1. 一個連線同時被多個流複用;
  2. 一個流代表一次完整的請求/響應過程,包含多個幀;
  3. 一個訊息被拆分封裝成多個幀進行傳輸; 

乾貨HTTP/2.0,騙你是小狗

訊息

HTTP 訊息泛指 HTTP 請求或響應。 流是用來傳輸一對請求 / 響 應訊息的。一個訊息至少由 HEADERS 幀(它初始化流)組成,並且可以另外包含 CONTINUATIONDATA 幀,以及其他的 HEADERS 幀。 

乾貨HTTP/2.0,騙你是小狗

h1 的請求和響應都分成訊息首部和訊息體兩部分;與之類似,h2 的請求和響應分成HEADERS 幀DATA 幀

h1 把訊息分成兩部分:請求 / 狀態行;首部。h2 取消了這種區分,並把這些行變成了 魔法偽首部。舉個例子,HTTP/1.1 的請求和響應可能是這樣的:

     GET / HTTP/1.1
     Host: www.example.com
     User-agent: Next-Great-h2-browser-1.0.0
     Accept-Encoding: compress, gzip
     HTTP/1.1 200 OK
     Content-type: text/plain
     Content-length: 2     ...複製程式碼

在 HTTP/2 中,它等價於:

 :scheme: https:method: GET
 :path: /
 :authority: www.example.com
 User-agent: Next-Great-h2-browser-1.0.0
 Accept-Encoding: compress, gzip 
 :status: 200 
 content-type: text/plain複製程式碼

請注意,請求和狀態行在這裡拆分成了多個首部,即 :scheme、:method、:path 和 :status。 同時要注意的是,http/2.0 的這種表示方式跟資料傳輸時不同。 

沒有分塊編碼(chunked encoding) 

在基於幀的世界裡,誰還需要分塊?只有在無法預先知道資料長度的情況下向對方傳送 資料時,才會用到分塊。在使用幀作為核心協議的 h2 裡,就不再需要它了。

不再有101的響應
Switching Protocol 響應是 h1 的邊緣應用。它如今最常見的應用可能就是用以升級到 WebSocket 連線。ALPN 提供了更明確的協議協商路徑,往返的開銷也更小。 

其實現在大部分還是都是http/1.1,http/2.0網站不多。還好平常刷leetcode,發現leetcode不僅用http/2.0,還用graphql,走在科技的前沿?

乾貨HTTP/2.0,騙你是小狗

再來觀摩一下人家的graphql,想起來前段時間組裡叫我實現前端graphql,現在想想就是一把辛酸淚?

乾貨HTTP/2.0,騙你是小狗

今天重點是http/2.0,就不多說graphql。有興趣老哥請點選傳送門

流量控制

h2 的新特性之一是基於流的流量控制。不同於 h1 的世界,只要客戶端可以處理,服務端就會盡可能快地傳送資料,h2 提供了客戶端調整傳輸速度的能力。(並且,由於在 h2 中, 一切幾乎都是對稱的,服務端也可以調整傳輸的速度。)WINDOW_UPDATE 幀用來指示流量控制資訊。每個幀告訴對方,傳送方想要接收多少位元組。 

客戶端有很多理由使用流量控制。一個很現實的原因可能是,確保某個流不會阻塞其他流。也可能客戶端可用的頻寬和記憶體比較有限,強制資料以可處理的分塊來載入反而可以提升效率。儘管流量控制不能關閉,把視窗最大值設定為設定 2^31-1 就等效于禁用它,至少對小於 2GB 的檔案來說是如此。

另一個需要注意的是中間代理。通常情況下,網路內容通過代理或者 CDN 來傳輸,也許它們就是傳輸的起點或終點。由於代理兩端的吞吐能力可能不同,有了流量控制,代理的兩端就可以密切同步,把代理的壓力降到最低。 

優先順序

流的最後一個重要特性是依賴關係。現代瀏覽器都經過了精心設計,首先請求網頁上最重要的元素,以最優的順序獲取資源,由此來優化頁面效能。拿到了 HTML 之後,在渲染頁面之前,瀏覽器通常還需要 CSS 和關鍵 JavaScript 這樣的東西。在沒有多路複用的時候,在它可以發出對新物件的請求之前,需要等待前一個響應完成。有了 h2,客戶端就可以 一次發出所有資源的請求,服務端也可以立即著手處理這些請求。由此帶來的問題是,瀏覽器失去了在 h1 時代預設的資源請求優先順序策略。假設伺服器同時接收到了 100 個請求, 也沒有標識哪個更重要,那麼它將幾乎同時傳送每個資源,次要元素就會影響到關鍵元素 的傳輸。 

h2 通過流的依賴關係來解決這個問題。通過 HEADERS 幀PRIORITY 幀,客戶端可以明確地和服務端溝通它需要什麼,以及它需要這些資源的順序。這是通過宣告依賴關係樹 和樹裡的相對權重實現的。 

依賴關係為客戶端提供了一種能力,通過指明某些物件對另一些物件有依賴,告知伺服器這些物件應該優先傳輸。

權重讓客戶端告訴伺服器如何確定具有共同依賴關係的物件的優先順序。 

我們來看看這個簡單的網站:

index.html
– header.jpg
– critical.js
– less_critical.js
– style.css
– ad.js
– photo.jpg 	複製程式碼

在收到主體 HTML 檔案之後,客戶端會解析它,並生成依賴樹,然後給樹裡的元素分配權重。這時這棵樹可能是這樣的:

  index.html
  – style.css
    – critical.js
      –  less_critical.js (weight 20)
      –  photo.jpg (weight 8)
      –  header.jpg (weight 8)
      –  ad.js (weight 4) 			複製程式碼

在這個依賴樹裡,客戶端表明它最需要的是 style.css,其次是 critical.js。沒有這兩個檔案, 它就不能接著渲染頁面。等它收到了 critical.js,就可以給出其餘物件的相對權重。權重表示服務一個物件時所需要花費的對應“努力”程度。 

伺服器推送

提升單個物件效能的最佳方式,就是在它被用到之前就放到瀏覽器的快取裡面。這正是 HTTP/2 的服務端推送的目的。推送使伺服器能夠主動將物件發給客戶端,這可能是因為 它知道客戶端不久將用到該物件。 

如果伺服器決定要推送一個物件(RFC 中稱為“推送響應”),會構造一個 PUSH_PROMISE 幀。這個幀有很多重要屬性,列舉如下 :

  • ?PUSH_PROMISE 幀首部中的流 ID 用來響應相關聯的請求。推送的響應一定會對應到 客戶端已傳送的某個請求。如果瀏覽器請求一個主體 HTML 頁面,如果要推送此頁面 使用的某個 JavaScript 物件,伺服器將使用請求對應的流 ID 構造 PUSH_PROMISE 幀

  • ?PUSH_PROMISE 幀的首部塊與客戶端請求推送物件時傳送的首部塊是相似的。所以客戶端有辦法放心檢查將要傳送的請求。

  • ?被髮送的物件必須確保是可快取的。

  • ?:method 首部的值必須確保安全。安全的方法就是冪等的那些方法,這是一種不改變

    任何狀態的好辦法。例如,GET 請求被認為是冪等的,因為它通常只是獲取物件,而POST 請求被認為是非冪等的,因為它可能會改變伺服器端的狀態。

  • ?理想情況下,PUSH_PROMISE 幀應該更早傳送,應當早於客戶端接收到可能承載著推

    送物件的 DATA 幀。假設伺服器要在傳送 PUSH_PROMISE 之前傳送完整的 HTML, 那客戶端可能在接收到 PUSH_PROMISE 之前已經發出了對這個資源的請求。h2 足夠健壯,可以優雅地解決這類問題,但還是會有些浪費。

  • ?PUSH_PROMISE 幀會指示將要傳送的響應所使用的流 ID。 

客戶端會從 1 開始設定流 ID,之後每新開啟一個流,就會增加 2,之後一直 使用奇數。伺服器開啟在 PUSH_PROMISE 中標明的流時,設定的流 ID 從 2 開始,之後一直使用偶數。這種設計避免了客戶端和伺服器之間的流 ID 衝 突,也可以輕鬆地判斷哪些物件是由服務端推送的。0 是保留數字,用於連 接級控制訊息,不能用於建立新的流。 

如果客戶端對 PUSH_PROMISE 的任何元素不滿意,就可以按照拒收原因選擇重置這個流 (使用 RST_STREAM),或者傳送 PROTOCOL_ERROR(在 GOAWAY 幀中)。常見的情況是快取中已經有了這個物件。而 PROTOCOL_ERROR 是專門留給 PUSH_PROMISE 涉及的協議層面問題的,比如方法不安全,或者當客戶端已經在 SETTINGS 幀中表明自己不接受推送時,仍然進行了推送。值得注意的是,伺服器可以在 PUSH_PROMISE 傳送後立即啟動推送流,因此拒收正在進行的推送可能仍然無法避免推送大量資源。推送正確的資源是不夠的,還需要保證只推送正確的資源,這是重要的效能優化手段。 

所以到底如何選擇要推送的資源?

決策的過程需要考慮到如下方面:

 • 資源已經在瀏覽器快取中的概率

• 從客戶端看來,這些資源的優先順序

• 可用的頻寬,以及其他類似的會影響客戶端接收推送的資源 

如果使用者第一次訪問頁面時,就能向客戶端推送頁面渲染所需的關鍵 CSS 和 JS 資源,那 麼服務端推送的真正價值就實現了。不過,這要求伺服器端實現足夠智慧,以避免“推送 承諾”(push promise)與主體 HTML 頁面傳輸競爭頻寬。

乾貨HTTP/2.0,騙你是小狗

 

首部壓縮

“臃腫的訊息首部”提到過,現代網頁平均包含 140 個請求,每個 HTTP 請求 平均有 460 位元組,總資料量達到 63KB。即使在最好的環境下,這也會造成相當長的延時, 如果考慮到擁擠的 WiFi 或連線不暢的蜂窩網路,那可是非常痛苦的。這些請求之間通常幾乎沒有新的或不同的內容,這才是真正的浪費。所以,大家迫切渴望某種型別的壓縮。 經過多次創新性的思考和討論,人們提出了 HPACKHPACK 是種表查詢壓縮方案,它利用霍夫曼編碼獲得接近 GZIP 的壓縮率。 

CRIME 攻擊告訴我們,GZIP 也有洩漏加密資訊的風 險。CRIME 的原理是這樣的,攻擊者在請求中新增資料,觀察壓縮加密後的資料量是否會小於預期。如果變小了,攻擊者就知道注入的文字和請求中的其他內容(比如私有的會話 cookie)有重複。在很短的時間內,經過加密 的資料內容就可以全部搞清楚。因此,大家放棄了已有的壓縮方案,研發出 HPACK。 

第一個請求:

     :authority: www.akamai.com
     :method: GET
     :path: /
     :scheme: https
     accept: text/html,application/xhtml+xml
     accept-language: en-US,en;q=0.8
     cookie: last_page=286A7F3DE
     upgrade-insecure-requests: 1
     user-agent: Awesome H2/1.0複製程式碼

第二個請求:

     :authority: www.akamai.com
     :method: GET     :path: /style.css     :scheme: https     accept: text/html,application/xhtml+xml accept-language: en-US,en;q=0.8     cookie: last_page=*398AB8E8F upgrade-insecure-requests: 1 user-agent: Awesome H2/1.0 
複製程式碼

可以看到,後者的很多資料與前者重複了。第一個請求約有 220 位元組,第二個約有 230 字 節,但二者只有 36 位元組是不同的。如果僅僅傳送這 36 位元組,就可以節省約 85%的位元組 數。簡而言之,HPACK 的原理就是這樣。 

http/2.0反模式

h1 下的一些效能調優辦法在 h2 下會起到反作用。 

域名拆分

域名拆分是為了利用瀏覽器對每個域名開啟多個連線的能力,以便實現資源的並行下載, 繞過 h1 的序列化下載的限制。對於包含大量小型資源的網站,普遍的做法是拆分域名, 以利用現代瀏覽器針能對每個域名開啟 6 個連線的特性。這樣實際上做到了讓瀏覽器並行 傳送多個請求,以及充分利用可用頻寬的效果。因為 HTTP/2 採取多路複用,所以域名拆 分就不是必要的了,並且反而會讓協議力圖實現的目標落空。 

資源內聯

資源內聯包括把 JavaScript、樣式,甚至圖片插入到 HTML 頁面中,目的是省掉載入外部資源所需的新連線以及請求響應的時間。然而,有些 Web 效能的最佳實踐不推薦使用內聯,因為這樣會損失更有價值的特性,比如快取。如果有同一個頁面上的重複訪問,快取通常可以減少請求數(而且能夠加速頁面渲染)。儘管如此,總體來說,對那些渲染滾動 條以上區域所需的微小資源進行內聯處理仍是值得的。

事實上有證據表明,在效能較弱的 裝置上,快取物件的好處不夠多,把內聯資源拆分出來並不划算。使用 h2 時的一般原則是避免內聯,但是內聯也並不一定毫無價值。 

資源合併

資源合併意味著把幾個小檔案合併成一個大檔案。它與內聯很相似,旨在省掉那些載入外部資源的請求響應時間,以及解碼 / 執行那些資源所消耗的 CPU 資源。之前針對資源內聯 的規則同樣適用於資源合併,我們可以使用它來合併非常小的檔案(1KB 或更小),以及 對初始渲染很關鍵的最簡化 JavaScript/CSS 資源。 

禁用cookie的域名

通過禁用 cookie 的域名來提供靜態資源是一項標準的效能優化最佳實踐。尤其是使用 h1 時,你無法壓縮首部,而且有些網站使用的 cookie 大小常常超過單個 TCP 資料包的限度。 不過,在 h2 下請求首部使用 HPACK 演算法被壓縮,會顯著減少巨型 cookie(尤其是當它 們在先後請求之間保持不變)的位元組數。與此同時,禁用 cookie 的域名需要額外的主機名 稱,這意味著將開啟更多的連線。

如果你正在使用禁用 cookie 的域名,以後有機會你可能得考慮消滅它。如果你確實不需要那些域名,最好刪掉它們。省一個位元組就是一個位元組。 

生成精靈圖

目前,生成精靈圖仍是一種避免小資源請求過多的技術(你能看到人們樂意做什麼來優化 h1)。為了生成精靈圖,開發者把較小的圖片拼合成較大的圖片,然後用 CSS 選擇圖片中 某個部分展示出來。依據裝置及其硬體圖形處理能力的不同,精靈圖要麼非常高效,要麼 非常低效。如果用 h2,最佳實踐就是避免生成精靈圖;主要原因在於,多路複用和首部壓縮去掉了大量的請求開銷。即便如此,還是有些場景適合使用精靈圖。


效能優化因人而異 

為了最大化 Web 效能,需要在許多變數之間取捨,包括網路條件、裝置 處理能力、瀏覽器能力,還有協議限制。這些組成了我們所說的場景,大多數開發者 的時間遠遠不夠考慮那麼多場景。 

怎麼辦?最佳實踐的第一原則就是:測試。效能測試與監控是獲得最大成果的關鍵, HTTP/2 也不例外。觀察真實使用者資料、詳盡分析各種條件、查詢問題,然後解決它們。要遵循業界推薦的方式,但也不要陷入過早優化的陷阱。



it's over.

有問題,歡迎指正呀~


相關文章