常莽
超文本傳輸協議(Http)是分布式協作超媒體信息系統的應用協議,超文本傳輸協議是萬維網數據通信的基礎,在萬維網中超文本文檔包括用戶可以訪問其他資源的超鏈接。Http超文本傳輸協議在網絡中如同空氣一般,感觸不到它的存在但又是無處不在。
萬維網之父蒂姆·伯納斯·李是英國工程師和計算機科學家,是最著名的萬維網的發明者,他是牛津大學計算機科學教授和麻省理工學院教授。他于1989年3月12日提出了一種信息管理系統,然后在同年11月中旬通過Internet實現了超文本傳輸協議Http客戶端和服務器之間的首次成功通信。他是萬維網聯盟W3C的負責人,該聯盟負責監督Web的持續發展。他還是萬維網基金會的創始人,還是麻省理工學院計算機科學和人工智能實驗室CSAIL的3Com創始人主席和高級研究員,他也是網絡科學研究計劃WSRI的主任和MIT集體智慧中心的顧問委員會成員,他也是開放數據研究所的創始人兼總裁,目前是社交網絡MeWe的顧問。
2004年,伯納斯·李因其開創性工作而被女王伊麗莎白二世封為爵士。在2009年4月,他當選為美國國家科學院外籍研究員,位列《時代》雜志的20世紀100位最重要人物名單被譽為“萬維網發明者”獲得了2016年圖靈獎。
Http各個版本的基本情況
Http協議經過20多年的演進出現過0.9,1.0,1.1,2.0,3.0,5個主要版本。

1. A.Http0.9版本
0.9是鼻祖版本,它的主要特點包括:
請求方法支持有限
只支持GET請求方式,不支持其他請求方式因此客戶端向服務端傳輸信息的量非常有限,現在常用的POST請求無法使用。
不支持請求頭header
不能在請求中指定版本號,服務端只具有返回HTML字符串的能力。
響應即關閉
服務端相響應之后,立即關閉TCP連接。
2. B.Http1.0版本
1.0版本主要是對0.9版本的強化,效果也比較明顯,主要特性和缺點包括:
新增了請求方法
請求方式新增了POST,DELETE,PUT,HEADER等方式,提高了客戶端向服務端發送信息的量級。
增加請求頭和響應頭
增添了請求頭和響應頭的概念,可以在通信中指定了Http協議版本號,以及其他HEADER信息,使得C/S交互更加靈活方便。
豐富數據傳輸內容
擴充了傳輸內容格式包括:圖片、音視頻資源和二進制等都可以進行傳輸,相比0.9版本的只能傳輸HTML內容讓Http的應用場景更多。
鏈接復用性差
1.0版本中每個TCP連接只能發送一個請求,數據發送完畢連接就關閉,如果還要請求其他資源,就必須重新建立連接。TCP為了保證正確性和可靠性需要客戶端和服務器3次握手和4次揮手,因此建立連接成本很高,基于擁塞控制開始時發送速率較慢,所以1.0版本的性能并不理想。
無狀態無連接的弊端
1.0版本是無狀態且無連接的,換句話說就是服務器不跟蹤不記錄請求過的狀態,客戶端每次請求都需要建立TCP連接不能復用,并且1.0規定在前一個請求響應到達之后下一個請求才能發送,如果前一個阻塞后面的請求就會被阻塞。丟包、亂序以及高成本的鏈接過程,讓復用和隊頭阻塞產生很多問題,所以無連接無狀態是1.0版本的一個弱肋。
3. C.Http 1.1版本
1.1版本在1.0版本發布后大約1年就推出了,是對1.0版本的優化和完善,1.1版本的主要特點包括:
增加長連接
新增Connection字段,可以設置keep-alive值保持連接不斷開,即TCP連接默認不關閉,可以被多個請求復用,這也是1.1版本很重要的優化,但是在S端服務器只有處理完一個回應,才會進行下一個回應。要是前面的回應特別慢,后面就會有許多請求排隊等著,仍然存在隊頭阻塞問題。
管道化
在長連接的基礎上,管道化可以不等第一個請求響應繼續發送后面的請求,但響應的順序還是按照請求的順序返回,即在同一個TCP連接中,客戶端可以同時發送多個請求,進一步改進了Http協議的傳輸效率。
更多的請求方法
增加了PUT,PATCH,OPTIONS,DELETE等請求方式。
Host字段
Host字段用來指定服務器的域名,這樣就可以將多種請求發往同一臺服務器上的不同網站,提高了機器的復用,這個也是重要的優化
4. D.Http 2.0版本
2.0版本是個里程碑式的版本,相比1.x版本有了非常多的優化去適應當前的網絡場景,其中幾個重要功能點包括:
二進制格式
1.x是文本協議,然而2.0是以二進制幀為基本單位,可以說是一個二進制協議,將所有傳輸的信息分割為消息和幀,并采用二進制格式的編碼,一幀中包含數據和標識符,使網絡傳輸變得高效而靈活。
多路復用
這是一個非常重要的改進,1.x中建立多個連接的消耗以及效率都存在問題,2.0版本的多路復用多個請求共用一個連接,多個請求可以同時在一個TCP連接上并發,主要借助于二進制幀中的標識進行區分實現鏈路的復用。
頭部壓縮
2.0版本使用HPACK算法對頭部HEADER數據進行壓縮,從而減少請求的大小提高效率,這個非常好理解,之前每次發送都要帶相同的HEADER,顯得很冗余,2.0版本對頭部信息進行增量更新有效地減少了頭部數據的傳輸。
服務端推送
之前1.x版本服務端都是收到請求后被動執行,在2.0版本允許服務器主動向客戶端發送資源,這樣在客戶端可以起到加速的作用。
Http2.0詳解
前面對比了幾個版本的演進和優化過程,接下來深入研究下2.0版本的一些特性及其基本實現原理。
從對比來看2.0版本并不是在1.1版本上的一些優化而是革新,因為2.0背負了更多的性能目標任務,1.1雖然增加了長連接和管道化,但是從根本上并沒有實現真正的高性能。2.0的設計目標是在兼容1.x語義和操作的基礎上,給用戶帶來更快捷、簡單和安全的體驗,以及高效地利用當前網絡帶寬,為此2.0做了很多調整主要包括:二進制化分幀、多路復用和頭部壓縮等。
1. SPDY協議
說到2.0版本標準和新特性就必須提谷歌的SPDY協議,SPDY是Google開發的基于TCP的會話層協議,可以最小化網絡延遲提升網絡速度、優化用戶的網絡使用體驗。SPDY并不是一種用于替代Http的協議,而是對Http協議的增強。新協議的功能包括數據流的多路復用、請求優先級以及Http報頭壓縮,谷歌表示引入SPDY協議后,在實驗室測試中頁面加載速度比原先快64 %。
隨后SPDY協議得到Chrome,Firefox等大型瀏覽器的支持,這個高效的協議引起了Http工作組的注意,在此基礎上制定了官方Http 2.0標準。之后幾年SPDY和Http 2.0繼續演進相互促進,Http 2.0讓服務器、瀏覽器和網站開發者在新協議中獲得更好的體驗,很快被大眾所認可。
2.二進制分幀層
二進制分幀層在不修改請求方法和語義的基礎上,重新設計了編碼機制。

二進制編碼機制使得通信可以在單個TCP連接上進行,該連接在整個對話期間一直處于活躍狀態。
二進制協議將通信數據分解為更小的幀,數據幀充斥在C/S之間的雙向數據流中,就像雙向多車道的高速路,來往如織川流不息。
要理解二進制分幀層需要知道4個概念:
鏈接Link:就是指一條C/S之間的TCP鏈接,這是個基礎的鏈路數據的高速公路。
數據流Stream:已建立的TCP連接內的雙向字節流,TCP鏈接中可以承載一條或多條消息。
消息Message:消息屬于一個數據流,消息就是邏輯請求或響應消息對應的完整的一系列幀,也就是幀組成了消息。
幀Frame:幀是通信的最小單位,每個幀都包含幀頭和消息體,標識出當前幀所屬的數據流。
四者是一對多的包含關系。
再來看一下Headers Frame頭部幀的結構:從各個域可以看到長度、類型、標志位、流標識符和數據凈荷等,感興趣可以閱讀一下相關文檔。
總之2.0版本將通信數據分解為二進制編碼幀進行交換,每個幀對應著特定數據流中的特定消息,所有幀和流都在一個TCP連接內復用,二進制分幀協議是2.0其他功能和性能優化的重要基礎。
3.多路復用
1.1版本中存在隊首阻塞問題,因此如果客戶端要發起多個并行請求來提升性能,必須使用多個TCP連接,這樣就要承受更大延時和建鏈拆鏈成本,不能有效利用TCP鏈接。
2.0版本中使用新的二進制分幀協議突破了1.0的諸多限制,從根本上實現了真正的請求和響應多路復用。客戶端和服務器將交互數據分解為相互獨立的幀,互不影響地交錯傳輸,最后再在對端根據幀頭中的流標識符把它們重新組裝起來,從而實現了TCP鏈接的多路復用。

4.首部壓縮
A.HEADER冗余傳輸
Http請求都有HEADER部分,并且相對于一條鏈接而言大部分包的header部分都是相同的,這樣的話每次傳輸相同的部分確實非常浪費。
現代網絡中每個網頁平均包含100多個Http請求,每個請求頭平均有300~500 Byte,總數據量達到幾十KB以上,這樣可能造成數據延時,尤其復雜的WiFi環境或者蜂窩網絡。但是這些請求頭之間通常幾乎沒有變化,在本已經擁擠的鏈路中多次傳輸相同的數據部分確實不是高效做法。
基于TCP設計的擁塞控制具有線增積減AIMD特性,如果發生丟包傳輸速率將大幅度下降,這樣在擁擠的網絡環境中大的包頭意味著只能加劇擁塞,造成低速率傳輸。
B.Http壓縮和犯罪攻擊
在2.0版本的HPACK算法之前,Http壓縮使用gzip去壓縮,后來提出的SPDY算法對Headers進行特殊設計,但是它依舊使用的是DEFLATE算法。
在后來的一些實際應用中發現DEFLATE和SPDY都有被攻擊的危險,因為DEFLATE算法使用后向字符串匹配和動態Huffman編碼,攻擊者可以控制部分請求頭部,通過修改請求部分然后看壓縮之后大小改變多少,如果變小了攻擊者就知道注入的文本和請求中的某些內容有重復。這個過程有點像俄羅斯方塊的消除過程,這樣經過一段時間的嘗試數據內容就可能被全部搞清楚,由于這種風險的存在才研發出更安全的壓縮算法。
C.HPACK算法
2.0版本中HPACK算法在C/S中使用首部表來存儲之前發送的鍵值對,對于相同數據通信期間幾乎不會改變的通用鍵值對只發送一次即可。
極端情況如果請求頭每次沒有變化,那么傳輸中則不包含首部,也就是首部就是零字節。如果首部鍵值對發生變化了,也只需要發送變化的數據,并且新增或修改的首部幀會被追加到首部表,首部表在鏈接存活期始終存在,并且由客戶端和服務器共同更新和維護。
簡單說就是客戶端和服務端共同維護了一個key-value的結構,發生變化時則更新傳輸,否則就不傳輸,這樣相當于在首次全量傳輸之后增量更新傳輸即可,這個思想在日常開發中也非常普遍,不用太復雜。

5.服務端推送
服務端推送是2.0版本新增的一個強大功能,和一問一答式的C/S交互不同,推送式交互中服務器可以對客戶端的一個請求發送多個響應,除了對最初請求的響應外還向客戶端推送額外資源,無需客戶端明確地請求也可以推送。
舉個例子:想象一下你去餐廳吃飯,服務好的快餐廳在你點好一份牛肉面之后,還會給你送上餐巾紙、筷子、勺子甚至調料等,這樣主動式的服務,節約了客人的時間并且提高了用餐體驗。
在實際的C/S交互中這種主動推送額外資源的方法很有效,因為幾乎每個網絡應用都會包含多種資源,客戶端需要全部逐個獲取他們,此時如果讓服務器提前推送這些資源,可以有效地減少額外的延遲時間,因為服務器可以知道客戶端下一步要請求什么資源。
Http 2.0和Http 3.0
科技永不止步,互聯網中業務是不斷迭代前進的,像Http這種重要的網絡協議也是如此,新版本是對舊版本的揚棄。
1. Http 2.0和TCP
Http2.0是2015年推出的,還是比較年輕的,其重要的二進制分幀協議、多路復用、頭部壓縮和服務端推送等重要優化使Http協議真正上了一個新臺階。
像谷歌這種重要的公司并沒有滿足于此,而且想繼續提升Http的性能,花最少的時間和資源獲取極致體驗。那么肯定要問Http2.0雖然性能已經不錯了,還有什么不足嗎?
建立連接時間長(本質上是TCP的問題);
隊頭阻塞問題;
移動互聯網領域表現不佳(弱網環境)。
熟悉Http2.0協議的同學應該知道,這些缺點基本都是由于TCP協議引起的,水能載舟亦能覆舟,其實TCP也很無辜。在我們眼里,TCP是面向連接、可靠的傳輸層協議,當前幾乎所有重要的協議和應用都是基于TCP來實現的。網絡環境的改變速度很快,但是TCP協議相對緩慢,正是這種矛盾促使谷歌做出了一個看似出乎意料的決定———基于UDP來開發新一代Http協議。

2.谷歌為什么選擇UDP
谷歌選擇UDP是看似出乎意料的,仔細想一想其實很有道理,我們來看看TCP協議的不足和UDP的一些優點:
基于TCP開發的設備和協議非常多,兼容困難;
TCP協議棧是Linux內部的重要部分,它的修改和升級成本很大;
UDP本身是無連接的、沒有建鏈和拆鏈成本;
UDP的數據包無隊頭阻塞問題;
UDP改造成本小。
從上面的對比可以知道,谷歌要想從TCP上進行改造升級絕非易事,但是UDP雖然沒有TCP為了保證可靠連接而引發的問題,但是UDP本身不可靠,又不能直接用。綜合而知,谷歌決定在UDP基礎上改造一個具備TCP協議優點的新協議也就順理成章了,這個新協議就是QUIC協議。
3. QUIC協議和Http 3.0
維基百科對于QUIC協議的一些介紹:QUIC協議最初由Google的Jim Roskind設計,實施并于2012年部署,在2013年隨著實驗的擴大而公開宣布,并向IETF進行了描述。
QUIC提高了當前正在使用TCP的面向連接的Web應用程序的性能。它在2個端點之間使用用戶數據報協議(UDP)建立多個復用連接來實現此目的。
QUIC的次要目標包括減少連接和傳輸延遲,在每個方向進行帶寬估計以避免擁塞。它還將擁塞控制算法移動到用戶空間,而不是內核空間,此外使用前向糾錯(FEC)進行擴展,以在出現錯誤時進一步提高性能。
Http 3.0又稱為Http Over QUIC,其棄用TCP協議,改為使用基于UDP協議的QUIC協議來實現。
QUIC協議
擇其善者而從之,其不善者而改之。Http3.0既然選擇了QUIC協議,也就意味著Http 3.0基本繼承了Http 2.0的強大功能,并且進一步解決了Http 2.0存在的一些問題,同時也必然引入了新的問題。
QUIC協議必須要實現Http2.0在TCP協議上的重要功能,同時解決遺留問題,我們來看看QUIC是如何實現的。
1.隊頭阻塞問題
隊頭阻塞是計算機網絡中是一種性能受限的現象,通俗來說就是:一個數據包影響了一堆數據包,它過不來大家都走不了。
隊頭阻塞問題可能存在于Http層和TCP層,在Http1.x時2個層次都存在該問題。

Http2.0協議的多路復用機制解決了Http層的隊頭阻塞問題,但是在TCP層仍然存在隊頭阻塞問題。
TCP協議在收到數據包之后,這部分數據可能是亂序到達的,但是TCP必須將所有數據收集排序整合后給上層使用,如果其中某個包丟失了,就必須等待重傳,從而出現某個丟包數據阻塞整個連接的數據使用。
QUIC協議是基于UDP協議實現的,在一條鏈接上可以有多個流,流與流之間是互不影響的,當一個流出現丟包影響范圍非常小,從而解決隊頭阻塞問題。
2. 0RTT建鏈
衡量網絡建鏈的常用指標是RTT,也就是數據包一來一回的時間消耗。

RTT包括3部分:往返傳播時延、網絡設備內排隊時延和應用程序數據處理時延。
HttpS協議要建立完整鏈接包括:TCP握手和TLS握手,總計需要至少2~3個RTT,普通的Http協議也需要至少1個RTT才可以完成握手。然而,QUIC協議可以在第一個包就包含有效的應用數據,從而實現0RTT,但這也是有條件的。
簡單來說,基于TCP協議和TLS協議的Http 2.0在真正發送數據包之前需要花費一些時間來完成握手和加密協商,完成之后才可以真正傳輸業務數據。但是QUIC則第一個數據包就可以發業務數據,從而在連接延時方面有很大優勢,可以節約數百毫秒的時間。
QUIC的0RTT也是需要條件的,對于第一次交互的客戶端和服務端0RTT也是做不到的,畢竟雙方完全陌生,因此,QUIC協議分為首次連接和非首次連接2種情況。
3.首次連接和非首次連接
使用QUIC協議的客戶端和服務端要使用1RTT進行密鑰交換,使用的交換算法是迪菲·赫爾曼(Diffie-Hellman,DH)算法。DH算法開辟了密鑰交換的新思路,RSA算法也是基于這種思想實現的,但是DH算法和RSA的密鑰交換不完全一樣,感興趣的讀者可以看看DH算法的數學原理。
首次連接
簡單來說,首次連接時客戶端和服務端的密鑰協商和數據傳輸過程,其中涉及了DH算法的基本過程。
非首次連接
客戶端和服務端首次連接時服務端傳遞了config包,里面包含了服務端公鑰和2個隨機數,客戶端會將config存儲下來,后續再連接時可以直接使用,從而跳過這個1RTT,實現0RTT的業務數據交互。
客戶端保存config是有時間期限的,在config失效之后仍然需要進行首次連接時的密鑰交換。
4.前向安全問題
前向安全是密碼學領域的專業術語,百度上的解釋是:前向安全或前向保密Forward Secrecy是密碼學中通信協議的安全屬性,是指長期使用的主密鑰泄漏不會導致過去的會話密鑰泄漏。
前向安全能夠保護過去進行的通信不受密碼或密鑰在未來暴露的威脅,如果系統具有前向安全性,就可以保證在主密鑰泄露時歷史通信的安全,即使系統遭到主動攻擊也是如此。通俗來說,前向安全指的是密鑰泄漏也不會讓之前加密的數據被泄漏,影響的只有當前,對之前的數據無影響。
前面提到QUIC協議首次連接時先后生成了2個加密密鑰,由于config被客戶端存儲了,如果期間服務端私鑰泄漏,那么可以根據K = mod p,計算出密鑰K。
如果一直使用這個密鑰進行加解密,那么就可以用K解密所有歷史消息,因此后續又生成了新密鑰,使用其進行加解密,當時完成交互時則銷毀,從而實現了前向安全。
5.前向糾錯
前向糾錯是通信領域的術語,也叫前向糾錯碼(Forward Error Correction,FEC)是增加數據通信可信度的方法,在單向通信信道中,一旦錯誤被發現,其接收器將無權再請求傳輸。FEC是利用數據進行傳輸冗余信息的方法,當傳輸中出現錯誤,將允許接收器再建數據。
QUIC每發送一組數據就對這組數據進行異或運算,并將結果作為一個FEC包發送出去,接收方收到這一組數據后根據數據包和FEC包即可進行校驗和糾錯。
6.連接遷移
網絡切換幾乎無時無刻都在發生,TCP協議使用五元組來表示一條唯一的連接,當從4G環境切換到WiFi環境時,手機的IP地址就會發生變化,這時必須創建新的TCP連接才能繼續傳輸數據。
QUIC協議基于UDP實現摒棄了五元組的概念,使用64位的隨機數作為連接的ID,并使用該ID表示連接。
基于QUIC協議之下,在日常WiFi和4G切換時,或者不同基站之間切換都不會重連,從而提高業務層的體驗。
QUIC的應用和前景
通過前面的一些介紹可以看出QUIC協議雖然是基于UDP來實現的,但是它將TCP的重要功能都進行了延續和優化,否則使用者是不會買賬的。
QUIC協議的核心思想是將TCP協議在內核實現的諸如可靠傳輸、流量控制和擁塞控制等功能轉移到用戶態來實現,同時在加密傳輸方向的嘗試也推動了TLS1.3的發展。但是TCP協議的勢力過于強大,很多網絡設備甚至對于UDP數據包做了很多不友好的策略,甚至進行攔截從而導致成功連接率下降。
主導者谷歌在自家產品做了很多嘗試,國內騰訊公司也做了很多關于QUIC協議的嘗試。其中騰訊云對QUIC協議表現了很大的興趣,并做了一些優化后在一些重點產品中對連接遷移、QUIC成功率和弱網環境耗時等進行了實驗,給出了來自生產環境的諸多寶貴數據。
任何新生事物的推動都是需要時間的,出現多年的Http 2.0和HttpS協議的普及度都沒有預想高,IPv6也是如此,不過QUIC已經展現出了強大的生命力。
本文通過介紹Http協議的歷史演進、各個版本的主要特征和優缺點、重點介紹了Http 2.0協議的一些特性,包括:SPDY協議、二進制分幀協議、多路復用、首部壓縮和服務端推送等重要功能。雖然Http2.0版本協議有很多非常優秀的功能并且在2015年正式發布,現在國內外一些大廠基本都有使用Http 2.0承擔部分請求,但是目前仍然未廣泛普及。
其實Http3.0版本在2018年也已經推出來了,但是Http 2.0和Http 3.0的推廣和普及還是需要時間的。
不過現在來看QUIC協議,雖然基于UDP主體將TCP的重要功能轉移到用戶空間來實現,從而繞開內核實現用戶態的TCP協議,但是真正實現起來還是非常復雜的。