HTTP/3 居然基於 UDP,HTTP 協議這些年都經歷了啥?

本文做者:oschina_2020前端

聽到 HTTP/3 基於 UDP 協議的消息,很多人可能都跟我同樣驚呆了。算法

咱們從開始學習網絡協議就必定會接觸到 HTTP,而教科書或者老師一直以來講的都是「UDP 不可靠,因此 HTTP 基於 TCP 協議」,雖然偶爾會思考「UDP 與 TCP 都是比較底層的協議,用 TCP 來定義上層的 HTTP 協議,也是須要通過一系列設計和封裝的,那憑什麼 UDP 就不能夠試試呢?」、「是成本問題?HTTP 在 TCP 之上設計的成本也不低啊,好比三次握手、四次揮手、滑動窗口等構思精妙的算法,也都是在通過無數次設計與嘗試以後肯定下來的。」……可是總之 HTTP 只能基於 TCP,而不能是 UDP 這一思惟仍是在一道道試題和一次次編程 request-response 的過程當中固定在腦海裏。編程

因此 HTTP/3 再也不基於 TCP 而是採用了 UDP,這一消息仍是挺讓人驚訝的。瀏覽器

看到這裏可能有人會驚訝於另外一個點:什麼?!HTTP 協議都發展到 v3 了緩存

其實目前正逐漸走向主流的 HTTP 協議是 HTTP/2,它相比於 HTTP/1,大幅度提升了性能,網站只須要升級到新版本協議就能夠減小不少以前須要作的性能優化工做,固然兼容問題以及如何優雅降級是比較棘手的問題,這也應該是國內目前還不廣泛使用 HTTP/2 的重要緣由。安全

雖然 HTTP/2 帶來了許多優勢,可是並不表明它已是完美的了,HTTP/3 就是爲了解決 HTTP/2 所存在的一些問題而被推出來的。性能優化

本文接下來會從基礎的 HTTP/1 開始講起,從第一代協議到第三代分別針對性地介紹,試圖把 HTTP 這一協議的技術發展過程以簡要通俗的方式分享給讀者,並讓你們明白,爲何通過這麼多年的發展,HTTP 協議最終居然採用了不安全的 UDP。服務器

1、HTTP 協議

HTTP 是 HyperText Transfer Protocol(超文本傳輸協議)的縮寫,它是互聯網上應用最爲普遍的一種網絡協議,全部 WWW 文件都必須遵照這個標準。cookie

伴隨着計算機網絡和瀏覽器的誕生,HTTP 1.0/1.1 也隨之而來,它創建在 TCP 協議之上,處於計算機網絡中的應用層,因此 HTTP 協議的瓶頸及其優化技巧都是基於 TCP 協議自己的特性,例如 TCP 創建鏈接的 3 次握手和斷開鏈接的 4 次揮手,以及每次創建鏈接帶來的 RTT 延遲時間等。網絡

HTTP 1.0 與 1.1 的主要區別在於長鏈接支持、多路複用、帶寬節約與數據壓縮等,相對於 HTTP/2,本文將其通稱爲 HTTP/1。

2、HTTP/1 的缺陷

HTTP/1 在 Web 時代迅速崛起,可是隨着採用日漲,其缺陷也暴露出來。

無論是 1.0 仍是 1.1 版本,HTTP/1 都主要存在如下幾個方面的缺陷:

  • 鏈接沒法複用:鏈接沒法複用會致使每次請求都經歷三次握手和慢啓動。三次握手在高延遲的場景下影響較明顯,慢啓動則對大量小文件請求影響較大(沒有達到最大窗口請求就被終止)。

    • HTTP/1.0 傳輸數據時,每次都須要從新創建鏈接,增長延遲。
    • HTTP/1.1 雖然加入 keep-alive 能夠複用一部分鏈接,但域名分片等狀況下仍然須要創建多個 connection,耗費資源,給服務器帶來性能壓力。
  • Head-Of-Line Blocking(HOLB,隊頭阻塞):這會致使帶寬沒法被充分利用,以及後續健康請求被阻塞。HOLB 是指一系列包(package)由於第一個包被阻塞;當頁面中須要請求不少資源的時候,HOLB 會致使在達到最大請求數量時,剩餘的資源須要等待其它資源請求完成後才能發起請求。

    • HTTP 1.0:下個請求必須在前一個請求返回後才能發出,request-response對按序發生。顯然,若是某個請求長時間沒有返回,那麼接下來的請求就所有阻塞了。
    • HTTP 1.1:嘗試使用 pipeling 來解決,即瀏覽器能夠一次性發出多個請求(同個域名、同一條 TCP 連接)。但 pipeling 要求返回是按序的,那麼前一個請求若是很耗時(好比處理大圖片),那麼後面的請求即便服務器已經處理完,仍會等待前面的請求處理完纔開始按序返回。因此,pipeling 只部分解決了 HOLB。

               
                如上圖所示,紅色圈出來的請求就因域名連接數已超過限制,而被掛起等待了一段時間。

  • 協議開銷大: HTTP/1 在使用時,header 裏攜帶的內容過大,在必定程度上增長了傳輸的成本,而且每次請求 header 基本不怎麼變化,尤爲在移動端增長用戶流量。
  • 安全因素:HTTP/1 在傳輸數據時,全部傳輸的內容都是明文,客戶端和服務器端都沒法驗證對方的身份,這在必定程度上沒法保證數據的安全性。

3、SPDY 協議

由於 HTTP/1 的問題,咱們會引入雪碧圖、將小圖內聯、使用多個域名等等的方式來提升性能。不過這些優化都繞開了協議自己,直到 2009 年,谷歌公開了自行研發的 SPDY 協議,它主要解決 HTTP/1.1 效率不高的問題。

直到這時,纔算是正式改造了 HTTP 協議自己。SPDY 進行延遲下降、header 壓縮等改進,其實踐證實了這些優化的效果,也最終帶來 HTTP/2 的誕生。

SPDY 協議在 Chrome 瀏覽器上證實可行之後,就被看成 HTTP/2 的基礎,主要特性都在 HTTP/2 之中獲得繼承,下面咱們就來說講這一部份內容。

4、HTTP/2

2015 年,繼承於 SPDY 的 HTTP/2 協議發佈了。HTTP/2 是 HTTP/1 的替代品,但它不是重寫,協議中還保留着第一代的一些內容,好比 HTTP 方法、狀態碼與語義等都與 HTTP/1 同樣。

HTTP/2 基於SPDY3,專一於性能,最大的一個目標是在用戶和網站間只用一個鏈接。

HTTP/2 由兩個規範組成:

  1. Hypertext Transfer Protocol version 2 - RFC7540
  2. HPACK - Header Compression for HTTP/2 - RFC7541

5、HTTP/2 特性

二進制傳輸

HTTP/2 採用二進制格式傳輸數據,而非 HTTP/1 的文本格式,二進制協議解析起來更高效。

HTTP/1 的請求和響應報文,都是由起始行、首部和實體正文(可選)組成,各部分之間以文本換行符分隔。HTTP/2 將請求和響應數據分割爲更小的幀,而且它們採用二進制編碼

接下來咱們介紹幾個重要的概念:

  • 流(stream):流是鏈接中的一個虛擬信道,能夠承載雙向的消息;每一個流都有一個惟一的整數標識符(一、2…N)
  • 消息(message):指邏輯上的 HTTP 消息,好比請求、響應等,由一或多個幀組成
  • 幀(frame):HTTP/2 通訊的最小單位,每一個幀包含幀首部,至少也會標識出當前幀所屬的流,承載着特定類型的數據,如 HTTP 首部、負荷等

                

HTTP/2 中,同域名下全部通訊都在單個鏈接上完成,該鏈接能夠承載任意數量的雙向數據流。每一個數據流都以消息的形式發送,而消息又由一個或多個幀組成。多個幀之間能夠亂序發送,根據幀首部的流標識能夠從新組裝。

多路複用

在 HTTP/2 中引入了多路複用技術。多路複用很好地解決了瀏覽器限制同一個域名下的請求數量的問題,同時也更容易實現全速傳輸,畢竟新開一個 TCP 鏈接都須要慢慢提高傳輸速度。

你們能夠經過這個連接(http2.akamai.com/demo)直觀感覺下 HTTP/2 比 HTTP/1 到底快了多少。

在 HTTP/2 中,有了二進制分幀以後,HTTP/2 再也不依賴 TCP 連接去實現多流並行了,像前邊提到的,在 HTTP/2 中:

  • 同域名下全部通訊都在單個鏈接上完成
  • 單個鏈接能夠承載任意數量的雙向數據流
  • 數據流以消息的形式發送,而消息又由一個或多個幀組成,多個幀之間能夠亂序發送,由於根據幀首部的流標識能夠從新組裝

這一特性,使性能有了極大提高:

  • 同個域名只須要佔用一個 TCP 鏈接,使用一個鏈接並行發送多個請求和響應,消除了因多個 TCP 鏈接而帶來的延時和內存消耗
  • 並行交錯地發送多個請求,請求之間互不影響
  • 並行交錯地發送多個響應,響應之間互不干擾
  • 在 HTTP/2 中,每一個請求均可以帶一個 31 bit 的優先值,數值越大優先級越低,0 表示最高優先級。有了這個優先值,客戶端和服務器就能夠在處理不一樣流時採起不一樣的策略,以最優的方式發送流、消息和幀。

                
如上圖所示,多路複用技術能夠只經過一個 TCP 鏈接傳輸全部的請求數據。

Header 壓縮

在 HTTP/1 中,咱們使用文本的形式傳輸 header,在 header 攜帶 cookie 的狀況下,可能每次都須要重複傳輸幾百到幾千字節。

爲了減小這塊的資源消耗並提高性能, HTTP/2 對這些首部採起了壓縮策略:

  • HTTP/2 在客戶端和服務器端使用「首部表」來跟蹤和存儲以前發送的鍵-值對,對於相同的數據,再也不經過每次請求和響應發送
  • 首部表在 HTTP/2 的鏈接存續期內始終存在,由客戶端和服務器共同漸進地更新
  • 每一個新的首部鍵-值對要麼被追加到當前表的末尾,要麼替換表中以前的值

例以下圖中的兩個請求, 請求 1 發送了全部頭部字段,第二個請求則只須要發送差別數據,這樣能夠減小冗餘數據,下降開銷:

Server Push

Server Push 即服務端能經過 push 的方式將客戶端須要的內容預先推送過去,也叫「cache push」。

能夠想象如下狀況:某些資源客戶端是必定會請求的,這時就能夠採起服務端 push 的技術,提早給客戶端推送必要的資源,這樣就能夠相對減小一點延遲時間。固然在瀏覽器兼容的狀況下你也可使用 prefetch。

例如服務端能夠主動把 JS 和 CSS 文件推送給客戶端,而不須要客戶端解析 HTML 時再發送這些請求。

服務端能夠主動推送,客戶端也有權利選擇是否接收。若是服務端推送的資源已經被瀏覽器緩存過,瀏覽器能夠經過發送 RST_STREAM 幀來拒收。主動推送也遵照同源策略,換句話說,服務器不能隨便將第三方資源推送給客戶端,而必須是通過雙方確認才行。

6、HTTP/3

雖然 HTTP/2 解決了不少以前舊版本的問題,可是它仍是存在一個巨大的問題,主要是底層支撐的 TCP 協議形成的。

上文提到 HTTP/2 使用了多路複用,通常來講同一域名下只須要使用一個 TCP 鏈接。但當這個鏈接中出現了丟包的狀況,那就會致使 HTTP/2 的表現狀況反倒不如 HTTP/1 了。

由於在出現丟包的狀況下,整個 TCP 都要開始等待重傳,也就致使了後面的全部數據都被阻塞了。可是對於 HTTP/1.1 來講,能夠開啓多個 TCP 鏈接,出現這種狀況反到只會影響其中一個鏈接,剩餘的 TCP 鏈接還能夠正常傳輸數據

那麼可能就會有人考慮到去修改 TCP 協議,其實這已是一件不可能完成的任務了,由於 TCP 存在的時間實在太長,已經充斥在各類設備中,而且這個協議是由操做系統實現的,更新起來不大現實。

基於這個緣由,Google 就本身架起爐竈搞了一個基於 UDP 協議的 QUIC 協議,而且使用在了 HTTP/3 上,HTTP/3 以前名爲 HTTP-over-QUIC,從這個名字中咱們也能夠發現,HTTP/3 最大的改造就是使用了 QUIC。

QUIC 雖然基於 UDP,可是在本來的基礎上新增了不少功能,接下來咱們重點介紹幾個 QUIC 功能。

QUIC 功能

  • 0RTT

經過使用相似 TCP 快速打開的技術,緩存當前會話的上下文,在下次恢復會話的時候,只須要將以前的緩存傳遞給服務端驗證經過就能夠進行傳輸了。0RTT 建連能夠說是 QUIC 相比 HTTP/2 最大的性能優點。那什麼是 0RTT 建連呢?

這裏面有兩層含義:

一、傳輸層 0RTT 就能創建鏈接。

二、加密層 0RTT 就能創建加密鏈接。

由於這裏考慮到安全性,咱們就拿加了 TLS 的「安全的 HTTP 協議」HTTPS 來對比。上圖左邊是 HTTPS 的一次徹底握手的建連過程,須要 3 個 RTT,就算是會話複用也須要至少 2 個 RTT。

而 QUIC 呢?因爲創建在 UDP 的基礎上,同時又實現了 0RTT 的安全握手,因此在大部分狀況下,只須要 0 個 RTT 就能實現數據發送,在實現前向加密的基礎上,而且 0RTT 的成功率相比 TLS 的會話記錄單要高不少。

  • 多路複用

QUIC 原生實現了多路複用功能,而且傳輸的單個數據流能夠保證有序交付且不會影響其它數據流,這樣的技術就解決了前邊提到的 TCP 多路複用存在的問題。

同 HTTP/2 同樣,同一個 QUIC 鏈接上能夠建立多個 stream 來發送多個 HTTP 請求,可是,QUIC 是基於 UDP 的,由於一個鏈接上的多個 stream 之間沒有依賴,因此不存在 HTTP/2 中的問題。好比下圖中 stream2 丟了一個 UDP 包,不會影響後面跟着 Stream3 和 Stream4,不存在 TCP 隊頭阻塞。雖然 stream2 的那個包須要從新傳,可是 stream三、stream4 的包無需等待就能夠發給用戶。


另外 QUIC 在移動端的表現也會比 TCP 好。由於 TCP 是基於 IP 和端口去識別鏈接的,這種方式在多變的移動端網絡環境下是很脆弱的。而 QUIC 是經過 ID 的方式去識別一個鏈接,無論你網絡環境如何變化,只要 ID 不變,就能迅速重連上。

  • 加密認證的報文

TCP 協議頭部沒有通過任何加密和認證,因此在傳輸過程當中很容易被中間網絡設備篡改、注入和竊聽,好比修改序列號與滑動窗口。這些行爲有多是出於性能優化,也有多是主動攻擊。

相比之下,QUIC 的 packet 能夠說是武裝到了牙齒。除了個別報文好比 PUBLIC_RESET 和 CHLO,全部報文頭部都是通過認證的,報文 Body 都是通過加密的。

這樣只要是針對 QUIC 報文進行了任何修改,接收端都可以及時發現,有效地下降了安全風險。

如上圖所示,紅色部分是 Stream Frame 的報文頭部,有認證;綠色部分是報文內容,所有通過加密。

  • 前向糾錯機制

QUIC 協議有一個很是獨特的特性,稱爲前向糾錯(Forward Error Correction,FEC),每一個數據包除了它自己的內容以外,還包括了部分其它數據包的數據,所以少許的丟包能夠經過其它包的冗餘數據直接組裝而無需重傳。

前向糾錯犧牲了每一個數據包能夠發送數據的上限,可是減小了由於丟包致使的數據重傳次數。這會取得更好的效果,由於數據重傳將會消耗更多的時間,包括確認數據包丟失、請求重傳與等待新數據包等步驟。

假如說此次我要發送三個包,那麼協議會算出這三個包的異或值並單獨發出一個校驗包,也就是總共發出了四個包,當出現其中的非校驗包丟包的狀況時,能夠經過另外三個包計算出丟失的數據包的內容。固然這種技術只能使用在丟失一個包的狀況下,若是出現丟失多個包就不能使用糾錯機制了,只能使用重傳的方式了

7、總結

  • HTTP/1 有鏈接沒法複用、隊頭阻塞、協議開銷大和安全因素等多個缺陷
  • HTTP/2 經過多路複用、二進制流與 Header 壓縮等技術,極大地提升了性能,可是仍是存在一些問題
  • HTTP/3 拋棄 TCP 協議,以全新的視角從新設計 HTTP。其底層支撐是 QUIC 協議,該協議基於 UDP,有 UDP 特有的優點,同時它又取了 TCP 中的精華,實現了即快又可靠的協議

從 HTTP/1 到 HTTP/3,HTTP 協議通過不斷進化,性能愈來愈高,在這個過程當中,底層協議甚至從 TCP 改成了以前被認定爲不適合 UDP,這其中不斷探索的設計思想值得學習。雖然本文是簡單的介紹,但已經把這一演進過程簡單地總結了出來,但願讀者可以有所收穫。

做者介紹

浪裏行舟,專一於前端領域。我的公衆號:前端工匠,致力於推送適合初中級工程師快速吸取的優質文章。

本文系做者投稿文章,歡迎投稿。投稿要求見:

https://my.oschina.net/editorial-story/blog/1814725

原文連接地址:https://developer.baidu.com/topic/show/290694

相關文章
相關標籤/搜索