(譯)HTTP/3 的漫長之路(The Long Road to HTTP/3)

當 HTTP/3 規範還處於草稿狀態時,最新版本的 Chrome 瀏覽器已經默認支持 HTTP/3 了。因爲Chrome佔據了瀏覽器70%的市場份額,你能夠說 HTTP/3 已經成爲主流。html

這個基礎協議的新修訂旨在使web更加高效、安全,並縮短內容傳輸的延遲。它大膽地採用了一個新的、專門構建的協議 QUIC 來替換底層 TCP 協議。解釋 QUIC 優勢的最好方法是說明 TCP 做爲 HTTP 請求傳輸的不足之處。要作到這一點,咱們要從頭開始提及。web

原始的 HTTP

1991年,當 Tim Berners-Lee 爵士正式設計了一個簡單的單行超文本交換協議時,TCP已是一個古老、可靠的協議。後來被稱爲 HTTP 0.9 的原始定義文檔特別提到 TCP 是一個首選的、但不是惟一的傳輸協議:算法

注意:HTTP目前運行在 TCP 之上,可是它能夠運行在任何面向鏈接的服務上。( Note: HTTP currently runs over TCP, but could run over any connection-oriented service.

固然了,這個概念驗證版的 HTTP 跟如今咱們熟知的 HTTP 已經相差很大。它沒有標題,也沒有狀態碼(status code)。典型的請求就像 ​GET /path​ 同樣簡單。響應只包含 HTML,並在結束時關閉TCP鏈接。因爲瀏覽器還不存在,用戶須要直接閱讀 HTML。它能夠連接到其餘資源,可是早期版本的 HTML 中沒有異步地請求其餘資源的標籤。一個 HTTP 請求就提供了一個完整的、自給自足的頁面。chrome

HTTP/1.0 的出現

在隨後的幾年裏,因特網迅速發展,HTTP 逐漸發展成爲一種可擴展和靈活的通用協議,儘管傳輸HTML 仍然是它的主要專長。HTTP的三個關鍵更新促成了這一演變:瀏覽器

  • HTTP 方法(methods)的引入容許客戶的指明它想要執行的操做類型。例如,建立 POST 是爲了容許客戶端向服務器發送數據以進行處理和存儲。
  • 狀態碼(status code)爲客戶端提供了一種方法,用於確認是否服務器已成功處理請求,若是沒有,還能夠了解發生了什麼類型的錯誤。
  • headers 添加了將結構化文本元數據(metadata)附加到請求和響應的能力,它能夠修改客戶端或服務端的行爲。例如,encodng 和 content-type 類型 headers 容許 HTTP 不只能夠傳輸HTML,還能夠傳輸任何類型的payload。「Compression」 header 容許客客戶端和服務器協商支持的壓縮格式,從而減小鏈接傳輸的數據量。

與此同時,HTML 先進到支持圖像、樣式和其餘連接資源。瀏覽器如今被迫執行多個請求來顯示一個單獨的網頁,而最初的「每一個請求一個鏈接」體系結構並非爲了處理這一問題設計的。創建和結束一個TCP鏈接須要大量來回的數據包交換,所以在延遲(latency)開銷方面相對昂貴。當一個網頁由一個單獨的文本文件組成時,這並不重要,可是隨着每一個頁面的請求數量的增長,延遲(latency)也隨之增長。緩存

下圖說明了每一個請求創建一個新的 TCP 鏈接所涉及的開銷。安全

所以建立了一個 「connection」 header來解決這個問題。客戶端發送一個帶有 「connection: keep-alive」 header 的請求,表示爲後續的請求保持TCP鏈接打開。若是服務器理解這個頭並贊成接受它,它的響應也將包含 「connection: keep-alive」 head。這樣,雙方都會保持 TCP 通道的打開狀態,並將其用於後續通訊,直到任何一方決定關閉它。隨着 SSL/TLS 加密的普及,這變得更加劇要,由於協商加密算法和交換加密密鑰須要在每一個鏈接上附加一個額外的請求/響應週期。服務器

當時,許多 HTTP 的改進都是自發出現的。當一個流行的瀏覽器或服務端應用發現須要一個新的HTTP特性時,他們只需本身實現它,並但願其餘各方也能跟隨效仿。諷刺的是,一個去中心化的網絡須要一個集權的管理機構來避免 HTTP 分裂成各類不兼容的碎片。Tim Berners-Lee,該協議的最初創造者,認識到了這一危險,並於1994年成立了萬維網聯盟(W3C),該聯盟與互聯網工程任務組(IETF)一塊兒致力於互聯網技術的規範化。做爲第一步,他們記錄了當時使用 HTTP 中的最多見的特性,並將其命名爲HTTP/1.0。然而,因爲這個「規範」描述了各類各樣的,每每不一致的技術,它歷來沒有獲得一個標準的狀態。所以,新版本 HTTP 協議的工做已經開始。網絡

HTTP/1.1 的標準化

HTTP/1.1 修復了 HTTP/1.0 的不一致性,並調整了協議,使其在新的 web 生態中更高效。引入的兩個最關鍵的變化是在默認狀況下使用持久的 TCP 鏈接(keep-alive)和 HTTP 管道(pipelining)。異步

HTTP 管道(pipelining)簡單地說,客戶端不須要等待服務器響應請求以後,才能發送後續的HTTP 請求。這個特性使得帶寬的使用更加有效,延遲也減小了,可是它還能夠獲得更大的改進。HTTP 管道(pipelining)仍然要求服務器按照接收到的請求的順序進行響應,所以,若是管道中的單個請求執行緩慢,則對客戶端的全部後續響應都將相應地延遲。這個問題被稱爲隊頭阻塞(head-of-the-line blocking)。

此時此刻,web 正在得到愈來愈多的交互功能。web2.0 就在眼前,一些網頁包含幾十個甚至幾百個外部資源。爲了解決隊頭阻塞(head-of-the-line blocking)的問題,並下降頁面加載速度,客戶端在每臺主機上創建多個TCP鏈接。固然,鏈接開銷歷來是毫無目的。在現實中,因爲愈來愈多的應用程序使用了 SSL/TLS 加密 HTTP 通訊,狀況變得更糟糕了。所以,大多數瀏覽器都設置了最大能夠同時鏈接的限制,以達到微妙的平衡。

許多較大的web服務已經認識到,現有的限制對於其異常繁重的交互式web應用程序來講過於侷限,所以它們經過多個域名分發應用程序來「玩弄系統(gamed the system)」。無論怎麼說,這一切都奏效了,但解決方案遠非優雅之舉。

儘管有一些缺點,但 HTTP/1.0 和 HTTP/1.1 的簡單性使它們得到了普遍的成功,並且在過去的十年裏,沒有人認真地嘗試改變它們。

SPDY 和 HTTP/2

2008年,谷歌發佈了 Chrome 瀏覽器,該瀏覽器因其快速和創新而迅速受到歡迎。它給了谷歌在互聯網技術問題上的強大投票權。2010年代初期,谷歌在Chrome上增長了對其網絡協議SPDY的支持。

HTTP/2 標準是在 SPDY 的基礎上進行改進的。HTTP/2 經過在單個打開的TCP鏈接上多路傳輸 HTTP 請求,解決了對頭阻塞(head-of-the-line blocking)問題。這容許服務器以任何順序響應請求,而後客戶端能夠在收到響應時從新組合響應,從而在單個鏈接中加快整個交換的速度。

事實上,使用 HTTP/2 服務端能夠在客戶端請求資源以前就將其提供給客戶端!舉個例子,若是服務端知道客戶機極可能須要一個樣式來顯示一個HTML頁面,那麼它能夠將CSS「推」給客戶端,而無需等待相應的請求。雖然在理論上這個功能頗有用,但在實踐中不多見到這種特性,由於它須要服務器理解它所服務的HTML的結構,這種狀況不多發生。

除了請求正文以外,HTTP/2還容許壓縮請求頭,這進一步減小了經過網絡傳輸的數據量。

HTTP/2 爲web 解決了不少問題,但並非全部的問題。在TCP協議層上,仍然存在相似的對頭阻塞( head-of-the-line)問題,TCP協議仍然是 web 的基本組成部分。當一個 TCP 包在傳輸過程當中丟失時,在服務器從新發送丟失的包以前,接收方沒法確認傳入的包。因爲 TCP 在設計上不受諸如 HTTP 之類的高級協議的影響,所以一個丟失的數據包將阻塞全部正在傳輸的HTTP請求的流,直到丟失的數據被從新發送。這個問題在不可靠的鏈接上尤其突出,這在無處不在的移動設備時代並很多見。

HTTP/3 變革

因爲 HTTP/2 的問題不能徹底在應用層解決,協議的新迭代必須更新傳輸層。然而,建立一個新的傳輸層協議並非一件容易的事情。傳輸協議須要硬件供應商的支持和大多數網絡運營商的部署,這些運營商因爲所涉及的成本和影響而不肯更新。以 IPv6 爲例:它是24年前引入的,如今還遠未獲得廣泛支持。

幸運的是,還有另外一個選擇。UDP 協議和 TCP 同樣受到普遍的支持,但它很是簡單,能夠運行在其上的建立自定義協議。UDP數據包是一次性的:沒有握手、持久鏈接或錯誤更正。HTTP3 背後的主要思想是放棄TCP,轉而使用基於 UDP 的 QUIC 協議。QUIC 以一種對 web 環境有意義的方式添加了必要的特性(那些之前由 TCP 提供的特性等等)。

跟 HTTP2 不同,HTTP2 容許未加密鏈接,QUIC 嚴格要求經過加密建立鏈接。此外,鏈接中的全部數據也應用了加密,而不只僅是 HTTP payload,它能夠防止全部類型的安全問題。在QUIC中,創建持久鏈接、協商加密協議、甚至發送第一批數據都被合併到一個請求/響應週期中,從而大大下降了鏈接延遲。若是客戶端在本地緩存了加密參數,則能夠經過簡化的握手(0-RTT)從新創建與已知主機的鏈接。

爲了解決傳輸級別的隊頭阻塞(head-of-the-line blocking)問題,經過QUIC鏈接傳輸的數據被分紅流(streams)。在持久的QUIC鏈接中,流(streams)是的短生命(short-lived)、獨立的「子鏈接」。每一個流(streams)處理本身的糾錯和傳遞保證,但使用全局鏈接的壓縮和加密屬性。每一個客戶端發起的 HTTP 請求都在一個單獨的流(streams)上運行,所以丟失一個包不會影響其餘流/請求的數據傳輸。

UDP是一種無狀態協議(持久鏈接只是其之上的一種抽象),這使得 QUIC 可以在很大程度上忽略包傳遞的複雜性。例如,客戶端更改其 IP 地址(例如智能手機從移動網絡跳到家庭wifi)理論上不該該中斷鏈接,由於該協議容許在不一樣IP地址之間遷移而無需從新鏈接。

現有QUIC協議的全部實現都在用戶態(userspace)而不是操做系統內核中運行。因爲客戶端(例如瀏覽器)和服務端的更新一般比操做系統內核更新得更頻繁,這將有望加快新功能的採用。


HTTP/3 的問題

在我看來,雖然 HTTP/3 標準是朝着更快、更安全的互聯網邁進的一大步,但它並不完美。它的一些問題是由其新穎性引發的,而另外一些問題彷佛是協議固有的。

TCP 協議已經存在了好久了,路由器很容易理解它。它有明確的未加密標記,用於創建和關閉鏈接,可用於跟蹤和控制現有會話。在網絡硬件學會理解新協議以前,它將把 QUIC 通訊簡單地看做一個獨立的UDP包流,這將使網絡配置更加複雜。

從客戶端緩存「恢復」(revive)鏈接的能力,使得協議對重放攻擊(replay attack)開放了:在某些狀況下,惡意攻擊者能夠從新發送先前捕獲的數據包,這些將被服務器解釋爲來自受害者有效的數據包。許多web服務器,像那些提供靜態內容的服務器,不會受到這種攻擊的損害。對此攻擊場景有效的應用程序必須記住禁用 0-RTT 功能。

這就是目前爲止 HTTP 的故事。我認爲HTTP/3是向前邁進的一大步,固然但願 HTTP/3 在不久的未來獲得普遍採用。

相關文章
相關標籤/搜索