那些年你所不知道的HTTP前世今生

HTTP (Hypertext transfer protocol) 翻譯成中文是超文本傳輸協議,是互聯網上重要的一個協議,由歐洲核子研究委員會CERN的英國工程師 Tim Berners-Lee v發明的,同時,他也是WWW的發明人,最初的主要是用於傳遞通過HTML封裝過的數據。在1991年發佈了HTTP 0.9版,在1996年發佈1.0版,1997年是1.1版,1.1版也是到今天為止傳輸最廣泛的版本(初始RFC 2068 在1997年發佈, 然後在1999年被 RFC 2616 取代,再在2014年被 RFC 7230 /7231/7232/7233/7234/7235取代),2015年發佈了2.0版,其極大的優化了HTTP/1.1的性能和安全性,而2018年發佈的3.0版,繼續優化HTTP/2,激進地使用UDP取代TCP協議,目前,HTTP/3 在2019年9月26日 被 Chrome,Firefox,和Cloudflare支持,所以我想寫下這篇文章,簡單地說一下HTTP的前世今生,讓大家學到一些知識,並希望可以在推動一下HTTP標準協議的發展。

HTTP 0.9 / 1.0

0.9和1.0這兩個版本,就是最傳統的 request – response的模式了,HTTP 0.9版本的協議簡單到極點,請求時,不支持請求頭,只支持 GET 方法,沒了。HTTP 1.0 擴展了0.9版,其中主要增加了幾個變化:


  • 在請求中加入了HTTP版本號,如:GET /coolshell/index.html HTTP/1.0
  • HTTP 開始有 header了,不管是request還是response 都有header了。
  • 增加了HTTP Status Code 標識相關的狀態碼。
  • 還有 Content-Type 可以傳輸其它的文件了。

我們可以看到,HTTP 1.0 開始讓這個協議變得很文明瞭,一種工程文明。因為:

  • 一個協議有沒有版本管理,是一個工程化的象徵。
  • header是協議可以說是把元數據和業務數據解耦,也可以說是控制邏輯和業務邏輯的分離。
  • Status Code 的出現可以讓請求雙方以及第三方的監控或管理程序有了統一的認識。最關鍵是還是控制錯誤和業務錯誤的分離。

(注:國內很多公司HTTP無論對錯只返回200,這種把HTTP Status Code 全部抹掉完全是一種工程界的倒退)

但是,HTTP1.0性能上有一個很大的問題,那就是每請求一個資源都要新建一個TCP鏈接,而且是串行請求,所以,就算網絡變快了,打開網頁的速度也還是很慢。所以,HTTP 1.0 應該是一個必須要淘汰的協議了。

HTTP/1.1

HTTP/1.1 主要解決了HTTP 1.0的網絡性能的問題,以及增加了一些新的東西:

  • 可以設置 keepalive 來讓HTTP重用TCP鏈接,重用TCP鏈接可以省了每次請求都要在廣域網上進行的TCP的三次握手的巨大開銷。這是所謂的“HTTP 長鏈接” 或是 “請求響應式的HTTP 持久鏈接”。英文叫 HTTP Persistent connection.
  • 然後支持pipeline網絡傳輸,只要第一個請求發出去了,不必等其回來,就可以發第二個請求出去,可以減少整體的響應時間。(注:非冪等的POST 方法或是有依賴的請求是不能被pipeline化的)
  • 支持 Chunked Responses ,也就是說,在Response的時候,不必說明 Content-Length 這樣,客戶端就不能斷連接,直到收到服務端的EOF標識。這種技術又叫 “
    服務端Push模型”,或是 “服務端Push式的HTTP 持久鏈接
  • 還增加了 cache control 機制。
  • 協議頭注增加了 Language, Encoding, Type 等等頭,讓客戶端可以跟服務器端進行更多的協商。
  • 還正式加入了一個很重要的頭—— HOST這樣的話,服務器就知道你要請求哪個網站了。因為可以有多個域名解析到同一個IP上,要區分用戶是請求的哪個域名,就需要在HTTP的協議中加入域名的信息,而不是被DNS轉換過的IP信息。
  • 正式加入了 OPTIONS 方法,其主要用於 CORS – Cross Origin Resource Sharing 應用。

HTTP/1.1應該分成兩個時代,一個是2014年前,一個是2014年後,因為2014年HTTP/1.1有了一組RFC(7230 /7231/7232/7233/7234/7235),這組RFC又叫“HTTP/2 預覽版”。其中影響HTTP發展的是兩個大的需求:

  • 一個需要是加大了HTTP的安全性,這樣就可以讓HTTP應用得廣泛,比如,使用TLS協議。
  • 另一個是讓HTTP可以支持更多的應用,在HTTP/1.1 下,HTTP已經支持四種網絡協議:傳統的短鏈接。可重用TCP的的長鏈接模型。服務端push的模型。WebSocket模型。

自從2005年以來,整個世界的應用API越來多,這些都造就了整個世界在推動HTTP的前進,我們可以看到,自2014的HTTP/1.1 以來,這個世界基本的應用協議的標準基本上都是向HTTP看齊了,也許2014年前,還有一些專用的RPC協議,但是2014年以後,HTTP協議的增強,讓我們實在找不出什麼理由不向標準靠攏,還要重新發明輪子了。

HTTP/2

雖然 HTTP/1.1 已經開始變成應用層通訊協議的一等公民了,但是還是有性能問題,雖然HTTP/1.1 可以重用TCP鏈接,但是請求還是一個一個串行發的,需要保證其順序。然而,大量的網頁請求中都是些資源類的東西,這些東西佔了整個HTTP請求中最多的傳輸數據量。所以,理論上來說,如果能夠並行這些請求,那就會增加更大的網絡吞吐和性能。

另外,HTTP/1.1傳輸數據時,是以文本的方式,藉助耗CPU的zip壓縮的方式減少網絡帶寬,但是耗了前端和後端的CPU。這也是為什麼很多RPC協議詬病HTTP的一個原因,就是數據傳輸的成本比較大。

其實,在2010年時,Google 就在搞一個實驗型的協議,這個協議叫SPDY,這個協議成為了HTTP/2的基礎(也可以說成HTTP/2就是SPDY的復刻)。HTTP/2基本上解決了之前的這些性能問題,其和HTTP/1.1最主要的不同是:

  • HTTP/2是一個二進制協議,增加了數據傳輸的效率。
  • HTTP/2是可以在一個TCP鏈接中併發請求多個HTTP請求,移除了HTTP/1.1中的串行請求。
  • HTTP/2會壓縮頭,如果你同時發出多個請求,他們的頭是一樣的或是相似的,那麼,協議會幫你消除重複的部分。這就是所謂的HPACK算法(參看RFC 7541 附錄A)
  • HTTP/2允許服務端在客戶端放cache,又叫服務端push,也就是說,你沒有請求的東西,我服務端可以先送給你放在你的本地緩存中。比如,你請求X,我服務端知道X依賴於Y,雖然你沒有的請求Y,但我把把Y跟著X的請求一起返回客戶端。

對於這些性能上的改善,在Medium上有篇文章你可看一下相關的細節說明和測試“HTTP/2: the difference between HTTP/1.1, benefits and how to use it”

當然,還需要注意到的是HTTP/2的協議複雜度比之前所有的HTTP協議的複雜度都上升了許多許多,其內部還有很多看不見的東西,比如其需要維護一個“優先級樹”來用於來做一些資源和請求的調度和控制。如此複雜的協議,自然會產生一些不同的聲音,或是降低協議的可維護和可擴展性。所以也有一些爭議。儘管如此,HTTP/2還是很快地被世界所採用。

HTTP/2 是2015年推出的,其發佈後,Google 宣佈移除對SPDY的支持,擁抱標準的 HTTP/2。過了一年後,就有8.7%的網站開啟了HTTP/2,根據 這份報告 ,截止至本文發佈時(2019年10月1日 ), 在全世界範圍內已經有41%的網站開啟了HTTP/2。

HTTP/2的官方組織在 Github 上維護了一份各種語言對HTTP/2的實現列表,大家可以去看看。

我們可以看到,HTTP/2 在性能上對HTTP有質的提高,所以,HTTP/2 被採用的也很快,所以,如果你在你的公司內負責架構的話,HTTP/2是你一個非常重要的需要推動的一個事,除了因為性能上的問題,推動標準落地也是架構師的主要職責,因為,你企業內部的架構越標準,你可以使用到開源軟件,或是開發方式就會越有效率,跟隨著工業界的標準的發展,你的企業會非常自然的享受到標準所帶來的紅利。

HTTP/3

然而,這個世界沒有完美的解決方案,HTTP/2也不例外,其主要的問題是:若干個HTTP的請求在複用一個TCP的連接,底層的TCP協議是不知道上層有多少個HTTP的請求的,所以,一旦發生丟包,造成的問題就是所有的HTTP請求都必需等待這個丟了的包被重傳回來,哪怕丟的那個包不是我這個HTTP請求的。因為TCP底層是沒有這個知識了。

這個問題又叫Head-of-Line Blocking問題,這也是一個比較經典的流量調度的問題。這個問題最早主要的發生的交換機上。下圖來自Wikipedia。

那些年你所不知道的HTTP前世今生

圖中,左邊的是輸入隊列,其中的1,2,3,4表示四個隊列,四個隊列中的1,2,3,4要去的右邊的output的端口號。此時,第一個隊列和第三個隊列都要寫右邊的第四個端口,然後,一個時刻只能處理一個包,所以,一個隊列只能在那等另一個隊列寫完後。然後,其此時的3號或1號端口是空閒的,而隊列中的要去1和3號端號的數據,被第四號端口給block住了。這就是所謂的HOL blocking問題。

HTTP/1.1中的pipeline中如果有一個請求block了,那麼隊列後請求也統統被block住了;HTTP/2 多請求複用一個TCP連接,一旦發生丟包,就會block住所有的HTTP請求。這樣的問題很討厭。好像基本無解了。

是的TCP是無解了,但是UDP是有解的 !於是HTTP/3破天荒地把HTTP底層的TCP協議改成了UDP!

然後又是Google 家的協議進入了標準 – QUIC (Quick UDP Internet Connections)。接下來是QUIC協議的幾個重要的特性,為了講清楚這些特性,我需要帶著問題來講(注:下面的網絡知識,如果你看不懂的話,你需要學習一下《TCP/IP詳解》一書(在我寫blog的這15年裡,這本書推薦了無數次了),或是看一下本站的《TCP的那些事》。):

  • 首先是上面的Head-of-Line blocking問題,在UDP的世界中,這個就沒了。這個應該比較好理解,因為UDP不管順序,不管丟包(當然,QUIC的一個任務是要像TCP的一個穩定,所以QUIC有自己的丟包重傳的機制)
  • TCP是一個無私的協議,也就是說,如果網絡上出現擁塞,大家都會丟包,於是大家都會進入擁塞控制的算法中,這個算法會讓所有人都“冷靜”下來,然後進入一個“慢啟動”的過程,包括在TCP連接建立時,這個慢啟動也在,所以導致TCP性能迸發地比較慢。QUIC基於UDP,使用更為激進的方式。同時,QUIC有一套自己的丟包重傳和擁塞控制的協,一開始QUIC是重新實現一TCP 的 CUBIC算法,但是隨著BBR算法的成熟(BBR也在借鑑CUBIC算法的數學模型),QUIC也可以使用BBR算法。這裡,多說幾句,從模型來說,以前的TCP的擁塞控制算法玩的是數學模型,而新型的TCP擁塞控制算法是以BBR為代表的測量模型,理論上來說,後者會更好,但QUIC的團隊在一開始覺得BBR不如CUBIC的算法好,所以沒有用。現在的BBR 2.x借鑑了CUBIC數學模型讓擁塞控制更公平。這裡有文章大家可以一讀“TCP BBR : Magic dust for network performance.”
  • 接下來,現在要建立一個HTTPS的連接,先是TCP的三次握手,然後是TLS的三次握手,要整出六次網絡交互,一個鏈接才建好,雖說HTTP/1.1和HTTP/2的連接複用解決這個問題,但是基於UDP後,UDP也得要實現這個事。於是QUIC直接把TCP的和TLS的合併成了三次握手(對此,在HTTP/2的時候,是否默認開啟TLS業內是有爭議的,反對派說,TLS在一些情況下是不需要的,比如企業內網的時候,而支持派則說,TLS的那些開銷,什麼也不算了)。
那些年你所不知道的HTTP前世今生

那些年你所不知道的HTTP前世今生


所以,QUIC是一個在UDP之上的偽TCP +TLS +HTTP/2的多路複用的協議。

但是對於UDP還是有一些挑戰的,這個挑戰主要來自互聯網上的各種網絡設備,這些設備根本不知道是什麼QUIC,他們看QUIC就只能看到的就是UDP,所以,在一些情況下,UDP就是有問題的,

  • 比如在NAT的環境下,如果是TCP的話,NAT路由或是代理服務器,可以通過記錄TCP的四元組(源地址、源端口,目標地址,目標端口)來做連接映射的,然而,在UDP的情況下不行了。於是,QUIC引入了個叫connection id的不透明的ID來標識一個鏈接,用這種業務ID很爽的一個事是,如果你從你的3G/4G的網絡切到WiFi網絡(或是反過來),你的鏈接不會斷,因為我們用的是connection id,而不是四元組。
  • 然而就算引用了connection id,也還是會有問題 ,比如一些不夠“聰明”的等價路由交換機,這些交換機會通過四元組來做hash把你的請求的IP轉到後端的實際的服務器上,然而,他們不懂connection id,只懂四元組,這麼導致屬於同一個connection id但是四元組不同的網絡包就轉到了不同的服務器上,這就是導致數據不能傳到同一臺服務器上,數據不完整,鏈接只能斷了。所以,你需要更聰明的算法(可以參看 Facebook 的 Katran 開源項目 )

好了,就算搞定上面的東西,還有一些業務層的事沒解,這個事就是 HTTP/2的頭壓縮算法 HPACK,HPACK需要維護一個動態的字典表來分析請求的頭中哪些是重複的,HPACK的這個數據結構需要在encoder和decoder端同步這個東西。在TCP上,這種同步是透明的,然而在UDP上這個事不好乾了。所以,這個事也必需要重新設計了,基於QUIC的QPACK就出來了,利用兩個附加的QUIC steam,一個用來發送這個字典表的更新給對方,另一個用來ack對方發過來的update。

目前看下來,HTTP/3目前看上去沒有太多的協議業務邏輯上的東西,更多是HTTP/2 + QUIC協議。但,HTTP/3 因為動到了底層協議,所以,在普及方面上可能會比 HTTP/2要慢的多的多。但是,可以看到QUIC協議的強大,細思及恐,QUIC這個協議真對TCP是個威脅,如果QUIC成熟了,TCP是不是會有可能成為歷史呢?

未來十年,讓我們看看UDP是否能夠逆襲TCP……

最後,小編想說:我是一名python開發工程師,

整理了一套最新的python系統學習教程,

想要這些資料的可以關注私信小編“01”即可(免費分享哦)希望能對你有所幫助


分享到:


相關文章: