從過去到未來:關於 HTTP2 的研究

創宇前端發表於2018-10-08

先說一下 HTTP 的發展簡史,以及 HTTP/2.0的優勢在哪裡。

從過去到未來:關於 HTTP2 的研究


HTTP/0.9

1991年釋出的 HTTP/0.9 版本 是基於 TCP/IP 協議的應用層協議。它不涉及資料包的傳輸,只規定了客戶端和伺服器之間的通訊格式,預設使用80埠,該版本極其簡單,只有一個命令 GET , 並且只支援文字傳輸。

請求過程: TCP 建立後,客戶端向伺服器請求 網頁 如 index.html , 並且協議規定, 伺服器只能迴應 HTML 格式的字串, 不能迴應別的格式。 伺服器傳送完畢後, 就關閉連線。

HTTP/1.0

1996年5月, HTTP/1.0 版本釋出。

首先: 加入了很多格式的內容都可以傳送,這使得網際網路不僅可以傳輸文字,還能傳輸影象、視訊、二進位制檔案。 通過這次的更新,為網際網路的大發展奠定了基礎。

其次: 除了GET命令,還加入了 POST 、HEAD 等命令, 豐富了瀏覽器與伺服器的互動手段。

最後: HTTP 請求和迴應的格式也變了。 除了資料部分,每次通訊都必須包括頭資訊(HTTP header),用來描述一些後設資料。

其他的新增功能還包括狀態碼、多字符集支援、多部分傳送、許可權、快取、內容編碼 等

相比 HTTP/0.9 有巨大的變化。

但是, HTTP/1.0 還是存在一些缺點,其中主要缺點是,每個TCP連線只能傳送一個請求。傳送資料完畢,連線就關閉,如果還要請求其他資源,就必須再新建一個連線。

TCP 新建連線的成本很高,因為需要客戶端和伺服器三次握手,並且開始時傳送速率較慢,因為 TCP 通過慢開始啟動。 所以,HTTP/1.0 版本的效能就比較差。 隨著網頁載入的外部資源越來越多,這個問題就愈發突出了。

解決每次都要重新建立連線的方法是: 在客戶端和伺服器端各自都加了一個 Connection: keep-alive 以便請求複用。但是,這個欄位不是標準欄位, 不同實現的行為可能不一致, 因此不是根本的解決方法。

HTTP/1.1

從過去到未來:關於 HTTP2 的研究

1997年1月, HTTP/1.1 版本釋出, 只比 1.0 版本晚了半年。 它進一步完善了 HTTP 協議,一直用到了20年後的今天, 直到現在還是最流行的版本。

加入了一些新的功能如:

  • 持久連線

    Connection: keep-alive , 即 TCP 連線時預設不關閉, 可以被多個請求複用,不用宣告 Connection: keep-alive。

客戶端和伺服器發現對方一段時間沒有活動,就可以主動關閉連線。規範的做法是,客戶端在最後一個請求時,傳送 Connection: close , 明確要求伺服器關閉 TCP 連線。 Connection: close

目前,為了安全性,對於同一個域名,大多數瀏覽器允許同時建立6個持久連線。

管線化:即在同一個 TCP 連線裡面,客戶端可以同時傳送多個請求, 這樣就進一步改進了 HTTP 協議的效率。(雖然可以提高效率,但是支援的很少,用的也不多,確實會提升一些效率,但是並且也存在一些問題,後面HTTP/2.0再說)

  • Content-Length 欄位

    一個 TCP 連線同時可以傳送多個迴應,那麼就需要有一種機制,區分資料包是屬於哪一個迴應的。這就是 Content-Length 欄位的作用, 宣告本次迴應的資料長度。

    如: Content-Length: 3000

    他告訴瀏覽器,本次迴應的長度是 3000 位元組, 後面的位元組就屬於下一個迴應了。

    在 HTTP/1.0 版本中, Content-Length 欄位不是必須的,因為不涉及一次傳多個迴應,或者瀏覽器發現伺服器關閉了 TCP連線,就表明收到的資料包已經全了。

  • 分塊傳輸編碼

    使用 Content-Length 欄位的前提條件是, 伺服器傳送迴應之前,必須知道響應的資料包長度。

    對於一些很耗時的動態操作來說,這意味著伺服器需要等所有操作完成,才能傳送資料,顯然這樣的效率不高。更好的處理方法是,產生一塊資料,就傳送一塊,採用“流模式”(stream) 取代 “快取模式”(buffer)。

    因此,HTTP/1.1 版本規定可以不使用 Content-Length 欄位,而使用 “分塊傳輸編碼”(chunked transfer encoding)。只要請求或迴應的頭資訊有 Transfer-Encoding 欄位, 就表明迴應將由數量未定的資料塊組成。

    每個非空的資料塊之前,會有一個16進位制的數值,表示這個塊的長度。最後是一個大小為 0 的塊, 就表示本次迴應的資料傳送完了。

  • 其他功能

    HTTP/1.1 版本還增加了很多動詞方法如:PUT、PATCH、HEAD、OPTIONS、DELETE

    另外,客戶端請求的頭資訊新增了 Host 欄位,用來指定伺服器的域名。

    有了Host欄位,就可以將請求發往同一臺伺服器上的不同網站,為虛擬主機的興起打下了基礎。

  • 缺點

    • Spriting 是將眾多個小圖片整合成大圖片,然後再拆分,壞處可想而知,每個網頁需不需要用到那麼多圖片,都需要傳送。

    • 內聯(Inlining),簡而言之就是把圖片資源放在 CSS 檔案裡面的 URL 裡面,這種缺點和 Spriting 類似。

    • 拼接(Concatenation),如果 js 檔案特別多的話,可以使用工具把眾多 js 檔案整合成一個檔案,這樣瀏覽器就只需要下載一次就可以完成,而不是無數次請求去下載,但是如果 js 的某個程式碼被改動,那麼也需要重新下載,這樣會對除錯和開發者造成極大的不變。

    • 分片(Sharding),因為 HTTP/1.1 規範提到一個客戶端最多能對同一主機建立有限個 TCP 連線。因此,Sharding 就是把服務分散在儘可能多的伺服器上面。這樣使用者就可以同時和多臺主機建立很多 TCP 連線,從而降低載入時間。

    • 線頭阻塞

      雖然加入了持久連線,管線化,但是同一個 TCP 連線裡面,所有的資料通訊都是按次序進行的。伺服器只有處理完一個迴應,才會進行下一個迴應。如果前面的迴應特別慢,後面就會有很多請求排隊等著。這稱為“隊頭堵塞”(Head-line blocking)

      為了避免這個問題,有兩種方法:一是減少請求數量,二是同時多開持久連線。這導致了很多的網頁優化技巧,比如合併指令碼和樣式表,將圖片嵌入 CSS 程式碼、域名分片等等。

    • 為了解決此類問題的一些解決方法:

    • 過多的可選項和太多的細節

      並且 HTTP/1.1 包含了太多細節和可選的部分,這讓它變得過於龐大。

      過多的可選項,這就導致了在一些不常用的功能在後來的實現中很少會被支援,而有些最初實現了的功能,卻又很少被使用。

      隨著時間的推移,這些當初看似被邊緣化的功能逐漸被用上,客戶端和伺服器的互用性問題就暴露出來了。HTTP 管線化(HTTP pipelining) 就是一個非常好的例子。

    • 未能被充分利用的 TCP

      我們可以通過更好的利用 TCP 來減少傳輸過程中的暫停,並充分挖掘利用那些本可以用於傳送/接收更多資料的時間。

    • 傳輸大小和資源數量

      近年來載入網站首頁需要下載的資料量在逐漸增加,並且已經超過了 1.9MB。並且我們更關心的是平均每個頁面為了完成顯示與渲染所需要的資源已經超過100個了。

    • 明顯的延遲

      HTTP/1.1 在網路延遲方面做的就不盡人意了。部分原因是 HTTP 管線化(pipelining)還存在很多問題,所以對大部分使用者來說這項技術還是預設被關閉的。雖然近幾年,我們的網路頻寬從原來只有 幾百KB ,現在一般的網路也能達到 數十MB ,但是 網路延遲也沒有降低。比如在移動裝置上,即使擁有高的連線速率,也很難獲得優質快速的網路體驗。

      例如:可以將資源分佈在不同的主機上面來建立連線,獲得更快的速度。

    從過去到未來:關於 HTTP2 的研究


SPDY 協議

2009年,Google 公開了自行研發的 SPDY 協議, 主要解決 HTTP/1.1 效率不高的問題。

這個協議在 Chrome 瀏覽器上證明可行以後,就被當做 HTTP/2 的基礎, 主要特性都在 HTTP/2 之中得到繼承。

如果用 Chrome ,就會發現,在百度網站上就有用 SPDY 的例子:

從過去到未來:關於 HTTP2 的研究


HTTP/2

2015年, HTTP/2 釋出。它不叫 HTTP/2.0 ,是因為標準委員會不打算再發布自版本了,下一個新版本將是 HTTP/3。

  • 改進

    • 降低協議對延遲的敏感

    • 修復 pipelining 和 head of line blocking 問題

    • 防止主機需要更高的連線數量

    • 保留所有現有的介面,內容,URI 格式和結構

  • HTTP/2 是一個二進位制協議

    • 我們可以使用 Wireshark 這樣的 http/2 解析器來分析和除錯協議。

    從過去到未來:關於 HTTP2 的研究

    • http/2 會傳送有著不同型別的二進位制幀,他們有一些公共欄位,一共規範了10種不同的幀,其中最基礎的兩種分別對應於 HTTP/1.1 的 DATA 和 HEADERS。

    • 多路複用的流,實現雙向的,實時的通訊,多工。

      從過去到未來:關於 HTTP2 的研究

    • 資料包具有優先順序和依賴性,伺服器可以優先處理

    • 頭部壓縮,可以降低傳輸成本,但是有可能會存在安全問題。

    • 支援 重置 ,就是如果要傳輸一個東西,但是需要傳輸好幾次,然後傳輸了一半之後,接收方突然不想要了,這個時候可以傳送一個 RST_STREAM 幀來終止。而在 HTTP/1.1 時,則需要斷開整個連線。

  • 伺服器主動推送,也可以稱快取推送

    • 如果客戶端請求一個資源,伺服器可以推斷出來客戶端 請求完此資源後,會再請求的資源,這個時候伺服器就可以主動準備好資源並且一併推送給客戶端。如果客戶端不需要,也可以傳送一個RST_STREAM 幀來終止。

    • 流量控制: HTTP/2 可以有自己公示的流量視窗,它可以限制一端傳送資料。和 SSH 的工作原理類似。

    從過去到未來:關於 HTTP2 的研究


其中包含了很多資訊,其中有 Stream ID SEQ/ACK 等欄位

下面這張圖來說明 HTTP/1.1 和 HTTP/2 在效能方面的差別。

從過去到未來:關於 HTTP2 的研究


從過去到未來:關於 HTTP2 的研究

文 / blank

編 / 陳皮爽,熒聲


作者過往文章:

要點梳理:TCP 連線及常見攻擊手法分析(微信公眾平臺連結)

(掘金社群連結點此)


本文已由作者授權釋出,版權屬於創宇前端。歡迎註明出處轉載本文。本文連結:https://knownsec-fed.com/2018-09-25-guan-yu-http2-de-yan-jiu/

想要訂閱更多來自知道創宇開發一線的分享,請搜尋關注我們的微信公眾號:創宇前端(KnownsecFED)。歡迎留言討論,我們會盡可能回覆。


從過去到未來:關於 HTTP2 的研究

感謝您的閱讀。

相關文章