一、浮光掠影
1. HTTP 0.9????只接受 GET 一種請求方法,沒有在通訊中指定版本號(hào),不支持請求頭。不支持 POST 方法,所以客戶端無法向服務(wù)器傳遞太多信息。
2. HTTP 1.0 RFC 1945 60頁 1996年
3. HTTP 1.1 RFC 2616 176頁 1999年
4. HTTP/2 原名 HTTP/2.0? ?RFC 7540/7541? 2015年5月15日
網(wǎng)站首頁加載需要下載的數(shù)據(jù)量增加,超過1.9MB,平均每個(gè)頁面為完成顯示和渲染所需下載的資源數(shù)超過100個(gè)
二、HTTP協(xié)議特點(diǎn)
1)采用請求/響應(yīng)模型, 支持客戶/服務(wù)器模式。
2)簡單快速:客戶向服務(wù)器請求服務(wù)時(shí),最簡單的方法只需傳送請求方法和路徑。請求方法常用的有GET、HEAD、POST、OPTIONS。每種方法規(guī)定了客戶與服務(wù)器聯(lián)系的類型不同。由于HTTP協(xié)議簡單,使得HTTP服務(wù)器的程序規(guī)模小,因而通信速度很快。
3)靈活:HTTP允許傳輸任意類型的數(shù)據(jù)對象。正在傳輸?shù)念愋陀蒀ontent-Type加以標(biāo)記,示例:
text/html
text/javascript;charset=utf-8
img/png
video/mp4
application/json;charset=utf-8
application-x-www-form-urlencoded
等等,二進(jìn)制的content-type如何?會(huì)在什么場景用到?
4)無連接:?無連接的含義是限制每次連接只處理一個(gè)請求,即連接無法復(fù)用。在持久連接或者HTTP pipelining出現(xiàn)之前,每個(gè)連接的獲取都需要?jiǎng)?chuàng)建一個(gè)獨(dú)立的TCP連接。每次請求都要經(jīng)歷三次握手和慢啟動(dòng),服務(wù)器處理完客戶的請求,并收到客戶的應(yīng)答后,即斷開連接。最初HTTP協(xié)議如此,后有所改進(jìn)。
HTTP 0.9:非持續(xù)連接,每個(gè)連接只處理一個(gè)請求響應(yīng)事務(wù),已過時(shí)。
HTTP 1.0:? ?默認(rèn)是非持續(xù)連接,請求頭可以設(shè)置Connection:Keep-Alive,可以在一定時(shí)間內(nèi)復(fù)用連接,具體復(fù)用時(shí)間的長短可以由服務(wù)器控制,一般在15s左右。
HTTP 1.1 默認(rèn)使用持續(xù)連接,不必為每一個(gè)WEB對象建立一個(gè)新的連接,一個(gè)連接可以傳送多個(gè)對象
HTTP/2? 多路復(fù)用(一個(gè)域只要一個(gè)TCP連接)實(shí)現(xiàn)真正的并發(fā)請求,降低延時(shí),提高了帶寬的利用率。
5)無狀態(tài):HTTP協(xié)議是無狀態(tài)協(xié)議。無狀態(tài)是指協(xié)議對于事務(wù)處理沒有記憶能力。缺少狀態(tài)意味著如果后續(xù)處理需要前面的信息,則它必須重傳,這樣可能導(dǎo)致每次連接傳送的數(shù)據(jù)量增大。另一方面,在服務(wù)器不需要先前信息時(shí)它的應(yīng)答就較快。
三、ODDS AND ENDS
1. HTTP1.0是排隊(duì)發(fā)出請求?

對于http1.0的實(shí)現(xiàn),在第一個(gè)請求沒有收到回復(fù)之前,后續(xù)從應(yīng)用層發(fā)出的請求只能排隊(duì),請求2,3,4,5只能等請求1的response回來之后才能逐個(gè)發(fā)出。網(wǎng)絡(luò)通暢的時(shí)候性能影響不大,一旦請求1的request因?yàn)槭裁丛驔]有抵達(dá)服務(wù)器,或者response因?yàn)榫W(wǎng)絡(luò)阻塞沒有及時(shí)返回,影響的就是所有后續(xù)請求,問題就變得比較嚴(yán)重了。
2. 無狀態(tài)的改進(jìn)
3.? 無連接的改變--連接的復(fù)用--保持長連接
1. Keep-Alive或者?persistent 連接
HTTP1.0 + Keep-Alive? 不肯定即否定? ? ?
如果服務(wù)器愿意為下一條請求將連接保持在打開狀態(tài),就在響應(yīng)頭中說明,如果響應(yīng)頭中沒有Connection:Keep-Alive ,客戶端就認(rèn)為服務(wù)器不支持keep-live,會(huì)在發(fā)回響應(yīng)報(bào)文后關(guān)閉連接。
HTTP 1.1+ ?persistent 連接? 不否定即肯定
HTTP/1.1逐漸停止了對keep-alive連接的支持,用一種名為持久連接的改進(jìn)型設(shè)計(jì)取代了它。持久連接的目的與keep-alive連接的目的相同,但是工作機(jī)制更優(yōu)些。HTTP/1.1就直接默認(rèn)連接情況下是激活的,除非特別指明,否則HTTP/1.1假定所有的連接都是持久的。要想在事務(wù)處理結(jié)束之后將連接關(guān)閉,HTTP/1.1應(yīng)用程序必須向報(bào)文中顯示地添加一個(gè)Connection:close報(bào)頭。
HTTP1.1客戶端加載在收到響應(yīng)后,除非響應(yīng)中包含了Connection:close首部,不然HTTP/1.1連接就仍然維持在打開狀態(tài)。但是,客戶端和服務(wù)器仍然可以隨時(shí)關(guān)閉空閑的連接。
是否有這種可能,防火墻檢測 TCP端口的 idle時(shí)間,超出一些限制會(huì)被關(guān)閉?
不發(fā)送Connection:close并不意味這服務(wù)器承諾永遠(yuǎn)將連接保持在打開狀態(tài).。
?Connection默認(rèn)值:Keep-Alive,如要關(guān)閉連接復(fù)用需顯式的設(shè)置Connection:Close。
PC端瀏覽器:? 大部分的請求在集中在一小段時(shí)間以內(nèi)。一段時(shí)間內(nèi)復(fù)用有效。
移動(dòng)app:? ? 請求比較分散且時(shí)間跨度相對較大,? 從應(yīng)用層尋求其它解決方案,長連接方案或者偽長連接方案。
方案一:基于tcp的長連接
http短連接模式:頻繁的創(chuàng)建和銷毀連接,增加服務(wù)器壓力
基于TCP的Socket編程,自己制定協(xié)議,建立自己的長連接通道,信息的上報(bào)和推送更及時(shí)。應(yīng)用產(chǎn)品:許多移動(dòng)端app,如即時(shí)通訊(IM),淘寶等電商類app,業(yè)界成熟方案:Google 的 protobut。
方案二:http long-polling


客戶端在初始狀態(tài)就會(huì)發(fā)送一個(gè)polling請求到服務(wù)器,服務(wù)器并不會(huì)馬上返回業(yè)務(wù)數(shù)據(jù),而是等待有新的業(yè)務(wù)數(shù)據(jù)產(chǎn)生的時(shí)候再返回。所以連接會(huì)一直被保持,一旦結(jié)束馬上又會(huì)發(fā)起一個(gè)新的polling請求,如此反復(fù),所以一直會(huì)有一個(gè)連接被保持。服務(wù)器有新的內(nèi)容產(chǎn)生的時(shí)候,并不需要等待客戶端建立一個(gè)新的連接。做法雖然簡單,但有些難題需要攻克才能實(shí)現(xiàn)穩(wěn)定可靠的業(yè)務(wù)框架:
a. 用戶增長時(shí)會(huì)增加服務(wù)器壓力
b. 移動(dòng)端網(wǎng)路環(huán)境復(fù)雜,比如,wifi和4g切換,進(jìn)入電梯導(dǎo)致網(wǎng)絡(luò)臨時(shí)中斷,這些場景如何重建監(jiān)控連接通道?
c. 這種polling的方式穩(wěn)定性并不好,需要做好數(shù)據(jù)可靠性的保證,比如重發(fā)和ack機(jī)制。
d. polling的response有可能會(huì)被中間代理cache住,要處理好業(yè)務(wù)數(shù)據(jù)的過期機(jī)制。
e. long-polling方式還有一些無法克服的缺點(diǎn),比如每次新的請求都會(huì)帶上重復(fù)的header信息,還有數(shù)據(jù)通道是單向的,主動(dòng)權(quán)掌握在server這邊,客戶端有新的業(yè)務(wù)請求的時(shí)候無法及時(shí)傳送。
方案三:http streaming


a. server response的響應(yīng)頭:”Transfer Encoding: chunked”? 告訴客戶端后續(xù)還會(huì)有新的數(shù)據(jù)到來
b. server不會(huì)結(jié)束初始的streaming請求,long-polling會(huì)?而是持續(xù)的通過這個(gè)通道返回最新的業(yè)務(wù)數(shù)據(jù)。這個(gè)數(shù)據(jù)通道也是單向的
c. streaming不會(huì)產(chǎn)生重復(fù)的header數(shù)據(jù)。
d.和long-polling有相同的難點(diǎn)之外,此外:
d.a. 有些代理服務(wù)器會(huì)等待服務(wù)器的response結(jié)束之后才會(huì)將結(jié)果推送到請求客戶端。對于streaming這種永遠(yuǎn)不會(huì)結(jié)束的方式來說,客戶端就會(huì)一直處于等待response的過程中。
d.b. 業(yè)務(wù)數(shù)據(jù)無法按照請求來做分割,所以客戶端每收到一塊數(shù)據(jù)都需要自己做協(xié)議解析,也就是說要做自己的協(xié)議定制。
方案四:web socket
a. 基于tcp協(xié)議,提供雙向數(shù)據(jù)通道
b. 傳統(tǒng)的tcp socket基于字節(jié)流
c. WebSocket優(yōu)勢:message,又提供了傳統(tǒng)的http所缺少的長連接功能
d. 2010年才起草,并非所有的瀏覽器支持。各大瀏覽器廠商最新的版本都提供了支持。
e.與HTML5的Server-Sent對比
Web應(yīng)用已經(jīng)使用了各種從服務(wù)器上輪詢資源的方法來持續(xù)地更新頁面,HTML5的EventSource對象和Server-Sent事件能通過瀏覽器端的JavaScript代碼打開一個(gè)服務(wù)端連接客戶端的單向通道,服務(wù)端可以使用這個(gè)寫通道來發(fā)送數(shù)據(jù),這樣能節(jié)省了HTTP創(chuàng)建多個(gè)輪詢請求的消耗。
這種方式比HTML的WebSocket更高效,WebSocket的使用場景是,當(dāng)有許多客戶端和服務(wù)端的交互的時(shí)候(比如消息或者游戲),在全雙工連接上建立一個(gè)雙向通道。
使用HTML5服務(wù)端發(fā)送事件,這個(gè)技術(shù)是基于具體的技術(shù)實(shí)現(xiàn)的,如果網(wǎng)站當(dāng)前是使用其他的Ajax或者Comet技術(shù)來輪詢的,轉(zhuǎn)變成Server-Sent事件需要重構(gòu)網(wǎng)站的Javascript代碼。
3.線頭阻塞head of line blocking的改進(jìn)
HTTP 1.0 必須是有響應(yīng)了才能發(fā)下一個(gè)請求
HTTP Pipelining:是將多個(gè)HTTP請求放到一個(gè)TCP連接中一一發(fā)送,而在發(fā)送過程中不需要等待服務(wù)器對前一個(gè)請求的響應(yīng);但是客戶端還是按照發(fā)送請求的順序接收響應(yīng)。
HTTP Pipelining只能適用于http1.1,一般來說,支持http1.1的server都要求支持pipelining。


請求2,3,4,5不用等請求1的response返回之后才發(fā)出,而是幾乎在同一時(shí)間把request發(fā)向了服務(wù)器。2,3,4,5及所有后續(xù)共用該連接的請求節(jié)約了等待的時(shí)間,極大的降低了整體延遲。
舉個(gè)例子,超市收銀臺(tái)或銀行柜臺(tái)排隊(duì),你不知道前面的顧客是干脆利索還是磨蹭到世界末日,收銀員/柜員(服務(wù)器)是要按照順序處理請求,如果前面一個(gè)顧客非常磨蹭(請求非常耗時(shí)),后續(xù)請求都會(huì)受到影響,即所謂的線頭阻塞(Head of line of blocking)。
只有冪等的請求(GET,HEAD)能使用pipelining,非冪等請求比如POST不能使用,因?yàn)檎埱笾g可能會(huì)存在先后依賴關(guān)系。
head of line blocking并沒有完全得到解決,server的response還是要求依次返回,遵循FIFO(first in first out)原則。也就是說如果請求1的response沒有回來,2,3,4,5的response也不會(huì)被送回來。
絕大部分的http代理服務(wù)器不支持pipelining。
和不支持pipelining的老服務(wù)器協(xié)商有問題。
可能會(huì)導(dǎo)致新的Front of queue blocking問題。
正是因?yàn)橛羞@么多的問題,各大瀏覽器廠商要么是根本就不支持pipelining,要么就是默認(rèn)關(guān)掉了pipelining機(jī)制,而且啟用的條件十分苛刻??梢詤⒖糲hrome對于pipeling的問題描述。
4.其他的延遲解決小技巧:
提高HTTP1.X協(xié)議傳輸速度的技巧:
· Spriting(圖片合并)
a.多個(gè)小圖片合并到一張大圖,原先本需要多個(gè)小請求,現(xiàn)只需一個(gè)大圖請求
b. 再利用js或者css取出其中的小圖
c. 優(yōu)點(diǎn):請求數(shù)減少,延遲降低。
d. 弊端:文件的粒度變大
只需小圖,卻不得不下載整張大圖
cache處理不便,一張小圖過期卻必須從服務(wù)器下載完整的大圖,浪費(fèi)流量。
· Inlining(內(nèi)容內(nèi)嵌)
HTML的標(biāo)準(zhǔn)是使用鏈接來加載外部資源,這使得更容易在服務(wù)器上(或者在CDN上)操作更新這些資源,而不是在每個(gè)頁面上修改更新這些資源,這種模式也使得瀏覽器能從本地緩存而不是服務(wù)器上獲取資源。
但是對還沒有緩存到瀏覽器localStorage的資源來說,這種模式對網(wǎng)站的性能有負(fù)面的影響,一般來說,一個(gè)頁面需要幾十個(gè)單獨(dú)的請求來獲取資源從而渲染頁面。
從性能的角度來說,如果一個(gè)資源沒有很高的被緩存的幾率的話,最好把它嵌入到頁面的HTML中(叫inlining),而不是使用鏈接外部,腳本和樣式是支持內(nèi)嵌到HTML中的,但是圖片和其他的二進(jìn)制資源其實(shí)也是可以通過內(nèi)嵌包含base64編碼的文本來嵌入到HTML中的。
如一個(gè)網(wǎng)頁有一張背景圖,可以通過如下代碼嵌入:
background: url(data:image/png;base64,)
data部分是base64編碼之后的字節(jié)碼,避免了一次多余的http請求。
內(nèi)嵌的缺點(diǎn)是頁面的大小會(huì)變得非常大,所以對于Web應(yīng)用來說,關(guān)鍵的是能夠跟蹤分析這個(gè)資源什么時(shí)候需要從服務(wù)端獲取,什么時(shí)候已經(jīng)緩存到客戶端了。
另外,在第一次請求資源后必須能夠使用代碼在客戶端緩存資源,因此,在移動(dòng)設(shè)備上,使用HTML5 localStorage能很好地做到內(nèi)嵌。
由于不知道用戶是否已經(jīng)訪問過這個(gè)頁面了,所以需要網(wǎng)站有機(jī)制能生成不同版本的頁面。
優(yōu)點(diǎn):
Inlining內(nèi)聯(lián)的方式,采用inline css/inline js等并入html中,減少對css/js文件的請求
弊端:
內(nèi)聯(lián)的方式,會(huì)讓我們的代碼變得難以維護(hù),讓html文件變得更大,代碼混合嚴(yán)重。
· Concatenation(文件合并)
大型網(wǎng)站現(xiàn)在前端開發(fā)交互越來越多,往往會(huì)包含大量的JavaScript文件,某些前端工具可以幫助開發(fā)人員將這些文件合并為一個(gè)大的文件,從而讓瀏覽器能只花一個(gè)請求就將其下載完,而不是發(fā)無數(shù)請求去分別下載那些瑣碎的JavaScript文件。弊端是如果某個(gè)頁面只需要其中一小部分代碼,也必須下載完整的那份,而文件中小小的一個(gè)改動(dòng)也會(huì)造成大量數(shù)據(jù)被重新下載。
將多個(gè)js文件合并到一個(gè)大的文件里在做一些壓縮處理也可以減小延遲和傳輸?shù)臄?shù)據(jù)量。
但同樣也面臨著粒度變大的問題,一個(gè)小的js代碼改動(dòng)會(huì)導(dǎo)致整個(gè)js文件被下載。
· CDN資源多域名轉(zhuǎn)發(fā),靜態(tài)資源分布存儲(chǔ)在多個(gè)域下
Domain Sharding(域名分片)
Sharding(shard是碎瓷片瓦片)
a.?
瀏覽器或者客戶端根據(jù)domain(域名)建立連接的。
最初HTTP1.1只允許一個(gè)客戶端只能對同一主機(jī)建立兩個(gè)TCP鏈接,比如針對Example Domain只允許同時(shí)建立2個(gè)連接,但mobile.example.com被認(rèn)為是另一個(gè)域名,可以再建立兩個(gè)新的連接。于是有些網(wǎng)站用新的主機(jī)名,這樣用戶就可以與網(wǎng)站建立更多的連接,從而降低載入時(shí)間。后來限制取消,客戶端很輕松地和每個(gè)主機(jī)建立6-8個(gè)連接,但由于連接上限仍然存在,所以網(wǎng)站還是會(huì)用更多連接來保證HTTP協(xié)議的效率,提升載入速度。
httparcgive.org15年時(shí)顯示,TOP30萬個(gè)URL中平均用40個(gè)TCP連接來顯示頁面。
b. 依此類推,再多建立幾個(gè)sub domain(子域名),增加同時(shí)可以建立的http請求數(shù),此即Domain Sharding。
c. 優(yōu)點(diǎn):增加連接數(shù),受限制的請求不需要等待前面的請求完成才能發(fā)出了。一個(gè)頗具規(guī)模的網(wǎng)頁請求數(shù)超過100,使用domain sharding之后同時(shí)建立的連接數(shù)可以多到50個(gè)甚至更多。
資源文件一般不需要cookie,將這些不同的靜態(tài)資源文件分散在不同的域名服務(wù)器上,可以減小請求的size。
d. 弊端:增加了系統(tǒng)資源的消耗,由于硬件資源升級(jí)非常之快,犧牲資源消耗換取用戶寶貴的等待時(shí)間。
e. 注意: domain sharding只有在請求數(shù)非常之多的場景下才有明顯的效果,請求數(shù)也不是越多越好,資源消耗是一方面,另一點(diǎn)是由于tcp的slow start會(huì)導(dǎo)致每個(gè)請求在初期都會(huì)經(jīng)歷slow start,還有tcp 三次握手,DNS查詢的延遲。這一部分帶來的時(shí)間損耗和請求排隊(duì)同樣重要,到底怎么去平衡這二者就需要取一個(gè)可靠的連接數(shù)中間值,這個(gè)值的最終確定要通過反復(fù)的測試。
f. 移動(dòng)端瀏覽器場景建議不使用domain sharding,具體細(xì)節(jié)參考這篇文章。
CDN資源多域名轉(zhuǎn)發(fā)
小結(jié):
前面回顧了HTTP 1.0 HTTP 1.1? ? 的三類問題和解決方式:
1. 無狀態(tài)(cookies)
2. 無連接·連接的復(fù)用·連接的保持
?a. Connection:Keep-Alive在HTTP 1.0 和 HTTP 1.1中的區(qū)別
HTTP1.0 + Keep-Alive? 不肯定即否定?
HTTP 1.1+ ?persistent 連接? 不否定即肯定
b. http long-polling
? ? streaming? ? ? ? ? ?
c.? ? ??·??WebSocket,是標(biāo)準(zhǔn)長連接,按說不屬于HTTP協(xié)議,可以看成是全新的TCP協(xié)議,但是建立的前期還是會(huì)通過HTTP和服務(wù)器通信,有些人將其劃分為HTTP的補(bǔ)充協(xié)議。它是全雙工的,這個(gè)協(xié)議主要的好處在于服務(wù)器可以主動(dòng)推送數(shù)據(jù),在HTTP下,服務(wù)器無法主動(dòng)推送,都是請求/響應(yīng)模式。
d.? ?·??移動(dòng)端基于TCP的Socket編程,自定義協(xié)議,建立自己的長連接通道
3. 線頭阻塞、延遲
前面無連接的問題看似緩和了,但是又產(chǎn)生了線頭阻塞,延遲等的問題
· 線頭阻塞·pipelining(管道化)
HTTP 1.0? ? ? 有響應(yīng)之后方能發(fā)送下一個(gè)請求
HTTP 1.1? ? ? pipeling只能用于HTTP 1.1 發(fā)請求可以不排隊(duì),但是處理請求仍排隊(duì),且大部分服務(wù)器不支持pipeling,即便支持,條件嚴(yán)格
延遲·HTTP1.X協(xié)議傳輸速度提高·帶著鐐銬跳舞
·減少請求次數(shù)·小合大:Spriting(圖片合并)、Concatenation(文件合并)
·不易緩存文件·減少css/js文件請求次數(shù):Inlining(內(nèi)容內(nèi)嵌)
·增加連接數(shù):Domain Sharding(域名分片)
應(yīng)聘者API·小結(jié)
HTTP1.X傳輸優(yōu)化方法
1、多個(gè)資源合并成一個(gè)請求連接,如前端Spriting雪碧圖,JS/CSS壓縮成一個(gè)文件等
2、Inlining內(nèi)聯(lián)的方式,采用inline css/inline js等并入html中,減少對css/js文件的請求
3、CDN資源多域名轉(zhuǎn)發(fā),靜態(tài)資源分布存儲(chǔ)在多個(gè)域下。
以上三種三種方法雖然能使HTTP1.X協(xié)議傳輸速度提高,但也有對應(yīng)的不足。
如雪碧圖,將多個(gè)小圖合并成一張大圖,降低多張小圖請求的高延遲,但是如果我只想要兩個(gè)icon小圖,卻需要加載一整張大圖,就會(huì)造成資源冗余。合并的JS/CSS文件也有類似的問題。
內(nèi)聯(lián)的方式,會(huì)讓我們的代碼變得難以維護(hù),讓html文件變得更大,代碼混合嚴(yán)重。
多域名下可緩解Max-Connection,但不同域會(huì)讓Cookie信息無法彼此共享。
· 上述是HTTP 1.X的一些痛點(diǎn),于是催生出了新一代的HTTP協(xié)議 HTTP/2
·? HTTP/2起源于SPDY, SPDY是由Google牽頭開發(fā)
HTTP1.0和1.1雖然存在這么多問題,業(yè)界也想出了各種優(yōu)化的手段,但這些方法手段都是在嘗試?yán)@開協(xié)議本身的缺陷,都有種隔靴搔癢,治標(biāo)不治本的感覺。直到2012年google如一聲驚雷提出了SPDY的方案,大家才開始從正面看待和解決老版本http協(xié)議本身的問題,這也直接加速了http2.0的誕生。實(shí)際上,HTTP2.0是以SPDY為原型進(jìn)行討論和標(biāo)準(zhǔn)化的。為了給HTTP2.0讓路,google已決定在2016年不再繼續(xù)支持SPDY開發(fā),但在HTTP2.0出生之前,SPDY已經(jīng)有了相當(dāng)規(guī)模的應(yīng)用,作為一個(gè)過渡方案恐怕在還將一段時(shí)間內(nèi)繼續(xù)存在?,F(xiàn)在不少app客戶端和server都已經(jīng)使用了SPDY來提升體驗(yàn),HTTP2.0在老的設(shè)備和系統(tǒng)上還無法使用(iOS系統(tǒng)只有在iOS9+上才支持),所以可以預(yù)見未來幾年SPDY將和http2.0共同服務(wù)的情況。
https://imququ.com/post/http2-resource.html
三. 開拓者SPDY
1 SPDY的目標(biāo)
HTTP 1.x的痛點(diǎn), 歸結(jié)起來是延遲和安全性。
SPDY的目標(biāo)在一開始就是瞄準(zhǔn)HTTP 1.X的痛點(diǎn)
HTTP 是明文協(xié)議,其安全性一直被業(yè)界詬病,不過這是另一個(gè)大的話題。
如果以降低延遲為目標(biāo),應(yīng)用層的HTTP和傳輸層的TCP都是都有調(diào)整的空間,不過TCP作為更底層協(xié)議存在已達(dá)數(shù)十年之久,其實(shí)現(xiàn)已深植全球的網(wǎng)絡(luò)基礎(chǔ)設(shè)施當(dāng)中,如果要?jiǎng)颖厝粋?jīng)動(dòng)骨,業(yè)界響應(yīng)度必然不高,所以SPDY的手術(shù)刀對準(zhǔn)的是HTTP。
降低延遲,客戶端的單連接單請求,server的FIFO響應(yīng)隊(duì)列都是延遲的大頭。
http最初設(shè)計(jì)都是客戶端發(fā)起請求,然后server響應(yīng),server無法主動(dòng)push內(nèi)容到客戶端。
壓縮HTTP header,HTTP 1.x的header越來越膨脹,cookie和user agent很容易讓header的size增至1kb大小,甚至更多。而且由于HTTP的無狀態(tài)特性,header必須每次request都重復(fù)攜帶,很浪費(fèi)流量。
為了增加業(yè)界響應(yīng)的可能性,Google一開始就避開了從傳輸層動(dòng)手,而且打算利用開源社區(qū)的力量以提高擴(kuò)散的力度,對于協(xié)議使用者來說,也只需要在請求的header里設(shè)置user agent,然后在server端做好支持即可,極大的降低了部署的難度。SPDY的設(shè)計(jì)如下:

SPDY位于HTTP之下,TCP和SSL之上,這樣可以輕松兼容老版本的HTTP協(xié)議(將HTTP 1.x的內(nèi)容封裝成一種新的frame格式),同時(shí)可以使用已有的SSL功能。SPDY的功能可以分為基礎(chǔ)功能和高級(jí)功能兩部分,基礎(chǔ)功能默認(rèn)啟用,高級(jí)功能需要手動(dòng)啟用。
2 SPDY基礎(chǔ)功能
多路復(fù)用(multiplexing)。多路復(fù)用通過多個(gè)請求stream共享一個(gè)tcp連接的方式,解決了HTTP1.x holb(head of line blocking)的問題,降低了延遲同時(shí)提高了帶寬的利用率。
請求優(yōu)先級(jí)(request prioritization)。多路復(fù)用帶來一個(gè)新的問題是,在連接共享的基礎(chǔ)之上有可能會(huì)導(dǎo)致關(guān)鍵請求被阻塞。SPDY允許給每個(gè)request設(shè)置優(yōu)先級(jí),這樣重要的請求就會(huì)優(yōu)先得到響應(yīng)。比如瀏覽器加載首頁,首頁的html內(nèi)容應(yīng)該優(yōu)先展示,之后才是各種靜態(tài)資源文件,腳本文件等加載,這樣可以保證用戶能第一時(shí)間看到網(wǎng)頁內(nèi)容。
header壓縮。前面提到過幾次HTTP1.x的header很多時(shí)候都是重復(fù)多余的。選擇合適的壓縮算法可以減小包的大小和數(shù)量。SPDY對header的壓縮率可以達(dá)到80%以上,低帶寬環(huán)境下效果很大。
2 SPDY高級(jí)功能
server推送(server push)。HTTP1.x只能由客戶端發(fā)起請求,然后服務(wù)器被動(dòng)的發(fā)送response。開啟server push之后,server通過X-Associated-Content header(X-開頭的header都屬于非標(biāo)準(zhǔn)的,自定義header)告知客戶端會(huì)有新的內(nèi)容推送過來。在用戶第一次打開網(wǎng)站首頁的時(shí)候,server將資源主動(dòng)推送過來可以極大的提升用戶體驗(yàn)。
server暗示(server hint)。和server push不同的是,server hint并不會(huì)主動(dòng)推送內(nèi)容,只是告訴有新的內(nèi)容產(chǎn)生,內(nèi)容的下載還是需要客戶端主動(dòng)發(fā)起請求。server hint通過X-Subresources header來通知,一般應(yīng)用場景是客戶端需要先查詢server狀態(tài),然后再下載資源,可以節(jié)約一次查詢請求。
3 SPDY的成績
SPDY的成績可以用google官方的一個(gè)數(shù)字來說明:頁面加載時(shí)間相比于HTTP 1.x減少了64%。而且各大瀏覽器廠商在SPDY誕生之后的1年多里都陸續(xù)支持了SPDY,不少大廠app和server端框架也都將SPDY應(yīng)用到了線上的產(chǎn)品當(dāng)中。
google的官網(wǎng)也給出了他們自己做的一份測試數(shù)據(jù)。測試對象是25個(gè)訪問量排名靠前的網(wǎng)站首頁,家用網(wǎng)絡(luò)%1的丟包率,每個(gè)網(wǎng)站測試10次取平均值。結(jié)果如下:

不開啟ssl的時(shí)候提升在 27% - 60%,開啟之后為39% - 55%。 這份測試結(jié)果有兩點(diǎn)值得特別注意:
3.1 連接數(shù)的選擇
連接:基于域名建立,另一種選擇是,不做區(qū)分所有子域名都共享一個(gè)連接。
以上google的測試結(jié)果顯示單一連接性能高于多域名連接方式。之所以出現(xiàn)這種情況是由于網(wǎng)頁所有的資源請求并不是同一時(shí)間發(fā)出,后續(xù)發(fā)出的子域名請求如果能復(fù)用之前的tcp連接當(dāng)然性能更好。實(shí)際應(yīng)用場景下應(yīng)該也是單連接共享模式表現(xiàn)好。
3.2 帶寬的影響
測試基于兩種帶寬環(huán)境,一慢一快。
網(wǎng)速快的環(huán)境下對減小延遲的提升更大,單連接模式下可以提升至60%。原因也比較簡單,帶寬越大,復(fù)用連接的請求完成越快,由于三次握手和慢啟動(dòng)導(dǎo)致的延遲損耗就變得更明顯。
出了連接模式和帶寬之外,丟包率和RTT也是需要測試的參數(shù)。SPDY對header的壓縮有80%以上,整體包大小能減少大概40%,發(fā)送的包越少,自然受丟包率影響也就越小,所以丟包率大的惡劣環(huán)境下SPDY反而更能提升體驗(yàn)。下圖是受丟包率影響的測試結(jié)果,丟包率超過2.5%之后就沒有提升了:

[圖8]
RTT越大,延遲會(huì)越大,在高RTT的場景下,由于SPDY的request是并發(fā)進(jìn)行的,所有對包的利用率更高,反而能更明顯的減小總體延遲。測試結(jié)果如下:
<img src="https://pic3.zhimg.com/0e76aabb96a1d492433aa7003975bda6_b.png" data-rawwidth="1170" data-rawheight="450" class="origin_image zh-lightbox-thumb" width="1170" data-original="https://pic3.zhimg.com/0e76aabb96a1d492433aa7003975bda6_r.jpg">

[圖9]
SPDY從2012年誕生到2016停止維護(hù),時(shí)間跨度對于網(wǎng)絡(luò)協(xié)議來說其實(shí)非常之短。如果HTTP2.0沒有出來,google或許能收集到更多業(yè)界產(chǎn)品的真實(shí)反饋和數(shù)據(jù),畢竟google自己的測試環(huán)境相對簡單。但SPDY也完成了自己的使命,作為一貫扮演拓荒者角色的google應(yīng)該也早就預(yù)見了這樣的結(jié)局。SPDY對產(chǎn)品網(wǎng)絡(luò)體驗(yàn)的提升到底如何,恐怕只有各大廠產(chǎn)品經(jīng)理才清楚了。
3. 救世主HTTP2.0
SPDY的誕生和表現(xiàn)說明了兩件事情:一是在現(xiàn)有互聯(lián)網(wǎng)設(shè)施基礎(chǔ)和http協(xié)議廣泛使用的前提下,是可以通過修改協(xié)議層來優(yōu)化http1.x的。二是針對http1.x的修改確實(shí)效果明顯而且業(yè)界反饋很好。正是這兩點(diǎn)讓IETF(Internet Enginerring Task Force)開始正式考慮制定HTTP2.0的計(jì)劃,最后決定以SPDY/3為藍(lán)圖起草HTTP2.0,SPDY的部分設(shè)計(jì)人員也被邀請參與了HTTP2.0的設(shè)計(jì)。
3.1 HTTP2.0需要考慮的問題
HTTP2.0與SPDY的起點(diǎn)不同,SPDY可以說是google的“玩具”,最早出現(xiàn)在自家的chrome瀏覽器和server上,好不好玩以及別人會(huì)不會(huì)跟著一起玩對google來說無關(guān)痛癢。但HTTP2.0作為業(yè)界標(biāo)準(zhǔn)還沒出生就是眾人矚目的焦點(diǎn),一開始如果有什么瑕疵或者不兼容的問題影響可能又是數(shù)十年之久,所以考慮的問題和角度要非常之廣。我們來看下HTTP2.0一些重要的設(shè)計(jì)前提:
客戶端向server發(fā)送request這種基本模型不會(huì)變。
老的scheme不會(huì)變,使用http://和https://的服務(wù)和應(yīng)用不會(huì)要做任何更改,不會(huì)有http2://。
使用http1.x的客戶端和服務(wù)器可以無縫的通過代理方式轉(zhuǎn)接到http2.0上。
不識(shí)別http2.0的代理服務(wù)器可以將請求降級(jí)到http1.x。
因?yàn)榭蛻舳撕蛃erver之間在確立使用http1.x還是http2.0之前,必須要要確認(rèn)對方是否支持http2.0,所以這里必須要有個(gè)協(xié)商的過程。最簡單的協(xié)商也要有一問一答,客戶端問server答,即使這種最簡單的方式也多了一個(gè)RTT的延遲,我們之所以要修改http1.x就是為了降低延遲,顯然這個(gè)RTT我們是無法接受的。google制定SPDY的時(shí)候也遇到了這個(gè)問題,他們的辦法是強(qiáng)制SPDY走h(yuǎn)ttps,在SSL層完成這個(gè)協(xié)商過程。ssl層的協(xié)商在http協(xié)議通信之前,所以是最適合的載體。google為此做了一個(gè)tls的拓展,叫NPN(Next Protocol Negotiation),從名字上也可以看出,這個(gè)拓展主要目的就是為了協(xié)商下一個(gè)要使用的協(xié)議。HTTP2.0雖然也采用了相同的方式,不過HTTP2.0經(jīng)過激烈的討論,最終還是沒有強(qiáng)制HTTP2.0要走ssl層,大部分瀏覽器廠商(除了IE)卻只實(shí)現(xiàn)了基于https的2.0協(xié)議。HTTP2.0沒有使用NPN,而是另一個(gè)tls的拓展叫ALPN(Application Layer Protocol Negotiation)。SPDY也打算從NPN遷移到ALPN了。
各瀏覽器(除了IE)之所以只實(shí)現(xiàn)了基于SSL的HTTP2.0,另一個(gè)原因是走SSL請求的成功率會(huì)更高,被SSL封裝的request不會(huì)被監(jiān)聽和修改,這樣網(wǎng)絡(luò)中間的網(wǎng)絡(luò)設(shè)備就無法基于http1.x的認(rèn)知去干涉修改request,http2.0的request如果被意外的修改,請求的成功率自然會(huì)下降。
HTTP2.0協(xié)議沒有強(qiáng)制使用SSL是因?yàn)槁牭搅撕芏嗟姆磳β曇?,畢竟https和http相比,在不優(yōu)化的前提下性能差了不少,要把https優(yōu)化到幾乎不增加延遲的程度又需要花費(fèi)不少力氣。IETF面對這種兩難的處境做了妥協(xié),但大部分瀏覽器廠商(除了IE)并不買帳,他們只認(rèn)https2.0。對于app開發(fā)者來說,他們可以堅(jiān)持使用沒有ssl的http2.0,不過要承擔(dān)一個(gè)多余的RTT延遲和請求可能被破壞的代價(jià)。
3.1 HTTP2.0主要改動(dòng)
HTTP2.0作為新版協(xié)議,改動(dòng)細(xì)節(jié)必然很多,不過對應(yīng)用開發(fā)者和服務(wù)提供商來說,影響較大的就幾點(diǎn)。
新的二進(jìn)制格式(Binary Format)
http1.x誕生的時(shí)候是明文協(xié)議,其格式由三部分組成:start line(request line或者status line),header,body。要識(shí)別這3部分就要做協(xié)議解析,http1.x的解析是基于文本?;谖谋緟f(xié)議的格式解析存在天然缺陷,文本的表現(xiàn)形式有多樣性,要做到健壯性考慮的場景必然很多,二進(jìn)制則不同,只認(rèn)0和1的組合。基于這種考慮http2.0的協(xié)議解析決定采用二進(jìn)制格式,實(shí)現(xiàn)方便且健壯。
有人可能會(huì)覺得基于文本的http調(diào)試方便很多,像firebug,chrome,charles等不少工具都可以即時(shí)調(diào)試修改請求。實(shí)際上現(xiàn)在很多請求都是走h(yuǎn)ttps了,要調(diào)試https請求必須有私鑰才行。http2.0的絕大部分request應(yīng)該都是走h(yuǎn)ttps,所以調(diào)試方便無法作為一個(gè)有力的考慮因素了。curl,tcpdump,wireshark這些工具會(huì)更適合http2.0的調(diào)試。
http2.0用binary格式定義了一個(gè)一個(gè)的frame,和http1.x的格式對比如下圖:

[圖10]
http2.0的格式定義更接近tcp層的方式,這張二機(jī)制的方式十分高效且精簡。length定義了整個(gè)frame的開始到結(jié)束,type定義frame的類型(一共10種),flags用bit位定義一些重要的參數(shù),stream id用作流控制,剩下的payload就是request的正文了。
雖然看上去協(xié)議的格式和http1.x完全不同了,實(shí)際上http2.0并沒有改變http1.x的語義,只是把原來http1.x的header和body部分用frame重新封裝了一層而已。調(diào)試的時(shí)候?yàn)g覽器甚至?xí)裩ttp2.0的frame自動(dòng)還原成http1.x的格式。具體的協(xié)議關(guān)系可以用下圖表示:

[圖11]
連接共享
http2.0要解決的一大難題就是多路復(fù)用(MultiPlexing),即連接共享。上面協(xié)議解析中提到的stream id就是用作連接共享機(jī)制的。一個(gè)request對應(yīng)一個(gè)stream并分配一個(gè)id,這樣一個(gè)連接上可以有多個(gè)stream,每個(gè)stream的frame可以隨機(jī)的混雜在一起,接收方可以根據(jù)stream id將frame再歸屬到各自不同的request里面。
前面還提到過連接共享之后,需要優(yōu)先級(jí)和請求依賴的機(jī)制配合才能解決關(guān)鍵請求被阻塞的問題。http2.0里的每個(gè)stream都可以設(shè)置又優(yōu)先級(jí)(Priority)和依賴(Dependency)。優(yōu)先級(jí)高的stream會(huì)被server優(yōu)先處理和返回給客戶端,stream還可以依賴其它的sub streams。優(yōu)先級(jí)和依賴都是可以動(dòng)態(tài)調(diào)整的。動(dòng)態(tài)調(diào)整在有些場景下很有用,假想用戶在用你的app瀏覽商品的時(shí)候,快速的滑動(dòng)到了商品列表的底部,但前面的請求先發(fā)出,如果不把后面的請求優(yōu)先級(jí)設(shè)高,用戶當(dāng)前瀏覽的圖片要到最后才能下載完成,顯然體驗(yàn)沒有設(shè)置優(yōu)先級(jí)好。同理依賴在有些場景下也有妙用。
header壓縮
前面提到過http1.x的header由于cookie和user agent很容易膨脹,而且每次都要重復(fù)發(fā)送。http2.0使用encoder來減少需要傳輸?shù)膆eader大小,通訊雙方各自cache一份header fields表,既避免了重復(fù)header的傳輸,又減小了需要傳輸?shù)拇笮 8咝У膲嚎s算法可以很大的壓縮header,減少發(fā)送包的數(shù)量從而降低延遲。
這里普及一個(gè)小知識(shí)點(diǎn)。現(xiàn)在大家都知道tcp有slow start的特性,三次握手之后開始發(fā)送tcp segment,第一次能發(fā)送的沒有被ack的segment數(shù)量是由initial tcp window大小決定的。這個(gè)initial tcp window根據(jù)平臺(tái)的實(shí)現(xiàn)會(huì)有差異,但一般是2個(gè)segment或者是4k的大?。ㄒ粋€(gè)segment大概是1500個(gè)字節(jié)),也就是說當(dāng)你發(fā)送的包大小超過這個(gè)值的時(shí)候,要等前面的包被ack之后才能發(fā)送后續(xù)的包,顯然這種情況下延遲更高。intial window也并不是越大越好,太大會(huì)導(dǎo)致網(wǎng)絡(luò)節(jié)點(diǎn)的阻塞,丟包率就會(huì)增加,具體細(xì)節(jié)可以參考IETF這篇文章。http的header現(xiàn)在膨脹到有可能會(huì)超過這個(gè)intial window的值了,所以更顯得壓縮header的重要性。
壓縮算法的選擇
SPDY/2使用的是gzip壓縮算法,但后來出現(xiàn)的兩種攻擊方式BREACH和CRIME使得即使走ssl的SPDY也可以被破解內(nèi)容,最后綜合考慮采用的是一種叫HPACK的壓縮算法。這兩個(gè)漏洞和相關(guān)算法可以點(diǎn)擊鏈接查看更多的細(xì)節(jié),不過這種漏洞主要存在于瀏覽器端,因?yàn)樾枰ㄟ^javascript來注入內(nèi)容并觀察payload的變化。
重置連接表現(xiàn)更好
很多app客戶端都有取消圖片下載的功能場景,對于http1.x來說,是通過設(shè)置tcp segment里的reset flag來通知對端關(guān)閉連接的。這種方式會(huì)直接斷開連接,下次再發(fā)請求就必須重新建立連接。http2.0引入RST_STREAM類型的frame,可以在不斷開連接的前提下取消某個(gè)request的stream,表現(xiàn)更好。
Server Push
Server Push的功能前面已經(jīng)提到過,http2.0能通過push的方式將客戶端需要的內(nèi)容預(yù)先推送過去,所以也叫“cache push”。另外有一點(diǎn)值得注意的是,客戶端如果退出某個(gè)業(yè)務(wù)場景,出于流量或者其它因素需要取消server push,也可以通過發(fā)送RST_STREAM類型的frame來做到。
流量控制(Flow Control)
TCP協(xié)議通過sliding window的算法來做流量控制。發(fā)送方有個(gè)sending window,接收方有receive window。http2.0的flow control是類似receive window的做法,數(shù)據(jù)的接收方通過告知對方自己的flow window大小表明自己還能接收多少數(shù)據(jù)。只有Data類型的frame才有flow control的功能。對于flow control,如果接收方在flow window為零的情況下依然更多的frame,則會(huì)返回block類型的frame,這種場景一般表明http2.0的部署出了問題。
Nagle Algorithm vs TCP Delayed Ack
tcp協(xié)議優(yōu)化的一個(gè)經(jīng)典場景是:Nagle算法和Berkeley的delayed ack算法的對立。http2.0并沒有對tcp層做任何修改,所以這種對立導(dǎo)致的高延遲問題依然存在。要么通過TCP_NODELAY禁用Nagle算法,要么通過TCP_QUICKACK禁用delayed ack算法。貌似http2.0官方建議是設(shè)置TCP_NODELAY。
更安全的SSL
HTTP2.0使用了tls的拓展ALPN來做協(xié)議升級(jí),除此之外加密這塊還有一個(gè)改動(dòng),HTTP2.0對tls的安全性做了近一步加強(qiáng),通過黑名單機(jī)制禁用了幾百種不再安全的加密算法,一些加密算法可能還在被繼續(xù)使用。如果在ssl協(xié)商過程當(dāng)中,客戶端和server的cipher suite沒有交集,直接就會(huì)導(dǎo)致協(xié)商失敗,從而請求失敗。在server端部署http2.0的時(shí)候要特別注意這一點(diǎn)。
3.2 HTTP2.0里的負(fù)能量
SPDY和HTTP2.0之間的曖昧關(guān)系,以及google作為SPDY的創(chuàng)造者,這兩點(diǎn)很容易讓陰謀論者懷疑google是否會(huì)成為協(xié)議的最終收益方。這其實(shí)是廢話,google當(dāng)然會(huì)受益,任何新協(xié)議使用者都會(huì)從中受益,至于誰吃肉,誰喝湯看的是自己的本事。從整個(gè)協(xié)議的變遷史也可以粗略看出,新協(xié)議的誕生完全是針對業(yè)界現(xiàn)存問題對癥下藥,并沒有g(shù)oogle業(yè)務(wù)相關(guān)的痕跡存在,google至始至終只扮演了一個(gè)角色:you can you up。
HTTP2.0不會(huì)是萬金油,但抹了也不會(huì)有副作用。HTTP2.0最大的亮點(diǎn)在于多路復(fù)用,而多路復(fù)用的好處只有在http請求量大的場景下才明顯,所以有人會(huì)覺得只適用于瀏覽器瀏覽大型站點(diǎn)的時(shí)候。這么說其實(shí)沒錯(cuò),但http2.0的好處不僅僅是multiplexing,請求壓縮,優(yōu)先級(jí)控制,server push等等都是亮點(diǎn)。對于內(nèi)容型移動(dòng)端app來說,比如淘寶app,http請求量大,多路復(fù)用還是能產(chǎn)生明顯的體驗(yàn)提升。多路復(fù)用對延遲的改變可以參考下這個(gè)測試網(wǎng)址。
HTTP2.0對于ssl的依賴使得有些開發(fā)者望而生畏。不少開發(fā)者對ssl還停留在高延遲,CPU性能損耗,配置麻煩的印象中。其實(shí)ssl于http結(jié)合對性能的影響已經(jīng)可以優(yōu)化到忽略的程度了,網(wǎng)上也有不少文章可以參考。HTTP2.0也可以不走ssl,有些場景確實(shí)可能不適合https,比如對代理服務(wù)器的cache依賴,對于內(nèi)容安全性不敏感的get請求可以通過代理服務(wù)器緩存來優(yōu)化體驗(yàn)。
3.3 HTTP2.0的現(xiàn)狀
HTTP2.0作為新版本的網(wǎng)絡(luò)協(xié)議肯定需要一段時(shí)間去普及,但HTTP本身屬于應(yīng)用層協(xié)議,和當(dāng)年的網(wǎng)絡(luò)層協(xié)議IPV6不同,離底層協(xié)議越遠(yuǎn),對網(wǎng)絡(luò)基礎(chǔ)硬件設(shè)施的影響就越小。HTTP2.0甚至還特意的考慮了與HTTP1.x的兼容問題,只是在HTTP1.x的下面做了一層framing layer,更使得其普及的阻力變小。所以不出意外,HTTP2.0的普及速度可能會(huì)遠(yuǎn)超大部分人的預(yù)期。
Firefox 2015年在其瀏覽器流量中檢測到,有13%的http流量已經(jīng)使用了http2.0,27%的https也是http2.0,而且還處于持續(xù)的增長當(dāng)中。一般用戶察覺不到是否使用了http2.0,不過可以裝這樣一個(gè)插件,安裝之后如果網(wǎng)站是http2.0的,在地址欄的最右邊會(huì)有個(gè)閃電圖標(biāo)。還可以使用這個(gè)網(wǎng)站來測試。對于開發(fā)者來說,可以通過Web Developer的Network來查看協(xié)議細(xì)節(jié),如下圖:

[圖12]
其中Version:HTTP/2.0已經(jīng)很明確表明協(xié)議類型,F(xiàn)irefox還在header里面插入了X-Firefox-Spdy:“h2”,也可以看出是否使用http2.0。
Chrome在2015年檢測到的http2.0流量大概有18%。不過這個(gè)數(shù)字本來會(huì)更高,因?yàn)镃hrome現(xiàn)在很大一部分流量都在試驗(yàn)QUIC(google正在開辟的另一塊疆土)。Chrome上也可以使用類似的插件來判斷網(wǎng)站是否是使用http2.0。
4. 移動(dòng)端HTTP現(xiàn)狀4.1 iOS下http現(xiàn)狀
iOS系統(tǒng)是從iOS8開始才通過NSURLSession來支持SPDY的,iOS9+開始自動(dòng)支持http2.0。實(shí)際上apple對http2.0非常有信心,推廣力度也很大。新版本ATS機(jī)制默認(rèn)使用https來進(jìn)行網(wǎng)絡(luò)傳輸。APN(Apple Push Notifiction)在iOS9上也已經(jīng)是通過http2.0來實(shí)現(xiàn)的了。iOS9 sdk里的NSURLSession默認(rèn)使用http2.0,而且對開發(fā)者來說是完全透明的,甚至沒有api來知道到底是用的哪個(gè)版本的http協(xié)議。
對于開發(fā)者來說到底怎么去配置最佳的http使用方案呢?在我看來,因app而異,主要從兩方面來考慮:一是app本身http流量是否大而且密集,二是開發(fā)團(tuán)隊(duì)本身的技術(shù)條件。http2.0的部署相對容易很多,客戶端開發(fā)者甚至不用做什么改動(dòng),只需要使用iOS9的SDK編譯即可,但缺點(diǎn)是http2.0只能適用于iOS9的設(shè)備。SPDY的部署相對麻煩一些,但優(yōu)點(diǎn)是可以兼顧iOS6+的設(shè)備。iOS端的SPDY可以使用twitter開發(fā)的CocoaSPDY方案,但有一點(diǎn)需要特別處理:
由于蘋果的TLS實(shí)現(xiàn)不支持NPN,所以通過NPN協(xié)商使用SPDY就無法通過默認(rèn)443端口來實(shí)現(xiàn)。有兩種做法,一是客戶端和server同時(shí)約定好使用另一個(gè)端口號(hào)來做NPN協(xié)商,二是server這邊通過request header智能判斷客戶端是否支持SPDY而越過NPN協(xié)商過程。第一種方法會(huì)簡單一點(diǎn),不過需要從框架層將所有的http請求都map到另一個(gè)port,url mapping可以參考我之前的一篇文章。twitter自己的網(wǎng)站twitter.com使用的是第二種方法。
瀏覽器端(比如Chrome),server端(比如nginx)都陸續(xù)打算放棄支持spdy了,畢竟google官方都宣布要停止維護(hù)了。spdy會(huì)是一個(gè)過渡方案,會(huì)隨著iOS9的普及會(huì)逐步消失,所以這部分的技術(shù)投入需要開發(fā)團(tuán)隊(duì)自己去衡量。
4.2 Android下http現(xiàn)狀
android和iOS情況類似,http2.0只能在新系統(tǒng)下支持,spdy作為過渡方案仍然有存在的必要。
對于使用webview的app來說,需要基于chrome內(nèi)核的webview才能支持spdy和http2.0,而android系統(tǒng)的webview是從android4.4(KitKat)才改成基于chrome內(nèi)核的。
對于使用native api調(diào)用的http請求來說,okhttp是同時(shí)支持spdy和http2.0的可行方案。如果使用ALPN,okhttp要求android系統(tǒng)5.0+(實(shí)際上,android4.4上就有了ALPN的實(shí)現(xiàn),不過有bug,知道5.0才正式修復(fù)),如果使用NPN,可以從android4.0+開始支持,不過NPN也是屬于將要被淘汰的協(xié)議。
結(jié)束語
以上是HTTP從1.x到SPDY,再到HTTP2.0的一些主要變遷技術(shù)點(diǎn)。HTTP2.0正處于逐步應(yīng)用到線上產(chǎn)品和服務(wù)的階段,可以預(yù)見未來會(huì)有不少新的坑產(chǎn)生和與之對應(yīng)的優(yōu)化技巧,HTTP1.x和SPDY也將在一段時(shí)間內(nèi)繼續(xù)發(fā)揮余熱。作為工程師,需要了解這些協(xié)議背后的技術(shù)細(xì)節(jié),才能打造高性能的網(wǎng)絡(luò)框架,從而提升我們的產(chǎn)品體驗(yàn)。
參考鏈接:
以上筆記轉(zhuǎn)載自以下文章,有許多改動(dòng):
知乎作者victor yu的相關(guān)回答:
https://www.zhihu.com/question/34074946/answer/108588042