官术网_书友最值得收藏!

4.3.1 連接數(shù)優(yōu)化

我們知道HTTP(特指HTTP/3以前)是以TCP為傳輸層的應(yīng)用層協(xié)議,但HTTP over TCP這種搭配只能說是TCP在當(dāng)今網(wǎng)絡(luò)中統(tǒng)治性地位所造就的結(jié)果,而不能說它們兩者的配合就是合適的。回想一下你上網(wǎng)時平均在每個頁面停留的時間,以及每個頁面中包含的資源(HTML、JS、CSS、圖片等)數(shù)量,可以總結(jié)出HTTP傳輸對象的主要特征是數(shù)量多、時間短、資源小、切換快。另一方面,TCP協(xié)議要求必須在三次握手完成之后才能開始數(shù)據(jù)傳輸,這是一個可能以高達“百毫秒”為計時尺度的事件;另外,TCP還有慢啟動的特性,使得剛剛建立連接時的傳輸速度是最低的,后面再逐步加速直至穩(wěn)定。由于TCP協(xié)議本身是面向長時間、大數(shù)據(jù)傳輸來設(shè)計的,在長時間尺度下,它建立連接的高昂成本才不至于成為瓶頸,它的穩(wěn)定性和可靠性的優(yōu)勢才能展現(xiàn)出來。因此,可以說HTTP over TCP這種搭配在目標特征上確實是有矛盾的,以至于HTTP/1.x時代,大量短而小的TCP連接導(dǎo)致了網(wǎng)絡(luò)性能的瓶頸。為了緩解HTTP與TCP之間的矛盾,聰明的程序員們一方面致力于減少發(fā)出的請求數(shù)量,另一方面也致力于增加客戶端到服務(wù)端的連接數(shù)量,這就是上面Yslow規(guī)則中“Minimize HTTP Requests”與“Split Components Across Domains”兩條優(yōu)化措施的根本依據(jù)所在。

通過前端開發(fā)人員的各種Tricks,的確能減少消耗TCP連接數(shù)量,這是有數(shù)據(jù)統(tǒng)計作為支撐的。圖4-2和圖4-3展示了HTTP Archive對最近五年數(shù)百萬個URL地址采樣得出的結(jié)論:在頁面平均請求沒有改變的情況下(桌面端下降3.8%,移動端上升1.4%),TCP連接正在持續(xù)且幅度較大地下降(桌面端下降36.4%,移動端下降28.6%)。

圖4-2 HTTP平均請求數(shù)量,70余個,沒有明顯變化

但是開發(fā)人員節(jié)省TCP連接的優(yōu)化措施并非只有好處,它們也帶來了諸多不良的副作用。

·如果你用雪碧圖將多張圖片合并,意味著任何場景下哪怕只用到其中一張小圖,也必須完整加載整張大圖片;任何場景下哪怕對一張小圖要進行修改,都會導(dǎo)致整個緩存失效,類似地,樣式、腳本等其他文件的合并也會存在同樣的問題。

·如果你使用了媒體內(nèi)嵌,除了要承受Base64編碼導(dǎo)致傳輸容量膨脹1/3的代價外(Base64以8位表示6位數(shù)據(jù)),也將無法有效利用緩存。

·如果你合并了異步請求,這就會導(dǎo)致所有請求的返回時間都受最慢的那個請求的拖累,導(dǎo)致整體響應(yīng)速度下降。

·如果你把圖片放到不同子域下面,將會導(dǎo)致更大的DNS解析負擔(dān),而且瀏覽器對兩個不同子域下的同一圖片必須持有兩份緩存,也使得緩存效率下降。

由此可見,一旦在技術(shù)根基上出現(xiàn)問題,依賴使用者通過各種Tricks去解決,無論如何都難以擺脫“兩害相權(quán)取其輕”的權(quán)衡困境,否則這就不是Tricks而是一種標準的設(shè)計模式了。

圖4-3 TCP連接數(shù)量,約15個,有明顯下降趨勢

在另一方面,HTTP的設(shè)計者們并不是沒有嘗試過在協(xié)議層面去解決連接成本過高的問題,即使HTTP協(xié)議的最初版本(指HTTP/1.0,忽略非正式的HTTP/0.9版本)就已經(jīng)支持了[1]連接復(fù)用技術(shù),即今天大家所熟知的持久連接(Persistent Connection),也稱為連接Keep-Alive機制。持久連接的原理是讓客戶端對同一個域名長期持有一個或多個不會用完即斷的TCP連接。典型做法是在客戶端維護一個FIFO隊列,在每次取完數(shù)據(jù)[2]之后一段時間內(nèi)先不自動斷開連接,以便在獲取下一個資源時直接復(fù)用,避免創(chuàng)建TCP連接的成本。

但是,連接復(fù)用技術(shù)依然是不完美的,最明顯的副作用是“隊首阻塞”(Head-of-Line Blocking)問題。請設(shè)想以下場景:瀏覽器有10個資源需要從服務(wù)器中獲取,此時它將10個資源放入隊列,入列順序只能按照瀏覽器遇見這些資源的先后順序來決定。但如果這10個資源中的第1個就讓服務(wù)器陷入長時間運算狀態(tài)會怎樣呢?當(dāng)它的請求被發(fā)送到服務(wù)端之后,服務(wù)端開始計算,而運算結(jié)果出來之前TCP連接中并沒有任何數(shù)據(jù)返回,此時后面9個資源都必須阻塞等待。因為服務(wù)端雖然可以并行處理另外9個請求(譬如第1個是復(fù)雜運算請求,消耗CPU資源,第2個是數(shù)據(jù)庫訪問,消耗數(shù)據(jù)庫資源,第3個是訪問某張圖片,消耗磁盤I/O資源,這就很適合并行),但問題是處理結(jié)果無法及時返回客戶端,服務(wù)端不能因為哪個請求先完成就返回哪個,更不可能將所有要返回的資源混雜到一起交叉?zhèn)鬏敚蚴侵皇褂靡粋€TCP連接來傳輸多個資源的話,如果順序亂了,客戶端就很難區(qū)分哪個數(shù)據(jù)包歸屬哪個資源了。

2014年,IETF發(fā)布的RFC 7230中提出了名為“HTTP管道”(HTTP Pipelining)的復(fù)用技術(shù),試圖在HTTP服務(wù)器中也建立類似客戶端的FIFO隊列,讓客戶端一次將所有要請求的資源名單全部發(fā)給服務(wù)端,由服務(wù)端來安排返回順序,管理傳輸隊列。無論隊列維護在服務(wù)端還是客戶端,其實都無法完全避免隊首阻塞的問題,但由于服務(wù)端能夠較為準確地評估資源消耗情況,進而能夠更緊湊地安排資源傳輸,保證隊列中兩項工作之間盡量減少空隙,甚至做到并行化傳輸,從而提升鏈路傳輸?shù)男省?墒牵捎贖TTP管道需要多方共同支持,協(xié)調(diào)起來相當(dāng)復(fù)雜,推廣得并不算成功。

隊首阻塞問題一直持續(xù)到第二代的HTTP協(xié)議,即HTTP/2發(fā)布后才算是被比較完美地解決。在HTTP/1.x中,HTTP請求就是傳輸過程中最小粒度的信息單位了,所以如果將多個請求切碎,再混雜在一塊傳輸,客戶端勢必難以分辨、重組出有效信息。而在HTTP/2中,幀(Frame)才是最小粒度的信息單位,它可以用來描述各種數(shù)據(jù),譬如請求的Headers、Body,或者用來做控制標識,譬如打開流、關(guān)閉流。這里說的流(Stream)是一個邏輯上的數(shù)據(jù)通道概念,每個幀都附帶一個流ID以標識這個幀屬于哪個流。這樣,在同一個TCP連接中傳輸?shù)亩鄠€數(shù)據(jù)幀就可以根據(jù)流ID輕易區(qū)分開來,在客戶端毫不費力地將不同流中的數(shù)據(jù)重組出不同HTTP請求和響應(yīng)報文來。這項設(shè)計是HTTP/2的最重要的技術(shù)特征一,被稱為HTTP/2多路復(fù)用(HTTP/2 Multiplexing)技術(shù),如圖4-4所示。

有了多路復(fù)用的支持,HTTP/2就可以對每個域名只維持一個TCP連接(One Connection Per Origin)并以任意順序傳輸任意數(shù)量的資源了,這樣既減輕了服務(wù)器的連接壓力,也不需要開發(fā)者去考慮域名分片這種事情來突破瀏覽器對每個域名最多6個的連接數(shù)限制。更重要的是,沒有TCP連接數(shù)的壓力,就無須刻意壓縮HTTP請求,所有通過合并、內(nèi)聯(lián)文件(無論是圖片、樣式、腳本)以減少請求數(shù)的需求都不再成立,甚至?xí)划?dāng)作徒增副作用的反模式。

說這是反模式,也許還有一些前端開發(fā)人員會不同意,認為HTTP請求少一些總是好的,減少請求數(shù)量,最起碼也減少了傳輸中耗費的Header。這里必須先承認一個事實,在HTTP傳輸中的Header占傳輸成本的比重是相當(dāng)大的,對于許多小資源,甚至可能出現(xiàn)Header的容量比Body還要大,以至于在HTTP/2中必須專門考慮如何進行Header壓縮的問題。但是,以下幾個因素決定了通過合并資源文件減少請求數(shù),對節(jié)省Header成本并沒有太大幫助。

·Header的傳輸成本在Ajax(尤其是只返回少量數(shù)據(jù)的請求)請求中是比重很大的開銷,但在圖片、樣式、腳本這些靜態(tài)資源的請求中,通常并不占主要地位。

·在HTTP/2中Header壓縮的原理是基于字典編碼的信息復(fù)用。簡而言之,同一個連接上產(chǎn)生的請求和響應(yīng)越多,動態(tài)字典積累得越全,頭部壓縮效果也就越好。所以HTTP/2是單域名單連接的機制,合并資源和域名分片反而不利于提升Header壓縮效果。

·與HTTP/1.x相比,HTTP/2本身變得更適合傳輸小資源,譬如傳輸1000張10KB的小圖,HTTP/2肯定要比HTTP/1.x快,但傳輸10張1000KB的大圖,則大概率HTTP/1.x會更快些。這是TCP連接數(shù)量(相當(dāng)于多點下載)的影響,但更多是由TCP協(xié)議可靠傳輸機制導(dǎo)致的,一個錯誤的TCP包會導(dǎo)致所有的流都必須等待這個包重傳成功,這是HTTP/3要解決的問題。因此,把小文件合并成大文件,在HTTP/2下是毫無益處的。

圖4-4 HTTP/2的多路復(fù)用

[1] 連接復(fù)用技術(shù)在HTTP/1.0中并沒有默認開啟,是從HTTP/1.1開始變?yōu)槟J開啟的。

[2] 如何在不斷開連接的情況下判斷數(shù)據(jù)已取完將會放到稍后的4.3.2節(jié)去討論。

主站蜘蛛池模板: 黑水县| 北辰区| 驻马店市| 濉溪县| 静宁县| 阜新市| 闻喜县| 高雄市| 荣成市| 曲阜市| 射洪县| 广德县| 如东县| 定结县| 西宁市| 新蔡县| 长岭县| 中宁县| 天等县| 遵义市| 连云港市| 阜城县| 江口县| 称多县| 定安县| 广河县| 珠海市| 获嘉县| 囊谦县| 屯门区| 太原市| 秦皇岛市| 社旗县| 北流市| 平度市| 九龙县| 东乌| 邓州市| 绵竹市| 会泽县| 青海省|