HTTP/1 已經支撐咱們走到今天,可是現代 Web 應用的需求迫使咱們關注其設計缺陷。下面是它面臨的一些比較重要的問題;這天然也是設計 HTTP/2 要解決的核心問題。瀏覽器
並無「HTTP/1」這種專業術語;此處這一用法(還有 h1),是對 HTTP/1.0 (RFC 1945)和 HTTP/1.1(RFC 2616)的簡稱。
瀏覽器不多隻從一個域名獲取一份資源。大多數時候,它但願能同時獲取許多資源。設想這樣一個網站,它把全部圖片放在單個特定域名下。HTTP/1 並未提供機制來同時請求這些資源。若是僅僅使用一個鏈接,它須要發起請求、等待響應,以後才能發起下一個請求。 服務器
h1 有個特性叫 管道化(pipelining)
,容許一次發送一組連續的請求,而不用等待應答返回。這樣能夠避免鏈接延遲。可是該特性只能按照發送順序依次接收響應。並且,管道化備受互操做性和部署的各類問題的困擾,基本沒有實用價值。cookie
在請求應答過程當中,若是出現任何情況,剩下全部的工做都會被阻塞在那次請求應答以後。這就是 隊頭阻塞 (Head-of-line blocking或縮寫爲HOL blocking),它會阻礙網絡傳輸和 Web 頁面渲染,直至失去響應。網絡
爲了防止這種問題,現代瀏覽器會針對單個域名開啓 6 個鏈接,經過各個鏈接分別發送請求。它實現了某種程度上的並行,可是每一個鏈接仍會受到 隊頭阻塞 的影響。另外,這也沒有高效利用有限的設備資源。併發
傳輸控制協議(TCP)
的設計思路是:對假設狀況很保守,並可以公平對待同一網絡的不一樣流量的應用。它的避免擁塞機制被設計成即便在最差的網絡情況下仍能起做用,而且若是有需求衝突也保證相對公平。這是它取得成功的緣由之一。socket
它的成功並非由於傳輸數據最快,而是由於它是最可靠的協議之一,涉及的核心概念就是 擁塞窗口(congestion window)
。擁塞窗口是指,在接收方確認數據包以前,發送方能夠發出的 TCP 包的數量。 例如,若是擁塞窗口指定爲 1,那麼發送方發出 1 個數據包以後,只有接收方確認了那個包,才能發送下一個。性能
通常來說,每次發送一個數據包並非很是低效。TCP 有個概念叫 慢啓動(Slow Start), 它用來探索當前鏈接對應擁塞窗口的合適大小。慢啓動的設計目標是爲了讓新鏈接搞清楚當前網絡情況,避免給已經擁堵的網絡繼續添亂。它容許發送者在收到每一個確認回覆後額外發送 1 個未確認包。這意味着新鏈接在收到 1 個確認回覆以後,能夠發送 2 個數據包; 在收到 2 個確認回覆以後,能夠發 4 個;以此類推。這種幾何級數增加很快就會到達協議規定的發包數上限,這時候鏈接將進入擁塞避免階段,網站
這種機制須要幾回往返數據請求才能得知最佳擁塞窗口大小。但在解決性能問題時,就這 區區幾回數據往返也是很是寶貴的時間(成本)。現代操做系統通常會取 4~10 個數據包做爲初始擁塞窗口大小。若是你把一個數據包設置爲最大值下限 1460 字節(也就是 最大有效負載
),那麼只能先發送 5840 字節(假定擁塞窗口爲 4),而後就須要等待接收確認回覆。ui
現在的 Web 頁面平均大小約 2MB,包括 HTML 和全部依賴的資源。在理想狀況下, 這須要大約 9 次往返請求來傳輸完整個頁面。除此以外,瀏覽器通常會針對同一個域名開啓 6 個併發鏈接,每一個鏈接都免不了擁塞窗口調節。spa
上面提到的那些數字是怎麼得出來的?這個時候瞭解一些數學知識頗有必要,有助於估算對網絡傳輸的影響,看看究竟是增長仍是減小了傳輸字節數。假設擁塞窗口的大 小每次往返請求以後會翻一番,每一個數據包承載 1460 字節。在理想狀況下,呈現出等比數列。
直到第 9 次,2MB 數據才能所有發完。不過,這過分簡化了現實狀況。在窗口大小達 到 1024 個數據包時,要麼會觸發一個叫
ssthresh(慢啓動閾值)
的上限值,要麼窗口 會自動縮小;無論哪一種狀況,幾何級數增加都會終止。不過,用於粗略估算時,這種簡單方法已經夠用了。
前面提到過,由於 h1 並不支持多路複用,因此瀏覽器通常會針對指定域名開啓 6 個併發 鏈接。這意味着擁塞窗口波動也會並行發生 6 次。TCP 協議保證那些鏈接都能正常工做, 可是不能保證它們的性能是最優的。
雖然 h1 提供了壓縮被請求內容的機制,可是消息首部卻沒法壓縮。消息首部可不能忽略, 儘管它比響應資源小不少,但它可能佔據請求的絕大部分(有時候多是所有)。若是算 上 cookie,有個幾千字節就很正常了。
據 HTTP 歷史存檔記錄,2016 年底,請求首部通常集中在 460 字節左右。對於包含 140 個 資源的普通Web 頁面,意味着它在發起的全部請求中大約佔 63KB。
想一想以前關於 TCP 擁塞窗口管理的討論,發送該頁面相關的全部請求可能須要 3~4 輪往返,所以網絡延遲的損耗會被迅速放大。此外,上行帶寬一般會受到網絡限制,尤爲是在移動網絡環境中,因而擁塞窗口機制根原本不及起做用,致使更多的請求和響應。
消息首部壓縮的缺失也容易致使客戶端到達帶寬上限,對於低帶寬或高擁堵的鏈路尤爲如此。體育館效應(Stadium Effect)
就是一個經典例子。若是成千上萬人同一時間出如今同一地點(例如重大致育賽事),會迅速耗盡無線蜂窩網絡帶寬。這時候,若是能壓縮請求首部,把請求變得更小,就可以緩解帶寬壓力,下降系統的總負載。
若是瀏覽器針對指定域名開啓了多個 socket(每一個都會受隊頭阻塞問題的困擾),開始請求資源,這時候瀏覽器能指定優先級的方式是有限的:要麼發起請求,要麼不發起。
然而 Web 頁面上某些資源會比另外一些更重要,這必然會加劇資源的 排隊效應
。這是由於瀏覽器爲了先請求優先級高的資源,會推遲請求其餘資源。
可是優先級高的資源獲取以後,在處理的過程當中,瀏覽器並不會發起新的資源請求,因此服務器沒法利用這段時間發送優先級低的資源,總的頁面下載時間所以延長了。還會出現這樣的狀況:一個高優先級資源被瀏覽器發現,可是受制於瀏覽器處理的方式,它被排在了一個正在獲取的低優先級資源以後。
雖然第三方資源不是 HTTP/1 特有的問題,但鑑於它日益增加的性能問題,咱們也把它列在這裏。
現在的 Web 頁面上請求的不少資源徹底獨立於站點服務器的控制,咱們稱這些爲 第三方資源
。現代 Web 頁面加載時長中每每有一半消耗在第三方資源上。雖然有不少技巧能把第三方資源對頁面性能的影響降到最低,可是不少第三方資源都不在 Web 開發者的控制範圍內,因此極可能其中有些資源的性能不好,會延遲甚至阻塞頁面渲染。
任何關於 Web 性能的討論,只要沒有提到第三方資源引發的問題,都不算完整。(使人掃興的是, h2 對此也一籌莫展。)
第三方資源究竟讓頁面慢多少? Akamai 的 Foundry 團隊的研究顯示,第三方資源的影響很是大,平均累計佔到頁面總體加載時間的一半 1。這份報告提出了新的用於跟蹤第三方資源影響的指標,稱爲
3rd Party Trailing Ratio
。它測量的是請求並展示第三方內容對頁面渲染時間的影響程度。