1. 程式人生 > >[譯] 關於 HTTP/3 的一些心得

[譯] 關於 HTTP/3 的一些心得

關於 HTTP/3 的一些心得

HTTP/3 即將成為以後的標準。作為協議的老使用者,我覺得自己應該寫點什麼。

Google (pbuh) 擁有最受歡迎的瀏覽器(Chrome)和兩個最受歡迎的網站(#1 Google.com #2 Youtube.com)。因此,谷歌手握未來 Web 協議的開發權。他們把第一次升級的協議稱為 SPDY(發音同 "speedy"),也就是之後被標準化的 HTTP 的第二個版本,即 HTTP/2。他們將第二次升級的協議稱為 QUIC(發音同 "quick"),也就是即將被標準化的 HTTP/3。

目前主流的 Web 瀏覽器(chrome、Firefox、Edge、safari)和主流的 Web 伺服器(Apache、Nginx、IIS、CloudFlare)都已經對 SPDY (HTTP/2) 進行了支援。許多受歡迎的網站也對其進行了支援(甚至是非谷歌站點),儘管你不太可能在網上看到它(用 wireshark 或者 tcpdump 進行檢測),因為它一直是用 SSL 進行加密的。儘管該標準允許 HTTP/2 在 TCP 上執行,但實際上所有的應用都是 SSL 進行的。

這裡有一個關於標準的知識點。在網際網路範圍以外,標準通常都是依附於法律之上,由政府進行管理。所有的主要利益相關人員在一個會議室進行制定,然後用法律強迫人們認可並使用它。然而在網際網路上則有所不同,人們首先會實現標準,然後由使用者的認可度決定是否開始使用。標準通常都是事實,RFC 文件是為了在網際網路上已經正常工作的內容所編寫的,用於記錄人們已經在使用的內容。SPDY 被瀏覽器/伺服器採用的原因不僅僅是因為它被標準化,還因為網際網路的主要廠商都開始新增對它的支援。QUIC 也是如此:它正在被標準化為 HTTP/3 則反映了它正在被使用,這不僅僅是一個里程碑,因為大多數人都已經開始在實踐中開始應用這一協議。

QUIC 實際上更像是 TCP(TCP/2)的新版本,而不是 HTTP(HTTP/3)的新版本。因為它並沒有真正改變 HTTP/2 所做的事情,而是改變了傳輸所做的工作方式。因此,我下面的評論重點是傳輸問題,而不是 HTTP 問題。

主要的重點特性是更快的連線設定和更短的延遲。TCP 需要在連線建立之前來回傳送多個數據包。SSL 會在加密建立之間請求來回傳送多個數據包。如果有大量網路延遲,比如人們使用半秒每次的衛星網際網路 ping 通訊,它可能需要很長的時間來建立一個連線。通過減少往返次數,連線可以更快地建立,因此當你點選一個連結時,連結資源就會立刻響應並反饋。

下一個重點特性是頻寬。由於頻寬限制這樣的存在,在網路通訊中雙方如果處於頻寬限制不對等的情況下,資料包接收慢的一方會因為來不及接收而產生丟包,導致傳送方資料重發從而耗費了更多的網路資源,所以只有在雙方頻寬對等時才能到達網路最大利用率。

為什麼傳統的 HTTP 在這方面會表現得如此差強人意。與網站的互動需要同時傳輸多種內容,而 HTTP 使用單獨的 TCP 無法達到這一目的,因此瀏覽器會和 Web 伺服器進行多個連線 (一般為 6)。可這又破壞了對頻寬的估計,因此每個 TCP 連線都試圖獨立地執行,就好像其他連線不存在一樣。SPDY 通過多路複用的特性解決了這一問題,它將瀏覽器/伺服器之間的多個互動與單個頻寬計算結合在一起。

QUIC 擴充套件了這種多路複用方式,促使在瀏覽器/伺服器之間的多個互動處理變得更加簡單,這不會導致互動之間彼此阻塞,這需要通用的頻寬估算。從使用者的角度來看,這將使互動更加順暢,同時減少路由擁塞的使用者體驗。

現在我們討論一下 user-mode stacks。這是來自於 TCP 的問題,尤其是在伺服器上的表現,TCP 連線由作業系統核心處理,而服務本身執行在使用者模式。跨核心/使用者模式邊界操作資源會導致效能問題。追蹤大量 TCP 連線會導致可伸縮性問題。有些人嘗試將伺服器放入核心來避免過度使用的情況,這顯然不是好主意。因為它會破壞作業系統的穩定性。我的解決方案是使用 BlackICE IPS 和 masscan,這是一個使用者模式驅動的硬體。從網路晶片中直接獲取資料包到使用者模式程序,繞過核心 (參閱 PoC||GTFO #15),使用自定義的 TCP 堆疊。這幾年在 DPDK kit 中開始流行起來。

但在沒有使用者模式驅動的情況下,將 TCP 遷移到 UDP 也會帶來相同的效能提升。與呼叫熟悉的 recv() 函式一次接收單個數據包不同,你可以呼叫 recvmmsg() 來同時接收一組 UDP 資料包。它仍然是一個核心/使用者模式轉換的情況,但在同時收到一百個包進行分攤比對每個包進行轉換要好得多。

在我的測試中,使用典型的 recv() 函式限制每秒為 5 十萬個 UDP 資料包,但使用 recvmmsg() 和其他一些優化手段(使用 RSS 的多核),在低端四核伺服器上每秒可以獲取 5 百萬個 UDP 資料包。這是由於每個核都帶有良好的可伸縮性,移動到有 64 個核的強大伺服器應該會進一步改善結果。

順便說一句,“RSS” 是網路硬體的一個特性,它將傳入的資料包分成多個接收佇列。多核可伸縮性的最大問題是當兩個 CPU 核心需要同時讀取/修改同一件事時,共享相同的 UDP 資料包佇列會變成最大的瓶頸。因此,Intel 首先和其他乙太網供應商增加了 RSS,使每個核都有自己的非共享資料包佇列。Linux 和其他作業系統升級了 UDP,以支援單個套接字(SO_REUSEPORT)的多個檔案描述符來處理多個佇列。現在,QUIC 利用這些進步,允許每個核管理自己的 UDP 資料包流,而不存在與其他 CPU 核共享東西的可伸縮性問題。我之所以提及,是因為我曾在 2000 年時,與 Intel 硬體工程師討論過建立多個分組佇列的問題。這是一個常見的問題,也是一個顯而易見的解決方案,在過去的二十年裡,看到它的進展一直很有趣,知道它以 HTTP/3 的形式出現在頂層。如果沒有網路硬體中的 RSS,QUIC 也不太可能會成為標準。

QUIC 另一個很酷的解決方案是移動支援。你的電腦或者手機會隨著你移動到不同的 WIFI 網路環境而改變自身的 IP 地址。作業系統和協議沒有優雅地關閉舊連線並開啟新的連線。然後在 QUIC 協議下,連線的識別符號不再是“套接字”(源/目的埠/地址協議組合)的傳統概念,而是分配給連線的 64 位識別符號。

這意味著,當你移動時,你可以在 IP 地址不斷變化的情況下,持續地從 YouTube 獲取一個數據流,或者繼續進行視訊電話呼叫,而不會被中斷。幾十年來,網際網路工程師一直在與“移動 IP”作鬥爭,試圖想出一個可行的解決方案。他們關注的是端到端的原則,即在你移動的時候,以某種方式保持一個固定的 IP 地址,但這不是一個實際的解決方案。很榮幸可以看到在現實世界中有 QUIC/HTTP/3 這樣一個可行的方案,最終解決了這個問題。

如何使用這些新的傳輸工具?幾十年來,網路程式設計的標準一直是傳輸層 API,即“套接字”。也就是呼叫像 recv() 這樣的函式來接收程式碼中的資料包。在 QUIC/HTTP/3,我們不再有作業系統傳輸層 API。取而代之的是一個更高層次的特性,你刻意在類似 GO 程式語言中使用它,或者在 OpenResty Nginx Web 伺服器中使用 Lua。

我之所以會提及,是因為你在 OSI 模型的教育學習中錯過了一件事,那就是它最初設想的是每個人都會編寫到應用層(7)API,而不是傳輸層(4)API。應該有像應用程式服務元素之類的能以標準方式為不同的應用程式處理像檔案傳輸和訊息傳遞之類的操作。尤其是包括 Go、QUIC、protobufs 等在 Google 的驅動下,人們會越來越傾向於這種模式。

我之所以會提到這一點,是因為 Google 和 微軟之間形成了鮮明的對比。微軟擁有一個流行的作業系統,所以它的創新是有它在該作業系統內所能做的事情驅動的。Google 的創新是由它能在作業系統上安裝的東西所驅動的。還有 Facebook 和 Amazon 本身,它們必須在谷歌所提供給他們的堆疊之上(或者外部)進行創新。世界上排名前五位的公司依次是 Apple、Google、Microsoft、Amazon、Facebook,因此每個公司推動創新的位置都很重要。

結論

當 TCP 在 20 世紀 70 年代被建立時,它是偉大的。它所處理的操作,諸如擁塞,遠遠好過相互競爭的協議。儘管人們聲稱 IPv4 沒有預料到超過 40 億個地址的事情,但它比 70 年代和 80 年代的競爭設計要好得多。IPv4 升級到 IPv6 很大程度上維持了 IP 的發展。類似的,TCP 升級到 QUIC 也延續了 TCP 的發展,不同的是在於它是為了滿足現代需求而進行的擴充套件。而令人驚訝的,TCP 竟然可以在一直未升級的狀態下維持了這麼長時間還仍在被使用著。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄