《HTTP/2 基礎教程》 閱讀摘要

最近粗線了很多 HTTP2 相關的帖子和討論,感受新一輪的潮流在造成,因此最近找了本 HTTP2 相關書籍作知識儲備,恰好記成筆記以備後詢 ~css

這本書自己不錯,缺點就是翻譯的有點蹩腳,另外由於是 2017 年出的書,因此有些內容時效性不太好,好比關於 Chrome 的部分,因此我根據 Chrome 的官方文檔增長了點內容 😅html

若是但願獲取本書的 PDF 資源,能夠關注文末二維碼加微信羣找羣主要~前端

1. HTTP進化史

1.1 HTTP/0.九、HTTP/1.0、HTTP/1.1

  1. HTTP/0.9: 是個至關簡單的協議。它只有一個方法(GET),沒有首部,其設計目標也無非是獲取 HTML(也就是說沒有圖片,只有文本)。
  2. HTTP/1.0: 多了不少功能,首部、錯誤碼、重定向、條件請求等,但仍存在不少瑕疵,尤爲是不能讓多個請求共用一個鏈接、缺乏強制的 Host 首部、緩存的選擇也至關簡陋,這三點影響了 Web 可擴展的方式。
  3. HTTP/1.1: 增長了緩存相關首部的擴展、OPTIONS 方法、Upgrade 首部、Range 請求、壓縮和傳輸編碼、管道化等功能。由於強制要求客戶端提供 Host 首部,因此虛擬主機託管成爲可能,也就是在一個 IP 上提供多個 Web 服務。另外使用了 keep-alive 以後,Web 服務器也不須要在每一個響應以後關閉鏈接。這對於提高性能和效率而言意義重大,由於瀏覽器不再用爲每一個請求從新發起 TCP 鏈接了。

1.2 HTTP/2

HTTP2 被但願達到如下特性:web

  • 相比 HTTP/1.1,性能顯著提升;
  • 解決 HTTP 中的隊頭阻塞問題;
  • 並行的實現機制不依賴與服務器創建多個鏈接,從而提高 TCP 鏈接的利用率,特別是在擁塞控制方面;
  • 保留 HTTP/1.1 的語義,能夠利用已有的文檔資源,包括(但不限於) HTTP 方法、狀態碼、URI 和首部字段;
  • 明肯定義 HTTP/2.0 和 HTTP/1.x 交互的方法,特別是經過中介時的方法(雙向);
  • 明確指出它們能夠被合理使用的新的擴展點和策略。

2. HTTP/2 快速入門

2.1 啓動並運行

不少網站已經在用HTTP/2(h2)了,好比 Facebook、Instagram、Twitter 等,下面介紹如下如何本身搭建 h2 服務器。要運行 h2 服務器,主要分兩步:ajax

  1. 獲取並安裝一個支持 h2 的 Web 服務器
  2. 下載並安裝一張 TLS 證書,讓瀏覽器和服務器經過 h2 鏈接

2.2 獲取證書

證書能夠經過三種方式獲取:算法

  1. 使用在線資源
  2. 本身建立一張證書
  3. 從數字證書認證機構(CA)申請一張證書

前兩個方法 將建立自簽名證書,僅用於測試,因爲不是 CA 簽發的,瀏覽器會報警chrome

後面關於建立 h2 服務器的步驟就不記了,能夠百度下api

3. Web優化『黑魔法』的動機與方式

3.1 當前的性能挑戰

3.1.1 剖析Web頁面請求

從用戶在瀏覽器中點擊連接到頁面呈如今屏幕上,在此期間到底發生了什麼?瀏覽器請求 Web 頁面時,會執行重複流程,獲取在屏幕上繪製頁面須要的全部信息。爲了更容易理解,咱們把這一過程分紅兩部分:資源獲取、頁面解析/渲染。promise

資源請求流程圖:瀏覽器

流程爲:

  1. 把待請求 URL 放入隊列
  2. 解析 URL 中域名的 IP 地址(A)
  3. 創建與目標主機的 TCP 鏈接(B)
  4. 若是是 HTTPS 請求,初始化並完成 TLS 握手(C)
  5. 向頁面對應的 URL 發送請求。

資源響應流程圖:

  1. 接收響應
  2. 若是(接收的)是主體 HTML,那麼解析它,並針對頁面中的資源觸發優先獲取機制(A)
  3. 若是頁面上的關鍵資源已經接收到,就開始渲染頁面(B)
  4. 接收其餘資源,繼續解析渲染,直到結束(C)

頁面上的每一次點擊,都須要重複執行前面那些流程,給網絡帶寬和設備資源帶來壓力。Web 性能優化的的核心,就是加快甚至乾脆去掉其中的某些步驟。

3.1.2 關鍵性能指標

下面網絡級別的性能指標,它會影響整個 Web 頁面加載。

  1. 延遲: 指 IP 數據包從一個網絡端點到另外一個網絡端點所花費的時間。
  2. 帶寬: 只要帶寬沒有飽和,兩個網絡端點之間的鏈接會一次處理儘量多的數據量。
  3. DNS查詢: 在客戶端可以獲取 Web 頁面前,它須要經過域名系統(DNS)把主機名稱轉換成 IP 地址。
  4. 創建鏈接時間: 在客戶端和服務器之間創建鏈接須要三次握手。握手時間通常與客戶端和服務器之間的延遲有關。
  5. TLS協商時間: 若是客戶端發起 HTTPS 鏈接,它還須要進行傳輸層安全協議(TLS)協商,TLS 會形成額外的往返傳輸。
  6. 首字節時間(TTFB): TTFB 是指客戶端從開始定位到 Web 頁面,至接收到主體頁面響應的第一字節所耗費的時間。它包含了以前提到的各類耗時,還要加上服務器處理時間。對於主體頁面上的資源,TTFB 測量的是從瀏覽器發起請求至收到其第一字節之間的耗時。
  7. 內容下載時間: 等同於被請求資源的最後字節到達時間(TTLB)。
  8. 開始渲染時間: 客戶端的屏幕上何時開始顯示內容?這個指標測量的是用戶看到空白頁面的時長。
  9. 文檔加載完成時間: 這是客戶端瀏覽器認爲頁面加載完畢的時間。

3.1.3 HTTP/1 的問題

HTTP/1 的問題天然是 HTTP/2 要解決的核心問題

1. 隊頭阻塞

瀏覽器不多隻從一個域名獲取一份資源,通常但願能同時獲取許多資源。h1 有個特性叫管道化(pipelining),容許一次發送一組請求,可是隻能按照發送順序依次接收響應。管道化備受互操做性和部署的各類問題的困擾,基本沒有實用價值。
在請求應答過程當中,若是出現任何情況,剩下全部的工做都會被阻塞在那次請求應答以後。這就是『隊頭阻塞』,它會阻礙網絡傳輸和 Web 頁面渲染,直至失去響應。爲了防止這種問題,現代瀏覽器會針對單個域名開啓 6 個鏈接,經過各個鏈接分別發送請求。它實現了某種程度上的並行,可是每一個鏈接仍會受到的影響。

2. 低效的 TCP 利用

傳輸控制協議(TCP)的設計思路是:對假設狀況很保守,並可以公平對待同一網絡的不一樣流量的應用。涉及的核心概念就是擁塞窗口(congestion window)。『擁塞窗口』是指,在接收方確認數據包以前,發送方能夠發出的 TCP 包的數量。 例如,若是擁塞窗口指定爲 1,那麼發送方發出 1 個數據包以後,只有接收方確認了那個包,才能發送下一個。

TCP 有個概念叫慢啓動(Slow Start),它用來探索當前鏈接對應擁塞窗口的合適大小。慢啓動的設計目標是爲了讓新鏈接搞清楚當前網絡情況,避免給已經擁堵的網絡繼續添亂。它容許發送者在收到每一個確認回覆後額外發送 1 個未確認包。這意味着新鏈接在收到 1 個確認回覆以後,能夠發送 2 個數據包;在收到 2 個確認回覆以後,能夠發 4 個,以此類推。這種幾何級數增加很快到達協議規定的發包數上限,這時候鏈接將進入擁塞避免階段。

TCP擁塞控制

這種機制須要幾回往返數據請求才能得知最佳擁塞窗口大小。但在解決性能問題時,就這區區幾回數據往返也是很是寶貴的時間成本。若是你把一個數據包設置爲最大值下限 1460 字節,那麼只能先發送 5840 字節(假定擁塞窗口爲 4),而後就須要等待接收確認回覆。理想狀況下,這須要大約 9 次往返請求來傳輸完整個頁面。除此以外,瀏覽器通常會針對同一個域名開啓 6 個併發鏈接,每一個鏈接都免不了擁塞窗口調節。

傳統 TCP 實現利用擁塞控制算法會根據數據包的丟失來反饋調整。若是數據包確認丟失了,算法就會縮小擁塞窗口。這就相似於咱們在黑暗的房間摸索,若是腿碰到了桌子就會立刻換個方向。若是遇到超時,也就是等待的回覆沒有按時抵達,它甚至會完全重置擁塞窗口並從新進入慢啓動階段。新的算法會把其餘因素也考慮進來,例如延遲,以提供更妥善的反饋機制。

前面提到過,由於 h1 並不支持多路複用,因此瀏覽器通常會針對指定域名開啓 6 個併發鏈接。這意味着擁塞窗口波動也會並行發生 6 次。TCP 協議保證那些鏈接都能正常工做,可是不能保證它們的性能是最優的。

3. 臃腫的消息首部

雖然 h1 提供了壓縮被請求內容的機制,可是消息首部卻沒法壓縮。消息首部可不能忽略,儘管它比響應資源小不少,但它可能佔據請求的絕大部分(有時候多是所有)。若是算上 cookie,就更大了。

消息首部壓縮的缺失也容易致使客戶端到達帶寬上限,對於低帶寬或高擁堵的鏈路尤爲如此。『體育館效應』(Stadium Effect)就是一個經典例子。若是成千上萬人同一時間出如今同一地點(例如重大致育賽事),會迅速耗盡無線蜂窩網絡帶寬。這時候,若是能壓縮請求首部,把請求變得更小,就可以緩解帶寬壓力,下降系統的總負載。

4. 受限的優先級設置

若是瀏覽器針對指定域名開啓了多個 socket(每一個都會受隊頭阻塞問題的困擾),開始請求資源,這時候瀏覽器能指定優先級的方式是有限的:要麼發起請求,要麼不發起。然而 Web 頁面上某些資源會比另外一些更重要,這必然會加劇資源的排隊效應。這是由於瀏覽器爲了先請求優先級高的資源,會推遲請求其餘資源。可是優先級高的資源獲取以後,在處理的過程當中,瀏覽器並不會發起新的資源請求,因此服務器沒法利用這段時間發送優先級低的資源,總的頁面下載時間所以延長了。還會出現這樣的狀況:一個高優先級資源被瀏覽器發現,可是受制於瀏覽器處理的方式,它被排在了一個正在獲取的低優先級資源以後。

5. 第三方資源

現在 Web 頁面上請求的不少資源徹底獨立於站點服務器的控制,咱們稱這些爲第三方資源。現代 Web 頁面加載時長中每每有一半消耗在第三方資源上。雖然有不少技巧能把第三方資源對頁面性能的影響降到最低,可是不少第三方資源都不在 Web 開發者的控制範圍內,因此極可能其中有些資源的性能不好,會延遲甚至阻塞頁面渲染。使人掃興的是, h2 對此也一籌莫展。

3.2 Web性能優化技術

2010 年,谷歌把 Web 性能做爲影響頁面搜索評分的重要因素之一,性能指標開始在搜索引擎中發揮做用。對於不少 Web 頁面,瀏覽器的大塊時間並非用於呈現來自網站的主體內容(一般是 HTML),而是在請求全部資源並渲染頁面。

所以,Web 開發者逐漸更多地關注經過減小客戶端網絡延遲和優化頁面渲染性能來提高Web 性能。

3.2.1 Web性能的最佳實踐

1. DNS 查詢優化

在與服務主機創建鏈接以前,須要先解析域名;那麼,解析越快就越好。下面有一些方法:

  1. 限制不一樣域名的數量。固然,這一般不是你能控制的
  2. 保證低限度的解析延遲。瞭解你的 DNS 服務基礎設施的結構,而後從你的最終用戶分佈的全部地域按期監控解析時間
  3. 在 HTML 或響應中利用 DNS 預取指令。這樣,在下載並處理 HTML 的同時,預取指令就能開始解析頁面上指定的域名
<link rel="dns-prefetch" href="//ajax.googleapis.com">
2. 優化 TCP 鏈接

本章前面提到過,開啓新鏈接是一個耗時的過程。若是鏈接使用 TLS(也確實應該這麼作),開銷會更高。下降這種開銷的方法以下

  1. 儘早終止並響應。藉助 CDN,在距離請求用戶很近的邊緣端點上,請求就能夠得到響應,因此能夠終止鏈接,大幅減小創建新鏈接的通訊延遲。
  2. 實施最新的 TLS 最佳實踐來優化 HTTPS。
  3. 利用 preconnect 指令,鏈接在使用以前就已經創建好了,這樣處理流程的關鍵路徑上就沒必要考慮鏈接時間了,preconnect 不光會解析 DNS,還會創建 TCP 握手鍊接和 TLS 協議(若是須要)
<link rel="preconnect" href="//fonts.example.com" crossorigin>

若是要從同一個域名請求大量資源,瀏覽器將自動開啓到服務器的併發鏈接,避免資源獲取瓶頸。雖然如今大部分瀏覽器支持 6 個或更多的併發鏈接數目,但你不能直接控制瀏覽器針對同一域名的併發鏈接數。

3. 避免重定向

重定向一般觸發與額外域名創建鏈接。在移動網絡中,一次額外的重定向可能把延遲增長數百毫秒,這不利於用戶體驗,並最終會影響到網站上的業務。簡單的解決方案就是完全消滅重定向,由於對於重定向的使每每並無合理緣由。若是它們不能被直接消滅,你還有兩個選擇:

  1. 利用 CDN 代替客戶端在雲端實現重定向
  2. 若是是同一域名的重定向,使用 Web 服務器上的 rewrite 規則,避免重定向
4. 客戶端緩存

沒有什麼比直接從本地緩存獲取資源來得更快,由於它根本就不須要創建網絡鏈接。

  1. 所謂的純靜態內容,例如圖片或帶版本的數據,能夠在客戶端永久緩存。即使 TTL 被設置得很長,好比一個月,它仍是會由於緩存提前回收或清理而過時,這時客戶端可能不得不從源頭再次獲取。
  2. CSS/JS 和個性化資源,緩存時間大約是會話(交互)平均時間的兩倍。這段時間足夠長,保證大多數用戶在瀏覽網站時可以從本地拉取資源;同時也足夠短,幾乎能保證下次會話時從網絡上拉取最新內容。

能夠經過 HTTP 首部指定 cache control 以及鍵 max-age(以秒爲單位),或者 expires 首部。

5. 網絡邊緣的緩存

我的信息(用戶偏好、財務數據等)絕對不能在網絡邊緣緩存,由於它們不能共享。時間敏感的資源也不該該緩存,例如實時交易系統上的股票報價。這就是說,除此以外其餘一切都是能夠緩存的,即便僅僅緩存幾秒或幾分鐘。對於那些不是常常更新,然而一旦有變化就必須馬上更新的資源,例如重大新聞,能夠利用各大 CDN 廠商提供的緩存清理(purging)機制處理。這種模式被稱爲『一直保留,直到被通知』(Hold til Told),意思是永久緩存這些資源,等收到通知後才刪除。

6. 條件緩存

若是緩存 TTL 過時,客戶端會向服務器發起請求。在多數狀況下,收到的響應其實和緩存的版本是同樣的,從新下載已經在緩存裏的內容也是一種浪費。HTTP 提供條件請求機制,客戶端能以有效方式詢問服務器:『若是內容變了,請返回內容自己;不然,直接告訴我內容沒變。』當資源不常常變化時,使用條件請求能夠顯著節省帶寬和性能;可是,保證資源的最新版迅速可用也是很是重要的。使用條件緩存能夠經過如下方法。

  1. 在請求中包含 HTTP 首部 Last-Modified-Since。僅當最新內容在首部中指定的日期以後被更新過,服務器才返回完整內容;不然只返回 304 響應碼,並在響應首部中附帶上新的時間戳 Date 字段。
  2. 在請求體中包含實體校驗碼,或者叫 ETag;它惟一標識所請求的資源。ETag 由服務器 提供,內嵌於資源的響應首部中。服務器會比較當前 ETag 與請求首部中收到的 ETag,若是一致,就只返回 304 響應碼;不然返回完整內容。

通常來講,大多數 Web 服務器會對圖片和 CSS/JS 使用這些技術,但你也能夠將其用到其餘資源。

7. 壓縮和代碼極簡化

全部的文本內容(HTML、JS、CSS、SVG、XML、JSON、字體等),均可以壓縮和極簡化。這兩種方法組合起來,能夠顯著減小資源大小。更少字節數對應着更少的請求與應答,也就意味着更短的請求時間。

極簡化(minification, 混淆)是指從文本資源中剝離全部非核心內容的過程。一般,要考慮方便人類閱讀和維護,而瀏覽器並不關心可讀性,放棄代碼可讀性反而能節省空間。在極簡化的基礎上,壓縮能夠進一步減小字節數。它經過可無損還原的算法減小資源大小。在發送資源以前,若是服務器進行壓縮處理,能夠節省 90% 的大小。

8. 避免阻塞 CSS/JS

在屏幕上繪製第一個像素以前,瀏覽器必須確保 CSS 已經下載完整。儘管瀏覽器的預處理器很智能,會盡早請求整個頁面所須要的 CSS,可是把 CSS 資源請求放在頁面靠前仍然是種最佳實踐,具體位置是在文檔的 head 標籤裏,並且要在任何 JS 或圖片被請求和處理以前。

默認狀況下,若是在 HTML 中定位了 JS,它就會被請求、解析,而後執行。在瀏覽器處理完這個 JS 以前,會阻止其後任何資源的下載渲染。然而大多數時候,這種默認的阻塞行爲致使了沒必要要的延遲,甚至會形成單點故障。爲了減輕 JS 阻塞帶來的潛在影響,下面針對己方資源(你能控制的)和第三方資源(你不能控制的)推薦了不一樣的策略

  1. 按期校驗這些資源的使用狀況。隨着時間的變遷,Web 頁面可能會持續下載一些再也不須要的 JS,這時最好去掉它。
  2. 若是 JS 執行順序可有可無,而且必須在 onload 事件觸發以前運行,那麼能夠設置 async 屬性,像這樣:

    <script async src="/js/myfile.js">

    只需作到下載 JS 與解析 HTML 並行,就能極大地提高總體用戶體驗。慎用 document.write 指令,由於極可能中斷頁面執行,因此須要仔細測試。

  3. 若是 JS 執行順序很重要,而且你也能承受腳本在 DOM 加載完以後運行,那麼請使用 defer 屬性。像這樣

    <script defer src="/js/myjs.js">
  4. 對不會影響到頁面初次展現的 JS 腳本,必須在 onload 事件觸發以後請求(處理)它。
  5. 若是你不想延遲主頁面的 onload 事件,能夠考慮經過 iframe 獲取 JS,由於它的處理獨立於主頁面。可是,經過 iframe 下載的 JS 訪問不了主頁面上的元素。
9. 圖片優化

對大多數網站而言,圖片的重要性和比重在不斷增長。既然圖片主導了多數現代網站,優化它們就可以得到最大的性能回報。全部圖片優化手段的目標都是在達到指定視覺質量的前提下傳輸最少的字節。

  1. 圖片元信息,例如題材地理位置信息、時間戳、尺寸和像素信息,一般包含在二進制數據裏,應該在發送給客戶端以前去掉(務必保留版權和色彩描述信息)。這種無損處理可以在圖片生成時完成。對於 PNG 圖片,通常會節省大概 10% 的空間。
  2. 圖片過載(image overloading)是指,圖片最終被瀏覽器自動縮小,要麼由於原始尺寸超過了瀏覽器可視區中的佔位大小,要麼由於像素超過設備的顯示能力。這不只浪費帶寬,消耗的 CPU 資源也很可觀,這些計算資源有時在手持設備上至關寶貴。想要解決圖片過載,能夠使用技術手段,針對用戶的設備、網絡情況和預期的視覺質量,提供裁剪過的圖片(就尺寸和質量而言)。

3.2.2 反模式

HTTP/2 對每一個域名只會開啓一個鏈接,因此 HTTP/1.1 下的一些訣竅對它來講只會拔苗助長。

詳細看 6.7 節

3.3 小結

HTTP/1.1 孕育了各類性能優化手段與訣竅,能夠幫助咱們深刻理解 Web 及其內部實現。HTTP/2 的目標之一就是淘汰掉衆多(並非所有)此類訣竅。

4. HTTP/2 遷移

在升級到 HTTP/2 以前,你應該考慮:

  1. 瀏覽器對 h2 的支持狀況
  2. 遷移到 TLS(HTTPS)的可能性
  3. 對你的網站作基於 h2 的優化(可能對 h1 有副作用)
  4. 網站上的第三方資源
  5. 保持對低版本客戶端的兼容

4.1 瀏覽器的支持狀況

任何不支持 h2 的客戶端都將簡單地退回到 h1,並仍然能夠訪問你的站點基礎設施。

4.2 遷移到 TLS

全部主流瀏覽器只能訪問基於 TLS(即 HTTPS 請求)的 h2。

4.3 撤銷針對 HTTP/1.1 的優化

Web 開發者以前花費了大量心血來充分使用 h1,而且已經總結了一些訣竅,例如資源合併、域名拆分、極簡化、禁用 cookie 的域名、生成精靈圖,等等。因此,當得知這些實踐中有些在 h2 下變成反模式時,你可能會感到吃驚。例如,資源合併(把不少 CSS/JS 文件拼合成一個)能避免瀏覽器發出多個請求。對 h1 而言這很重要,由於發起請求的代價很高;可是在 h2 的世界裏,這部分已經作了深度優化。放棄資源合併的結果多是,針對單個資源發起請求的代價很低,但瀏覽器端能夠進行更細粒度的緩存。

詳細看 6.7 節

5. HTTP/2 協議

本章將全面探討 HTTP/2 的底層工做原理,深刻到數據層傳輸的幀及其通訊方式。

5.1 HTTP/2 分層

HTTP/2 大體能夠分爲兩部分

  1. 分幀層 即 h2 多路複用能力的核心部分,主要目的仍是傳輸 HTTP
  2. 數據或 http 層 其中包含傳統上被認爲是 HTTP 及其關聯數據的部分,向後兼容 HTTP/1.1

h2 有些特色須要關注一下:

  1. 二進制協議 :h2 的分幀層是基於幀的二進制協議,這方便了機器解析,可是肉眼識別比較困難
  2. 首部壓縮 :僅使用二進制協議還不夠,h2 的首部還會被深度壓縮。這將顯著減小傳輸中的冗餘字節
  3. 多路複用 :在調試工具裏查看基於 h2 傳輸的鏈接的時候,你會發現請求和響應交織在一塊兒
  4. 加密傳輸 :線上傳輸的絕大部分數據是加密過的,因此在中途讀取會更加困難

5.2 鏈接

與徹底無狀態的 h1 不一樣的是,h2 把它所承載的幀(frame)和流(stream)共同依賴的鏈接層元素捆綁在一塊兒,其中既包含鏈接層設置也包含首部表。也就是說,與以前的 HTTP 版本不一樣,每一個 h2 鏈接都有必定的開銷。之因此這麼設計,是考慮到收益遠遠超過其開銷。

在鏈接的時候,HTTP/2 提供兩種協議發現的機制:

  1. 在鏈接不加密的狀況下,客戶端會利用 Upgrade 首部來代表指望使用 h2。若是服務器也能夠支持 h2,它會返回一個101 Switching Protocols(協議轉換)響應。這增長了一輪完整的請求-響應通訊
  2. 若是鏈接基於 TLS,狀況就不一樣了。客戶端在 ClientHello 消息中設置 ALPN (Application-Layer Protocol Negotiation,應用層協議協商)擴展來代表指望使用 h2 協議,服務器用一樣的方式回覆。若是使用這種方式,那麼 h2 在建立 TLS 握手的過程當中完成協商,不須要多餘的網絡通訊。
在協議制定過程當中,很早就把小數點去掉了,這代表將來的 HTTP 版本不能保證語義的向後兼容,也就是說只有 HTTP/2 沒有 HTTP/2.0、HTTP/2.2

5.3 幀

HTTP/2 是基於幀(frame)的協議,採用分幀是爲了將重要信息都封裝起來,讓協議的解析方能夠輕鬆閱讀、解析並還原信息。 相比之下,h1 不是基於幀的,而是以文本分隔。因此解析 h1 的請求或響應可能會出現如下問題:

  1. 一次只能處理一個請求或響應,完成以前不能中止解析
  2. 沒法預判解析須要多少內存。這會帶來一系列問題:你要把一行讀到多大的緩衝區裏;若是行太長會發生什麼;應該增長並從新分配內存,仍是返回 400 錯誤

從另外一方面來講,有了幀,處理協議的程序就能預先知道會收到什麼。下圖是一個 HTTP/2 幀的結構

HTTP/2 幀結構

前 9 個字節對於每一個幀是一致的。解析時只須要讀取這些字節,就能夠準確地知道在整個幀中指望的字節數。其中每一個字段的說明以下

名稱 長度 描述
Length 3 字節 表示幀負載的長度(取值範圍爲 2^14~2^24-1 字節)。 請注意,214 字節是默認的最大幀大小,若是須要更大的幀,必須在 SETTINGS 幀中設置
Type 1 字節 當前幀類型
Flags 1 字節 具體幀類型的標識
R 1 位 保留位,不要設置,不然可能帶來嚴重後果
Stream Identifier 31 位 每一個流的惟一 ID
Frame Payload 長度可變 真實的幀內容,長度是在 Length 字段中設置的

相比依靠分隔符的 h1,h2 還有另外一大優點:若是使用 h1 的話,你須要發送完上一個請求或者響應,才能發送下一個;因爲 h2 是分幀的,請求和響應能夠交錯甚至多路複用。多路複用有助於解決相似隊頭阻塞的問題。

h2 有十種不一樣的幀類型:

名稱 ID (Type) 描述
DATA 0x0 數據幀,傳輸流的核心內容
HEADERS 0x1 報頭幀,包含 HTTP 首部,和可選的優先級參數
PRIORITY 0x2 優先級幀,指示或者更改流的優先級和依賴
RST_STREAM 0x3 流終止幀,容許一端中止流(一般因爲錯誤致使的)
SETTINGS 0x4 設置幀,協商鏈接級參數
PUSH_PROMISE 0x5 推送幀,提示客戶端,服務器要推送些東西
PING 0x6 PING 幀,測試鏈接可用性和往返時延(RTT)
GOAWAY 0x7 GOAWAY 幀,告訴另外一端,當前端已結束
WINDOW_UPDATE 0x8 窗口更新幀,協商一端將要接收多少字節(用於流量控制)
CONTINUATION 0x9 延續幀,用以擴展 HEADER 數據塊

擴展幀 :HTTP/2 內置了名爲擴展幀的處理新的幀類型的能力。依靠這種機制,客戶端和服務器的實現者能夠實驗新的幀類型,而無需制定新協議。按照規範,任何客戶端不能識別的幀都會被丟棄,因此網絡上新出現的幀就不會影響核心協議。固然,若是你的應用程序依賴於新的幀,而中間代理會丟棄它,那麼可能會出現問題。

5.4 流

HTTP/2 規範中的流(stream):HTTP/2 鏈接上獨立的、雙向的幀序列交換。你能夠將流看做在鏈接上的一系列幀,用來傳輸一對請求/響應消息。若是客戶端想要發出請求,它會開啓一個新的流,服務器也在這個流上回復。這與 h1 的請求、響應流程相似,區別在於,由於有分幀,因此多個請求和響應能夠交錯,而不會互相阻塞。流 ID(幀首部的第 6~9 字節)用來標識幀所屬的流。

客戶端到服務器的 h2 鏈接創建以後,經過發送 HEADERS 幀來啓動新的流,若是首部須要跨多個幀,可能還發會送 CONTINUATION 幀。

5.4.1 消息

HTTP 消息泛指 HTTP 請求或響應。一個消息至少由 HEADERS 幀(用來初始化流)組成,而且能夠另外包含 CONTINUATIONDATA 幀,以及其餘的 HEADERS 幀。 下圖是普通 GET 請求的示例流程

普通 GET 請求的示例流程

POST 和 GET 的主要差異之一就是 POST 請求一般包含客戶端發出的大量數據。下圖是 POST 消息對應的各幀可能的樣子

普通 POST 請求的示例流程

h1 的請求和響應都分紅消息首部和消息體兩部分;與之相似,h2 的請求和響應分紅 HEADERS 幀和 DATA 幀。HTTP/1 和 HTTP/2 消息的下列差異是須要注意

  1. 一切都是header :h1 把消息分紅請求/狀態行、首部兩部分。h2 取消了這種區分,並把這些行變成了魔法僞首部
  2. 沒有分塊編碼(chunked encoding) :只有在沒法預先知道數據長度的狀況下向對方發送數據時,纔會用到分塊。在使用幀做爲核心協議的 h2 裏,再也不須要分塊
  3. 再也不有101的響應 :Switching Protocol 響應是 h1 的邊緣應用。它現在最多見的應用可能就是用以升級到 WebSocket 鏈接。ALPN 提供了更明確的協議協商路徑,往返的開銷也更小

5.4.2 流量控制

h2 的新特性之一是基於流的流量控制。h1 中只要客戶端能夠處理,服務端就會盡量快地發送數據,h2 提供了客戶端調整傳輸速度的能力,服務端也一樣能夠調整傳輸的速度。

WINDOW_UPDATE 幀用於執行流量控制功能,能夠做用在單獨某個流上(指定具體 Stream Identifier)也能夠做用整個鏈接 (Stream Identifier 爲 0x0),只有 DATA 幀受流量控制影響。初始化流量窗口後,發送多少負載,流量窗口就減小多少,若是流量窗口不足就沒法發送,WINDOW_UPDATE 幀能夠增長流量窗口大小。流創建的時候,窗口大小默認 65535(2^16-1)字節。

5.4.3 優先級

流的最後一個重要特性是依賴關係。

現代瀏覽器會盡可能以最優的順序獲取資源,由此來優化頁面性能。在沒有多路複用的時候,在它能夠發出對新對象的請求以前,須要等待前一個響應完成。有了 h2 多路複用的能力,客戶端就能夠一次發出全部資源的請求,服務端也能夠當即着手處理這些請求。由此帶來的問題是,瀏覽器失去了在 h1 時代默認的資源請求優先級策略。假設服務器同時接收到了 100 個請求,也沒有標識哪一個更重要,那麼它將幾乎同時發送每一個資源,次要元素就會影響到關鍵元素的傳輸。

h2 經過流的依賴關係來解決上面這個問題。經過 HEADERS 幀和 PRIORITY 幀,客戶端能夠明確地和服務端溝通它須要什麼,以及它須要這些資源的順序。這是經過聲明依賴關係樹和樹裏的相對權重實現的。

  1. 依賴關係 爲客戶端提供了一種能力,經過指明某些對象對另外一些對象有依賴,告知服務器這些對象應該優先傳輸
  2. 權重 讓客戶端告訴服務器如何肯定具備共同依賴關係的對象的優先級

流能夠被標記爲依賴其餘流,所依賴的流完成後再處理當前流。每一個依賴 (dependency) 後都跟着一個權重 (weight),這一數字是用來肯定依賴於相同的流的可分配可用資源的相對比例。

其餘時候也能夠經過 PRIORITY 幀調整流優先級。

設置優先級的目的是爲了讓端點表達它所指望對端在併發的多個流之間如何分配資源的行爲。更重要的是,當發送容量有限時,能夠使用優先級來選擇用於發送幀的流。

5.5 服務端推送

升單個對象性能的最佳方式,就是在它被用到以前就放到瀏覽器的緩存裏面。這正是 h2 服務端推送的目的。

5.5.1 推送對象

若是服務器決定要推送一個對象(RFC 中稱爲『推送響應』),會構造一個 PUSH_PROMISE 幀。這個幀有不少重要屬性:

  1. PUSH_PROMISE 幀首部中的流 ID (Promised Stream ID)用來響應相關聯的請求。推送的響應必定會對應到客戶端已發送的某個請求。若是瀏覽器請求一個主體 HTML 頁面,若是要推送此頁面使用的某個 JavaScript 對象,服務器將使用請求對應的流 ID 構造 PUSH_PROMISE 幀。
  2. PUSH_PROMISE 幀的首部塊與客戶端請求推送對象時發送的首部塊是類似的。因此客戶端有辦法放心檢查將要發送的請求。
  3. 被髮送的對象必須確保是可緩存的。
  4. :method 首部的值必須確保安全。安全的方法就是冪等的那些方法,這是一種不改變任何狀態的好辦法。例如,GET 請求被認爲是冪等的,由於它一般只是獲取對象,而 POST 請求被認爲是非冪等的,由於它可能會改變服務器端的狀態。
  5. 理想狀況下,PUSH_PROMISE 幀應該更早發送,應當早於客戶端接收到可能承載着推送對象的 DATA 幀。假設服務器要在發送 PUSH_PROMISE 以前發送完整的 HTML,那客戶端可能在接收到 PUSH_PROMISE 以前已經發出了對這個資源的請求。h2 足夠健壯,能夠優雅地解決這類問題,但仍是會有些浪費。
  6. PUSH_PROMISE 幀會指示將要發送的響應所使用的流 ID

若是客戶端對 PUSH_PROMISE 的任何元素不滿意,就能夠按照拒收緣由選擇重置這個流(使用 RST_STREAM),或者發送 PROTOCOL_ERROR (在 GOAWAY 幀中)。常見的狀況是緩存中已經有了這個對象。

假設客戶端不拒收推送,服務端會繼續進行推送流程,用 PUSH_PROMISE 中指明 ID 對應的流來發送對象

服務端推送消息處理

5.5.2 選擇要推送的資源

若是服務器接收到一個頁面的請求,它須要決定是推送頁面上的資源仍是等客戶端來請求。決策的過程須要考慮到以下方面

  1. 資源已經在瀏覽器緩存中的機率
  2. 從客戶端看來,這些資源的優先級 (參見 5.4.3 節)
  3. 可用的帶寬,以及其餘相似的會影響客戶端接收推送的資源

若是服務器選擇正確,那就真的有助於提高頁面的總體性能,反之則會損耗頁面性能。

5.6 首部壓縮

現代網頁平均有不少請求,這些請求之間幾乎沒有新的的內容,這是極大的浪費。

首部列表 (Header List) 是零個或多個首部字段 (Header Field) 的集合。當經過鏈接傳送時,首部列表經過壓縮算法(即下文 HPACK) 序列化成首部塊 (Header Block),不用 GZIP 是由於它有泄漏加密信息的風險。HPACK 是種表查找壓縮方案,它利用霍夫曼編碼得到接近 GZIP 的壓縮率。

而後,序列化的首部塊又被劃分紅一個或多個叫作首部塊片斷 (Header Block Fragment) 的字節序列,並經過 HEADERSPUSH_PROMISE,或者 CONTINUATION 幀進行有效負載傳送。

假設客戶端按順序發送以下請求首部:

Header1: foo
Header2: bar
Header3: bat

當客戶端發送請求時,能夠在首部數據塊中指示特定首部及其應該被索引的值。它會建立一張表:

索引 首部名稱
62 Header1 foo
63 Header2 bar
64 Header3 bat

若是服務端讀到了這些請求首部,它會照樣建立一張表。客戶端發送下一個請求的時候, 若是首部相同,它能夠直接發送:62 63 64 ,服務器會查找先前的表格,並把這些數字還原成索引對應的完整首部。首部壓縮機制中每一個鏈接都維護了本身的狀態。

HPACK 的實現比這個要複雜得多,好比:

  1. 請求端和響應端各維護了兩張表格。其中之一是動態表,建立方法和上面差不 多。另外一張是靜態表,它由 61 個最多見的首部的鍵值組合而成。例如 :method: GET 在靜態表中索引爲 2。按規定,靜態表包含 61 個條目,因此上例索引編號從 62 開始。
  2. 關於字段如何索引,有不少控制規則:

    1. 發送索引編號和文本值僅發送文本值,不對它們進行索引(對於一次性或敏感首部)
    2. 發送索引的首部名,值用文本表示,但不進行索引處理(如:path: /foo.html,其值每次都不一樣)
    3. 發送索引過的首部名和值(如上例中的第二個請求)
  3. 使用打包方案的整數壓縮,以實現極高的空間效率
  4. 利用霍夫曼編碼表進一步壓縮字符串

5.7 線上傳輸

線上傳輸的 h2 信息是通過壓縮的二進制數據。

一個簡單的GET請求

下面是一個簡單的 h2 的 get 請求

:authority: www.akamai.com
:method: GET
:path: /
:scheme: https
accept: text/html,application/xhtml+xml,...
accept-language: en-US,en;q=0.8
cookie: sidebar_collapsed=0; _mkto_trk=...
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Macintosh;...

下面是 h2 的一個響應

:status: 200
cache-control: max-age=600
content-encoding: gzip
content-type: text/html;charset=UTF-8
date: Tue, 31 May 2016 23:38:47 GMT
etag: "08c024491eb772547850bf157abb6c430-gzip"
expires: Tue, 31 May 2016 23:48:47 GMT
link: <https://c.go-mpulse.net>;rel=preconnect
set-cookie: ak_bmsc=8DEA673F92AC...
vary: Accept-Encoding, User-Agent
x-akamai-transformed: 9c 237807 0 pmb=mRUM,1
x-frame-options: SAMEORIGIN

在這個響應中,服務器表示請求已成功受理(狀態碼 200),設置了 cookie(cookie 首部),表示返回的內容使用 gzip 壓縮(content-encoding 首部)

6. HTTP/2性能

HTTP/2 大部分狀況下傳輸 web 頁面比 HTTP/1.1 快。

  1. 對於包含不少小型資源的頁面,h2 加載頁面的時間比 h1 更短。這是由於 h1 下(有 6 個 TCP 鏈接)服務器只能並行發送 6 個資源(因爲隊頭阻塞),而 h2 下多個流(stream)能夠共用鏈接。進一步說,隨着網絡條件變差,h1 和 h2 下頁面加載時間(PLT)都會增長;可是 h2 因爲單鏈接,若是惟一的鏈接發生了丟包,全部工做都會受影響。
  2. 對於包含少許大型資源的頁面,在全部網絡條件下,h1 性能上都比 h2 表現要好。這個多少使人感到意外的結果是初始擁塞窗口致使的。若是開啓 6 個鏈接,h1 的初始擁塞窗口大小其實是 h2 的 6 倍。所以在會話開始階段,h2 的鏈接窗口還沒有增加到最佳值,但 h1 早就能更快地傳輸更多數據了。這個問題目前仍在解決,由於它致使初始擁塞窗口對 h2 而言過小,然而對 h1 而言又太大。 此外,h2 比 h1 更容易受丟包的影響。
  3. 對於包含一些極大型資源的 Web 頁面,二者沒有任何差別。h2 的初始擁塞窗口劣勢被總體下載時長掩蓋了,多路複用此時也再也不具備優點。

6.1 客戶端實現

網絡條件相同,使用不一樣瀏覽器客戶端,一樣的網站頁面加載性能可能差異很大。

  1. 協議的具體實現很重要
  2. 並不是全部請求在任何狀況下都會從 HTTP/2 受益,即使如此,URL 使用 h2 後性能提高的比例也依舊高於降低的比例

6.2 延遲

延遲是指數據包從一個端點到另外一個端點所花的時間。有時,它也表示數據包到達接收方而後返回發送方所需的時間,又稱爲往返時延(RTT),長度通常以毫秒計。

影響延遲的因素衆多,但有兩個是最重要的:端點間的距離,以及所用傳輸介質

兩點之間的網線不會是筆直的,另外各類網關、路由器、交換機以及移動基站等(也包括服務器應用自己)都會增長延遲

6.3 丟包

若是網絡中傳輸的數據包沒有成功到達目的地,就會發生丟包,這一般是由網絡擁堵形成的。

頻繁丟包會影響 h2 的頁面傳輸,主要是由於 h2 開啓單一 TCP 鏈接,每次有丟包/擁堵時,TCP 協議就會縮減 TCP 窗口。

若是 TCP 流上丟了一個數據包,那麼整個 h2 鏈接都會停頓下來,直到該數據包重發並被接收到。

6.4 服務端推送

服務端推送讓服務器具有了在客戶端請求以前就推送資源的能力。 測試代表,若是合理使用推送,頁面渲染時間能夠減小 20%~50%。

然而,推送也會浪費帶寬,這是由於服務端可能試圖推送那些在客戶端已經緩存的資源,致使客戶端收到並不須要的數據。客戶端確實能夠發送 RST_STREAM 幀來拒絕服務器的 PUSH_PROMISE 幀,可是 RST_STREAM 並不會即刻到達,因此服務器仍是會發送一些多餘的信息。

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

理想狀況下,服務端正在處理 HTML 頁面主體請求時纔會發起推送。有時候,服務端須要作一些後臺工做來生成 HTML 頁面。這時候服務端在忙,客戶端卻在等待,這正是開始向客戶端推送所需資源的絕佳時機。

在後臺處理的同時進行推送

6.5 首字節時間

首字節時間(TTFB)用於測量服務器的響應能力。是從客戶端發起 HTTP 請求到客戶端瀏覽器收到資源的第一個字節所經歷的時間。由 socket 鏈接時間、發送 HTTP 請求所需時間、收到頁面第一個字節所需時間組成。

h1 中,客戶端針對單個域名在每一個鏈接上依次請求資源,並且服務器會按序發送這些資源。客戶端只有接收了以前請求的資源,纔會再請求剩下的資源,服務器接着繼續響應新的資源請求。這個過程會一直重複,直到客戶端接收完渲染頁面所需的所有資源。

與 h1 不一樣,經過 h2 的多路複用,客戶端一旦加載了 HTML,就會向服務器並行發送大量請求。相比 h1,這些請求得到響應的時間之和通常會更短;可是由於是請求是同時發出的,而單個請求的計時起點更早,因此 h2 統計到的 TTFB 值會更高。

HTTP/2 比 h1 確實作了更多的工做,其目的就是爲了從整體上提高性能。下面是一些 h1 沒有,但 h2 實現了的

  1. 窗口大小調節
  2. 依賴樹構建
  3. 維持首部信息的靜態 / 動態表
  4. 壓縮 / 解壓縮首部
  5. 優先級調整(h2 容許客戶端屢次調整單一請求的優先級)
  6. 預先推送客戶端還沒有請求的數據流

下圖是使用 h1 和 h2 加載同一個頁面的加載時序對比,整體來講 h2 體驗更好

h1 與 h2 請求的時間序列

6.6 第三方資源

許多網站會使用各類統計、跟蹤、社交以及廣告平臺,就會引入各類第三方的資源。

  1. 第三方請求每每經過不一樣域名發送;因爲瀏覽器須要解析 DNS、創建 TCP 鏈接、協商 TLS,這將嚴重影響性能;
  2. 由於第三方資源在不一樣域名下,因此請求不能從服務端推送、資源依賴、請求優先級等 h2 特性中受益。這些特性僅是爲請求相同域名下的資源設計的;
  3. 你沒法控制第三方資源的性能,也沒法決定它們是否會經過 h2 傳輸;

6.7 HTTP/2反模式

h1 下的一些性能調優辦法在 h2 下會起到副作用。下面列出了一些用於優化 h1 請求的經常使用技巧,並標註了 h2 方面的考慮。

名稱 描述 備註
資源合併 把多個文件(JavaScript、CSS) 合成一個文件,以減小 HTTP 請求 在 HTTP/2 下這並不是必要,由於請求的傳輸字節數和時間成本更低,雖然這種成本仍然存在
極簡化 去除 HTML、JavaScript、CSS 這類文件中無用的代碼 很棒的作法,在 HTTP/2 下也要保留
域名拆分 把資源分佈到不一樣的域名上面去,讓瀏覽器利用更多的 socket 鏈接 HTTP/2 的設計意圖是充分利用單個 socket 鏈接,而拆分域名會違背這種意圖。建議取消域名拆分,但請注意本表格以後的附註框會介紹這個問題相關的各類複雜狀況
禁用 cookie 的域名 爲圖片之類的資源創建單獨的域名,這些域名不用 cookie,以儘量減小請求尺寸 應該避免爲這些資源單獨設立域名(參見域名拆分),但更重要的是,因爲 HTTP/2 提供了首部壓縮,cookie 的開銷會顯著下降
生成精靈圖 把多張圖片拼合爲一個文件,使用 CSS 控制在 Web 頁面上展現的部分 與極簡化相似,只不過用 CSS 實現這種效果的代價高昂;不推薦在 HTTP/2 中使用

6.7.1 生成精靈圖和資源合併/內聯

精靈圖(spriting)是指把不少小圖片拼合成一張大圖,這樣只需發起一個請求就能夠覆蓋多個圖片元素。在 HTTP/2 中,針對特定資源的請求再也不是阻塞式的,不少請求能夠並行處理;就性能而言,生成精靈圖已失去意義,由於多路複用和首部壓縮去掉了大量的請求開銷。

與之相似,小的文本資源,例如 JS 和 CSS,會依照慣例合併成一份更大的資源,或者直接內嵌在主體 HTML 中,這也是爲了減小客戶端-服務器鏈接數。這種作法有個問題是,那些小的 CSS 或 JS 自身也許可緩存,但若是它們內嵌在不可緩存的 HTML 中的話,固然也就不可緩存了。把不少小的 JS 腳本合併成一個大文件可能仍舊對 h2 有意義,由於這樣能夠更好地壓縮處理並節省 CPU。

6.7.2 域名拆分

域名拆分(sharding)是爲了利用瀏覽器針對每一個域名開啓多個鏈接的能力來並行下載資源。對於包含大量小型資源的網站,廣泛的作法是拆分域名,以利用現代瀏覽器針能對每一個域名開啓 6 個鏈接的特性,充分利用可用帶寬。

由於 HTTP/2 採起多路複用,因此域名拆分就不是必要的了,而且反而會讓協議力圖實現的目標落空。比較好的辦法就是繼續保持當前的域名拆分,可是確保這些域名共享同一張證書 [ 通配符 / 存儲區域網絡(SAN)],並保持服務器 IP 地址和端口相同,以便從瀏覽器網絡歸併(network coalescence)中收益,這樣能夠節省爲單個域名鏈接創建的時間。

6.7.3 禁用cookie的域名

在 HTTP/1 下,請求和響應首部從不會被壓縮。隨着時間推移,首部大小已經增加了,超過單個 TCP 數據包的 cookie 能夠說司空見慣。所以,在內容源和客戶端之間來回傳輸首部信息的開銷可能形成明顯的延遲。

所以,對圖片之類不依賴於 cookie 的資源,設置禁用 cookie 的域名是個合理的建議。

可是 HTTP/2 中,首部是被壓縮的,而且客戶端和服務器都會保留『首部歷史』,避免重複傳輸已知信息。因此,若是你要重構站點,大可沒必要考慮禁用 cookie 的域名,這樣能減小不少包袱。

靜態資源也應該從同一域名提供;使用與主頁面 HTTP 相同的域名,消除了額外的 DNS 查詢以及(潛在的)socket 鏈接,它們都會減慢靜態資源的獲取。把阻塞渲染的資源放在一樣的域名下,也能夠提高性能。

6.7.4 資源預取

資源預取也是一項 Web 性能優化手段,它提示瀏覽器只要有可能就繼續下載可緩存資源,並把這些資源緩存起來。儘管如此,若是瀏覽器很忙,或者資源下載花的時間太 長,預取請求將會被忽略。資源預取能夠在 HTML 中插入 link 標籤實現:

<link rel="prefetch" href="/important.css">

也能夠使用 HTTP 響應中的 Link 首部: Link: </important.css>; rel=prefetch

資源預取與 h2 引入的服務端推送並沒多少關聯。服務端推送用於讓資源更快到達瀏覽器, 而資源預取相比推送的優勢之一是,若是資源已經在緩存裏,瀏覽器就不會浪費時間和帶寬重複請求它。因此,能夠把它看做 h2 推送的補充工具,而不是將被替代的特性。

6.8 現實狀況中的性能

網絡丟包是 h2 的命門,一次丟包機會就會讓它的全部優化泡湯。

7. HTTP/2 實現

7.1 桌面Web瀏覽器

全部瀏覽器在進行 HTTP/2 傳輸時都須要使用 TLS(HTTPS),即便事實上HTTP/2 規範自己並無強制要求 TLS。這個緣由是:

  1. 從以前對 WebSocket 和 SPDY 的實驗看來,使用 Upgrade 首部,經過 80 端口(明文的 HTTP 端口)通訊時,通訊鏈路上代理服務器的中斷等因素會致使很是高的錯誤率。若是基於 443 端口(HTTPS 端口)上的 TLS 發起請求,錯誤率會顯著下降,而且協議通訊也更簡潔。
  2. 人們愈來愈相信,考慮到安全和隱私,一切都應該被加密。HTTP/2 被視爲一次推進全網加密通訊發展的機會。

7.1.2 禁用HTTP/2

HTTP/2 畢竟是新鮮事物,如今不少瀏覽器都支持啓用或禁用 h2。

7.1.3 支持 HTTP/2 服務端推送

服務端推送是 h2 中最使人興奮也最難正確使用的特性之一,如今全部的主流瀏覽器都已經支持了此特性。

7.1.4 鏈接歸併

若是須要創建一個新鏈接,而瀏覽器支持鏈接歸併,那麼經過複用以前已經存在的鏈接,就可以提高請求性能。這意味着能夠跳過 TCP 和 TLS 的握手過程,改善首次請求新域名的性能。若是瀏覽器支持鏈接歸併,它會在開啓新鏈接以前先檢查是否已經創建了到相同目的地的鏈接。

相同目的地具體指的是:已經存在鏈接,其證書對新域名有效,此域名能夠被解析成那個鏈接對應的 IP 地址。若是上述條件都知足,那麼瀏覽器會在已創建的鏈接上向該域名發起 HTTP/2 請求。

7.2 服務器、代理以及緩存

若是要經過 h2 傳輸內容,咱們有幾個選擇。支持 HTTP/2 的網絡設施大體有如下兩類。

Web服務器 :一般所說的提供靜態和動態內容服務的程序。

代理/緩存 :通常處在服務器和最終用戶之間,能夠提供緩存以減輕服務器負載,或進行額外加工。許多代理也能扮演 Web 服務器的角色。

在選擇 HTTP/2 服務器時,咱們須要檢查、評估一些關鍵點。除了基本的通用性能、操做系統支持、學習曲線、可擴展性以及穩定性,還應當關注 Web 請求的依賴項優先級,以及對服務端推送的支持。

7.3 內容分發網絡 CDN

內容分發網絡(CDN)是反向代理服務器的全球性分佈式網絡,它部署在多個數據中心。CDN 的目標是經過縮短與最終用戶的距離來減小請求往返次數,以此爲最終用戶提供高可用、高性能的內容服務。

大多數主流 CDN 是支持 HTTP/2 的,選擇 CDN 時主要考慮的兩點是:對服務端推送的支持,以及它們處理優先級的方式。這兩點對現實世界中的 Web 性能影響重大。

8. HTTP/2調試

8.1 chrome devtools 可視化

Chrome 開發者工具中的 Network 欄,有助於簡單直觀地跟蹤客戶端和服務端的通信,它 按下面表格的形式展現了若干信息:

  1. 資源名
  2. 資源大小
  3. 狀態碼
  4. 優先級
  5. 總加載時間
  6. 使用時間線方式分解加載時間

打開 devtools 的 Network 欄,鼠標放在瀑布流 Waterfall 的資源上,就會看到資源加載過程當中各個階段的詳細時間

  1. Connection Setup (鏈接設置)

    1. Queueing :請求被渲染引擎或者網絡層延遲的時間,瀏覽器在如下狀況下對請求排隊

      • 存在更高優先級的請求。
      • 此源已打開六個 TCP 鏈接,達到限值。 僅適用於 HTTP/1.0 和 HTTP/1.1
      • 瀏覽器正在短暫分配磁盤緩存中的空間
  2. Connection Start (開始鏈接階段)

    1. Stalled :請求可能會因 Queueing 中描述的任何緣由而中止
    2. Proxy negotiation :瀏覽器與代理服務器協商請求花費的時間
    3. DNS Lookup :瀏覽器解析請求的 IP 地址花費的時間
  3. Request/Response (請求 / 響應)

    1. Request Sent :發送請求包含的數據花費的時間
    2. Waiting (TTFB) :等待初始響應花費的時間,也就是所說的首字節時間;這個數字包括等待服務器傳輸響應的時間,以及往返服務器的延遲
    3. Content Download :接收響應的數據所花費的時間
  4. Explanation (總時間)
  5. 其餘

    1. ServiceWorker Preparation :瀏覽器正在啓動 Service Worker
    2. Request to ServiceWorker :正在將請求發送到 Service Worker
    3. Receiving Push :瀏覽器正在經過 HTTP/2 服務器推送接收此響應的數據
    4. Reading Push :瀏覽器正在讀取以前收到的本地數據

9. 展望將來

HTTP/2 的弱點之一就是依賴主流 TCP 實現。在 3.1.3 節中已經討論過,TCP 鏈接受制於 TCP 慢啓動、擁塞規避,以及不合理的丟包處理機制。用單個連接承載頁面涉及的全部資源請求,就能享受多路複用帶來的好處;然而面對 TCP 層級的隊首阻塞時,咱們仍是一籌莫展。因此 Google 開發的 QUIC 採納了 HTTP/2 的優勢,而且避免了這些缺點。


網上的帖子大多深淺不一,甚至有些先後矛盾,在下的文章都是學習過程當中的總結,若是發現錯誤,歡迎留言指出~

推介閱讀:

  1. HTTP2 詳解 | Wangriyu’s Blog

PS:歡迎你們關注個人公衆號【前端下午茶】,一塊兒加油吧~

另外能夠加入「前端下午茶交流羣」微信羣,長按識別下面二維碼便可加我好友,備註加羣,我拉你入羣~

相關文章
相關標籤/搜索