理解TCP/IP協(xié)議棧之HTTP2.0
1 前言
今天一起來研究Http協(xié)議的一些事情,通過本文你將了解到以下內(nèi)容:
- Http協(xié)議各版本的對比和優(yōu)缺點(diǎn)
- Http2.0協(xié)議相關(guān)的SPDY協(xié)議、二進(jìn)制分幀協(xié)議、多路復(fù)用、首部壓縮、服務(wù)推送等基本原理
乘風(fēng)破浪前往知識的海洋吧, 大白船長 要開船了!
2. Http協(xié)議各版本的對比
Http超文本傳輸協(xié)議同空氣一般, 感觸不到它的存在但是又無處不在 ,筆者從維基百科摘錄了一些Http協(xié)議的發(fā)展歷程的簡單信息,一起來看下吧:
超文本傳輸協(xié)議是 分布式協(xié)作超媒體信息系統(tǒng)的應(yīng)用協(xié)議 。超文本傳輸協(xié)議是萬維網(wǎng)數(shù)據(jù)通信的基礎(chǔ),在萬維網(wǎng)中超文本文檔包括到用戶可以輕松訪問的其他資源的超鏈接。
蒂姆·伯納斯·李于1989年在歐洲核子研究中心發(fā)起了超文本傳輸協(xié)議的開發(fā)。早期的超文本傳輸協(xié)議征求意見(RFCs)的開發(fā)是由互聯(lián)網(wǎng)工程任務(wù)組(IETF)和萬維網(wǎng)聯(lián)盟(W3C)共同努力的結(jié)果,其工作后來轉(zhuǎn)移到IETF。
萬維網(wǎng)之父蒂姆·伯納斯·李簡介
Tim Berners-Lee是英國工程師和計(jì)算機(jī)科學(xué)家,最著名的是萬維網(wǎng)的發(fā)明者。他是 牛津大學(xué)計(jì)算機(jī)科學(xué)教授和麻省理工學(xué)院教授 。
他于1989年3月12日提出了一種信息管理系統(tǒng),然后在同年11月中旬通過Internet實(shí)現(xiàn)了超文本傳輸協(xié)議HTTP客戶端和服務(wù)器之間的 首次成功通信 。
他是萬維網(wǎng)聯(lián)盟W3C的負(fù)責(zé)人,該聯(lián)盟負(fù)責(zé)監(jiān)督Web的持續(xù)發(fā)展,他還是 萬維網(wǎng)基金會的創(chuàng)始人 ,還是麻省理工學(xué)院計(jì)算機(jī)科學(xué)和人工智能實(shí)驗(yàn)室CSAIL的3Com創(chuàng)始人主席和高級研究員,他也是網(wǎng)絡(luò)科學(xué)研究計(jì)劃WSRI的主任和MIT集體智慧中心的顧問委員會成員,他也是開放數(shù)據(jù)研究所的 創(chuàng)始人兼總裁 ,目前是社交網(wǎng)絡(luò)MeWe的顧問。
2004年,伯納斯·李因其開創(chuàng)性工作而被女王伊麗莎白二世 封為爵士 。在2009年4月,他當(dāng)選為 美國國家科學(xué)院外籍研究員 ,位列《時(shí)代》雜志的 20世紀(jì)100位最重要人物 名單被譽(yù)為“萬維網(wǎng)發(fā)明者”獲得了2016年 圖靈獎 。
http各個(gè)版本的基本情況
http協(xié)議經(jīng)過20多年的演進(jìn)出現(xiàn)過 0.9、1.0、1.1、2.0、3.0 五個(gè)主要版本,筆者畫了張圖看下:

A.Http0.9版本
0.9是鼻祖版本,它的主要特點(diǎn)包括:
- 請求方法支持有限只支持GET請求方式,不支持其他請求方式 因此客戶端向服務(wù)端傳輸信息的量非常有限,也就是現(xiàn)在常用的Post請求無法使用
- 不支持請求頭header不能在請求中指定版本號,服務(wù)端只具有返回HTML字符串的能力
- 響應(yīng)即關(guān)閉服務(wù)端相響應(yīng)之后,立即關(guān)閉TCP連接
B.Http1.0版本
1.0版本主要是對0.9版本的強(qiáng)化,效果也比較明顯,主要特性和缺點(diǎn)包括:
- 豐富請求方法請求方式新增了POST,DELETE,PUT,HEADER等方式,提高了客戶端向服務(wù)端發(fā)送信息的量級
- 增加請求頭和響應(yīng)頭增添了請求頭和響應(yīng)頭的概念,可以在通信中指定了HTTP協(xié)議版本號,以及其他header信息,使得C/S交互更加靈活方便
- 豐富數(shù)據(jù)傳輸內(nèi)容擴(kuò)充了傳輸內(nèi)容格式包括: 圖片、音視頻資源、二進(jìn)制 等都可以進(jìn)行傳輸,相比0.9的只能傳輸html內(nèi)容讓http的應(yīng)用場景更多
- 鏈接復(fù)用性差1.0版本中每個(gè)TCP連接只能發(fā)送一個(gè)請求,數(shù)據(jù)發(fā)送完畢連接就關(guān)閉,如果還要請求其他資源,就必須重新建立連接。TCP為了保證正確性和可靠性需要客戶端和服務(wù)器三次握手和四次揮手,因此建立連接成本很高,基于擁塞控制開始時(shí)發(fā)送速率較慢,所以1.0版本的 性能并不理想 。
- 無狀態(tài)無連接的弊端1.0版本是 無狀態(tài)且無連接 的,換句話說就是服務(wù)器不跟蹤不記錄請求過的狀態(tài),客戶端每次請求都需要建立tcp連接不能復(fù)用,并且1.0規(guī)定在前一個(gè)請求響應(yīng)到達(dá)之后下一個(gè)請求才能發(fā)送,如果前一個(gè)阻塞后面的請求就會被阻塞。 丟包和亂序問題 和高成本的鏈接過程讓復(fù)用和 隊(duì)頭阻塞 產(chǎn)生很多問題,所以 無連接無狀態(tài) 是1.0版本的一個(gè) 弱肋 。
C.Http1.1版本
1.1版本在1.0版本發(fā)布后大約1年就推出了,是 對1.0版本的優(yōu)化和完善 ,1.1版本的主要特點(diǎn)包括:
- 增加長連接新增Connection字段,可以設(shè)置keep-alive值保持連接不斷開,即 TCP 連接默認(rèn)不關(guān)閉,可以被多個(gè)請求復(fù)用,這也是1.1版本很重要的優(yōu)化,但是在S端服務(wù)器只有處理完一個(gè)回應(yīng),才會進(jìn)行下一個(gè)回應(yīng)。要是前面的回應(yīng)特別慢,后面就會有許多請求排隊(duì)等著,仍然存在隊(duì)頭阻塞問題。
- 管道化在長連接的基礎(chǔ)上,管道化可以不等第一個(gè)請求響應(yīng)繼續(xù)發(fā)送后面的請求,但響應(yīng)的順序還是按照請求的順序返回,即在同一個(gè)TCP連接中,客戶端可以同時(shí)發(fā)送多個(gè)請求,進(jìn)一步改進(jìn)了HTTP協(xié)議的傳輸效率。
- 更多的請求方法增加了 PUT、PATCH、OPTIONS、DELETE 等請求方式。
- host字段Host字段用來指定服務(wù)器的域名,這樣就可以將多種請求發(fā)往同一臺服務(wù)器上的不同網(wǎng)站,提高了機(jī)器的復(fù)用,這個(gè)也是重要的優(yōu)化
D.Http2.0版本
2.0版本是個(gè)里程碑式的版本,相比1.x版本有了非常多的優(yōu)化去適應(yīng)當(dāng)前的網(wǎng)絡(luò)場景,其中幾個(gè)重要功能點(diǎn)包括:
- 二進(jìn)制格式1.x是文本協(xié)議,然而2.0是以二進(jìn)制幀為基本單位,可以說是一個(gè)二進(jìn)制協(xié)議,將所有傳輸?shù)男畔⒎指顬橄⒑蛶?,并采用二進(jìn)制格式的編碼,一幀中包含數(shù)據(jù)和標(biāo)識符,使得網(wǎng)絡(luò)傳輸變得高效而靈活。
- 多路復(fù)用這是一個(gè)非常重要的改進(jìn),1.x中建立多個(gè)連接的消耗以及效率都存在問題,2.0版本的多路復(fù)用多個(gè)請求共用一個(gè)連接,多個(gè)請求可以同時(shí)在一個(gè)TCP連接上并發(fā),主要借助于二進(jìn)制幀中的標(biāo)識進(jìn)行區(qū)分實(shí)現(xiàn)鏈路的復(fù)用。
- 頭部壓縮2.0版本使用使用HPACK算法對頭部header數(shù)據(jù)進(jìn)行壓縮,從而減少請求的大小提高效率,這個(gè)非常好理解,之前每次發(fā)送都要帶相同的header,顯得很冗余,2.0版本對頭部信息進(jìn)行增量更新有效減少了頭部數(shù)據(jù)的傳輸。
- 服務(wù)端推送這個(gè)功能有點(diǎn)意思,之前1.x版本服務(wù)端都是收到請求后被動執(zhí)行,在2.0版本允許服務(wù)器主動向客戶端發(fā)送資源,這樣在客戶端可以起到加速的作用。
3 Http2.0 詳解
前面對比了幾個(gè)版本的演進(jìn)和優(yōu)化過程,接下來深入研究下2.0版本的一些特性及其基本實(shí)現(xiàn)原理。
從對比來看2.0版本并不是在1.1版本上的一些 優(yōu)化而是革新 ,因?yàn)?.0背負(fù)了更多的 性能目標(biāo)任務(wù) ,1.1雖然增加了長連接和管道化,但是從根本上并沒有實(shí)現(xiàn)真正的高性能。
2.0的設(shè)計(jì)目標(biāo)是在 兼容1.x語義和操作 的基礎(chǔ)上,給用戶帶來 更快捷、更簡單、更安全 的體驗(yàn)高效地利用當(dāng)前的網(wǎng)絡(luò)帶寬,為此2.0做了很多調(diào)整主要包括: 二進(jìn)制化分幀、多路復(fù)用、頭部壓縮 等。
akamai做了http2.0和http1.1在加載過程中的對比效果( 實(shí)驗(yàn)中加載379個(gè)小片段 在筆者的電腦上的加載時(shí)間是0.99s VS 5.80s ):

https://http2.akamai.com/demo
3.1 SPDY協(xié)議
要說2.0版本標(biāo)準(zhǔn)和新特性就必須提谷歌的 SPDY協(xié)議 ,看一下百度百科:
SPDY是Google開發(fā)的基于TCP的會話層協(xié)議,用以最小化網(wǎng)絡(luò)延遲,提升網(wǎng)絡(luò)速度,優(yōu)化用戶的網(wǎng)絡(luò)使用體驗(yàn)。SPDY并不是一種用于替代HTTP的協(xié)議,而是對HTTP協(xié)議的增強(qiáng)。
新協(xié)議的功能包括 數(shù)據(jù)流的多路復(fù)用、請求優(yōu)先級以及HTTP報(bào)頭壓縮 。谷歌表示引入SPDY協(xié)議后,在實(shí)驗(yàn)室測試中頁面加載速度比原先快64%。
隨后SPDY協(xié)議得到 Chrome、Firefox 等大型瀏覽器的支持,在一些大型網(wǎng)站和小型網(wǎng)站種部署,這個(gè)高效的協(xié)議引起了 HTTP工作組 的注意,在 此基礎(chǔ)上制定了官方Http2.0標(biāo)準(zhǔn) 。
之后幾年SPDY和Http2.0繼續(xù)演進(jìn)相互促進(jìn),Http2.0讓服務(wù)器、瀏覽器和網(wǎng)站開發(fā)者在新協(xié)議中獲得更好的體驗(yàn),很快被大眾所認(rèn)可。
3.2 二進(jìn)制分幀層
二進(jìn)制分幀層 binary framing layer 在不修改請求方法和語義的基礎(chǔ)上,重新設(shè)計(jì)了 編碼機(jī)制 ,如圖為http2.0分層結(jié)構(gòu)( 圖片來自參考4 ):

二進(jìn)制編碼機(jī)制使得通信可以在 單個(gè)TCP連接 上進(jìn)行,該連接在整個(gè)對話期間一直處于活躍狀態(tài)。
二進(jìn)制協(xié)議將通信 數(shù)據(jù)分解為更小的幀 ,數(shù)據(jù)幀充斥在C/S之間的雙向數(shù)據(jù)流中,就像雙向多車道的高速路,來往如織川流不息:
要理解二進(jìn)制分幀層需要知道四個(gè)概念:
- 鏈接Link就是指一條C/S之間的TCP鏈接,這是個(gè)基礎(chǔ)的鏈路數(shù)據(jù)的高速公路
- 數(shù)據(jù)流Stream已建立的TCP連接內(nèi)的雙向字節(jié)流,TCP鏈接中可以承載一條或多條消息
- 消息Message消息屬于一個(gè)數(shù)據(jù)流,消息就是邏輯請求或響應(yīng)消息對應(yīng)的完整的一系列幀,也就是幀組成了消息
- 幀F(xiàn)rame幀是通信的最小單位,每個(gè)幀都包含幀頭和消息體,標(biāo)識出當(dāng)前幀所屬的數(shù)據(jù)流
四者是 一對多的 包含 關(guān)系,筆者畫了一張圖:

再來看一下HeadersFrame頭部幀的結(jié)構(gòu):

再來看一下HeadersFrame頭部幀的結(jié)構(gòu):從各個(gè)域可以看到長度、類型、標(biāo)志位、 流標(biāo)識符、 數(shù)據(jù)凈荷等,感興趣可以閱讀rfc7540相關(guān)文檔。
- https://httpwg.org/specs/rfc7540.html
總之 2.0版本將通信數(shù)據(jù)分解為二進(jìn)制編碼幀進(jìn)行交換,每個(gè)幀對應(yīng)著特定數(shù)據(jù)流中的特定消息,所有幀和流都在一個(gè)TCP連接內(nèi)復(fù)用,二進(jìn)制分幀協(xié)議是2.0其他功能和性能優(yōu)化的重要基礎(chǔ)。

3.3 多路復(fù)用
1.1版本中存在 隊(duì)首阻塞問題 ,因此如果客戶端要發(fā)起多個(gè)并行請求來提升性能,必須使用 多個(gè)TCP連接 ,這樣就要承受 更大延時(shí)和建鏈拆鏈成本 ,不能有效利用TCP鏈接。
由于2.0版本中使用新的二進(jìn)制分幀協(xié)議突破了1.0的諸多限制,從根本上實(shí)現(xiàn)了真正的 請求和響應(yīng)多路復(fù)用 。
客戶端和服務(wù)器將交互數(shù)據(jù)分解為 相互獨(dú)立的幀 ,互不影響地 交錯傳輸 ,最后再在對端根據(jù) 幀頭中的流標(biāo)識符 把它們 重新組裝 起來,從而實(shí)現(xiàn)了TCP鏈接的多路復(fù)用。
如圖展示了2.0版本的基于幀的消息通信過程 ( 圖片來自參考4 ) :


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

hpack算法的相關(guān)文檔:
- https://tools.ietf.org/html/draft-ietf-httpbis-header-compression-12
3.5 服務(wù)端推送
服務(wù)端推送是2.0版本新增的一個(gè)強(qiáng)大功能,和一般的 一問一答 式的C/S交互不同, 推送式交互中服務(wù)器可以對客戶端的一個(gè)請求發(fā)送多個(gè)響應(yīng) ,除了對最初請求的響應(yīng)外還向客戶端推送額外資源,無需客戶端明確地請求也可以推送。
舉個(gè)栗子:
想象一下你去餐廳吃飯,服務(wù)好的快餐廳在你點(diǎn)好一份牛肉面之后,還會給你送上餐巾紙、筷子、勺子甚至調(diào)料等,這樣主動式的服務(wù),節(jié)約了客人的時(shí)間并且提高了用餐體驗(yàn)。
在實(shí)際的C/S交互中這種 主動推送額外資源 的方法很有效,因?yàn)閹缀趺總€(gè)網(wǎng)絡(luò)應(yīng)用都會包含多種資源,客戶端需要全部逐個(gè)獲取它們,此時(shí)如果讓服務(wù)器提前推送這些資源,從而可以 有效減少額外的延遲時(shí) 間 ,因?yàn)榉?wù)器可以知道客戶端下一步要請求什么資源。
如圖為服務(wù)端推送的簡單過程 ( 圖片來自參考4 ) :

4. 總結(jié)
本文通過介紹Http協(xié)議的歷史演進(jìn)、各個(gè)版本的主要特征和優(yōu)缺點(diǎn)、重點(diǎn)介紹了Http2.0協(xié)議的一些特性,包括: SPDY協(xié)議、二進(jìn)制分幀協(xié)議、多路復(fù)用、首部壓縮、服務(wù)端推送 等重要功能, 篇幅有限不能展開太多 。
雖然http2.0版本協(xié)議有很多非常優(yōu)秀的功能并且在 2015年正式發(fā)布 ,現(xiàn)在國內(nèi)外一些大廠基本都有使用http2.0承擔(dān)部分請求,但是目前仍 然未廣泛普及 。
目前http3.0版本在2018年也推出來了,至于http2.0和http3.0的推廣和普及是需要時(shí)間的,但是堅(jiān)信我們的網(wǎng)絡(luò)可以 更安全、更快捷、更節(jié)約 。