移動網(wǎng)絡性能揭秘--網(wǎng)絡協(xié)議及性能提升實踐
網(wǎng)絡處理的性能與延遲時間的增加是不成比例的。這是由于大多數(shù)網(wǎng)絡協(xié)議的內(nèi)在操作是雙向信息交換。本章的其余部分則側(cè)重于理解為什么會產(chǎn)生這些信息交換以及如何減少甚至消除它們交換的頻率。
圖3:網(wǎng)絡協(xié)議
傳輸控制協(xié)議
傳輸控制協(xié)議(TCP)是一種面向連接、基于ip的傳輸協(xié)議。TCP影響下的無差錯雙工通信信道對其他協(xié)議如HTTP或TLS來說都必不可少。
TCP展示了許多我們需要盡量避免的雙向通訊。這其中一些可以通過采用擴展協(xié)議如TCP Fast Open協(xié)議來替代;另一些則可以通過調(diào)整系統(tǒng)參數(shù)來達到最小化,比如初始化擁塞窗口。在本節(jié)中,我們將探討這兩種方法同時也提供一些TCP內(nèi)部組件的背景。
TCP Fast Open
初始化一個TCP連接約定需要3次信息交換,也就是我們所說的3次握手。TCP Fast Open(TFO)是TCP的一個擴展,它消除了通常握手過程中的往返延遲。
TCP在客戶端和服務端的三次握手協(xié)商操作參數(shù)使得雙方做健壯的雙向通信稱為可能。最開始的SYN信息(同步信息)代表客戶端的連接請求;如果服務端接受這個請求,那么它將返回一個SYN-ACK消息(同步和接受消息);最后,客戶端發(fā)送一個ACK消息來應答服務器。這時,一個邏輯連接就已經(jīng)建立完成,客戶端就可以發(fā)送數(shù)據(jù)了。這其中你如果注意到,3次握手過程中至少引入了一個RTT的延遲那就很好了。
圖4:TCP3次握手
從傳統(tǒng)角度來看,除了對連接進行回收利用外沒有其他方法來避免TCP3次握手造成的延遲。然而,這種想法發(fā)生隨著Tcp Fast Open IETF規(guī)范的引入發(fā)生了變化。
TFO允許客戶端在邏輯連接建立之前就開始發(fā)送數(shù)據(jù)。這實際上否定了3次握手中的往返延遲。這種優(yōu)化的累積效應是讓人印象深刻。根據(jù)谷歌的調(diào)查,TFO可以減少頁面40%的加載時間。雖然這個規(guī)范只是草案,但是TFO已經(jīng)被主流瀏覽器(Chrome22以上)和平臺(Linux3.6以上)所支持,并且其他供應商也保證將在不久以后會完全支持它。
TCP Fast Open是對3次握手協(xié)議的一個修正,它允許在同步消息(SYN Message)內(nèi)有少量的數(shù)據(jù)負載(如HTTP請求)。這個有效負責會傳遞給應用服務器,否則連接握手完成。
早些時候擴展方案像TFO最終因安全問題而失敗。而TFO通過使用安全令牌或者cookie來解決這個問題,也就是說在傳統(tǒng)的TCP連接握手過程中給客戶端分安全令牌(tooken),并且期望將安全令牌包含在TFO優(yōu)化請求的SYN消息中。
對于TFO的使用,這里有一些小的警告。其中最值得注意的是,在初始化的SYN消息中請求的數(shù)據(jù)缺乏冪等性保證。雖然TCP保證重復數(shù)據(jù)包(重復經(jīng)常發(fā)生)會被接受者忽略,但是這個保證并不適用于連接的握手過程。目前在規(guī)范草案中正在標準化這個解決方案,但是于此同時TFO仍然可以被安全的應用于冪等性處理。
初始擁塞窗口
初始擁塞窗口是TCP的一個可配置項并且有巨大的潛在能力來加速小的網(wǎng)絡事務。
最近的IETF規(guī)范促進通常的初始擁塞窗口的設(shè)置增長到3個報文段(如數(shù)據(jù)包)到10個報文段。這個建議是基于谷歌進行的廣泛研究,這個研究證明了這個參數(shù)的設(shè)置對性能有平均10%的提升。但如果不介紹TCP的擁塞窗口(cwnd)的話,這種設(shè)置的目的和潛在影響就不會被真正領(lǐng)會。
當在一個不可靠的網(wǎng)絡上進行操作時,TCP來保證客戶端和服務端的可靠性。這相當于一個承諾,所有發(fā)送出去的數(shù)據(jù)都會被接收到,或者至少看起來是這樣。其中,包丟失是滿足可靠性要求的最大障礙,這需要偵測、糾錯以及預防。
TCP采用一個肯定應答機制來檢測丟包情況,即每個發(fā)送出去的包都應該被它預定的接收方應答,如果沒有應答就意味著這個包在傳輸過程中丟失。在等待確認的過程中,傳輸數(shù)據(jù)包保存在一個特殊的緩沖區(qū)中,也就是所說的擁塞窗口。當這個緩沖區(qū)被塞滿時,一個被稱作cwnd耗盡的事件發(fā)生,所有傳輸停止,直到接收方應答后騰出有效空間來發(fā)送更多的數(shù)據(jù)包。這些事件在TCP性能中至關(guān)重要。
除了網(wǎng)絡帶寬的限制,TCP吞吐量根本上受cwnd耗盡事件發(fā)生頻率的限制,而這可能與擁塞窗口的大小有關(guān)。當TCP性能達到峰值時需要一個擁塞沖口來調(diào)節(jié)當前的網(wǎng)絡狀態(tài):擁塞窗口過大將增加網(wǎng)絡堵塞的風險--過度擁堵的網(wǎng)絡狀況會增加大量包丟失;過小則珍貴的網(wǎng)絡帶寬將不能充分被利用。從邏輯上講,對網(wǎng)絡情況了解的越多,肯能越能選擇一個最佳的擁塞窗口大小。實際情況則是,關(guān)鍵網(wǎng)絡屬性比如容量和延遲,是很難衡量的并且不斷在變化。而且,如果一個基于互聯(lián)網(wǎng)的TCP連接需要穿過許多網(wǎng)絡的這又會是一件更加復雜的事情。
由于缺乏手段來準確確定網(wǎng)絡容量大小,相反TCP通過網(wǎng)絡擁堵情況來推斷擁塞窗口大小。當TCP發(fā)現(xiàn)有包丟失時它就會擴大擁塞窗口的大小,提示下行某處有一個網(wǎng)絡無法處理當前的傳輸速率。通過采用這種擁塞避免機制,TCP最終最小化cwnd耗盡事件在某種程度上它消耗完為所有連接所分配的容量。那么現(xiàn)在,最終,我們也達到了目的,解釋清楚了初始擁塞窗口參數(shù)的重要性。
網(wǎng)絡擁堵情況只能通過丟包測試來檢測。一個新的或者空閑的連接由于沒有足夠丟包數(shù)據(jù)來證明創(chuàng)建擁塞窗口的最佳大小;TCP采用了一個比較明智的做法就是以一個可能最小情況導致網(wǎng)絡擁堵的大小一開始作為擁塞窗口大小;這最初意味著需要設(shè)置1個分片(大約1480字節(jié)),而且有些時候這種做法是推薦的。而稍后的實驗會演示一個高達4的設(shè)置也是有效的。在實踐中你也通常發(fā)現(xiàn)初始擁塞窗口設(shè)置為3報文段(大約4kb)。
初始擁塞窗口不利于小的網(wǎng)絡事務處理。這種效果很容易說明。在表中的3個報文段設(shè)置下,在發(fā)送3個數(shù)據(jù)包或者4k的數(shù)據(jù)后cwnd耗盡時間就會發(fā)生。假設(shè)數(shù)據(jù)包是連續(xù)發(fā)送的,響應的響應不會在任何所允許的往返時間(RTT)之前到達;假如RTT是100ms的話,那么有效傳輸速率只有可憐的400字節(jié)/秒。盡管TCP會調(diào)節(jié)自身的擁塞窗口來充分利用有效容量,但是它在一開始將會很慢。事實上,這種方式被稱為慢啟動。
為了減少慢啟動對較小的下載的性能影響,它需要重新評估初始擁塞窗口的風險回報。谷歌正是這樣做的,而且發(fā)現(xiàn)將初始擁塞窗口設(shè)置在10個報文段(約14kb)會在最小網(wǎng)絡擁堵情況下達到最大吞吐量?,F(xiàn)實世界中也證明了這樣設(shè)置總共可以減少頁面10%的加載時間;連接的往返延遲將得到更大的改善。
修改初始擁塞窗口的默認值也并不是那么簡單的。在大多數(shù)服務器操作系統(tǒng)下,一個系統(tǒng)級的配置只有有特權(quán)的用戶才可設(shè)置;這個參數(shù)也很少甚至不能被沒有權(quán)限的應用在客戶端配置。需要注意的是一個更大的初始擁塞窗口在服務器端可以加速下載,而在客戶端則可以加速上傳。如果無法在客戶端改變這個設(shè)置就意味著應該特別努力去減少請求負載的大小。
超文本傳輸協(xié)議
本節(jié)將討論在超文本傳輸協(xié)議(HTTP)性能方面來減少高的往返延遲的技術(shù)。
KeepAlive
KeepAlive是一個HTTP約定來允許同步連續(xù)的HTTP請求來使用同一個TCP連接。至少一個單組往返請求所需要的TCP的3次握手可以避免,每次請求可以節(jié)省幾十或者幾百毫秒。更深層次的,keepalive還有一個額外的但是未被提及的好處就是它在各個請求之間保留了當前TCP的擁塞窗口大小,這將導致更少的cwnd耗盡事件發(fā)生。
圖5:HTTP pipelining
實際上,管道使網(wǎng)絡延遲分布于網(wǎng)絡往返的各個HTTP事務中。例如,5個管線式的HTTP請求通過一個100毫秒的RTT連接時將產(chǎn)生一個平均20毫秒的往返延遲;在同樣條件下,10個管線式請求時這個平均延遲將減少到10毫秒。
但是,HTTP pipeling有明顯的缺點阻止了它被廣泛使用,即歷史上參差不齊的HTTP代理支持和拒絕服務攻擊的影響。
安全傳輸層協(xié)議
傳輸層安全性(Transport Layer Security,TLS)是一個面向會話的網(wǎng)絡協(xié)議允許在公共網(wǎng)絡安全地交換敏感信息。雖然TLS在安全通信方面卓有成效,但是在高延遲網(wǎng)絡下它的性能會下降。
TLS采用一個復雜的握手協(xié)議包括兩次交換客戶端-服務端信息。一個TLS安全的HTTP傳輸明顯比較慢也正是這個原因。通常,發(fā)現(xiàn)一個TLS慢實際上是在抱怨它的握手協(xié)議中多重往返所產(chǎn)生的延遲。
圖6:DNS查詢
通常,主平臺提供了緩存實現(xiàn)來避免頻繁的DNS查詢。DNS緩存的語義非常簡單:每個DNS響應包含一個存活時間(time-to-live,TTL)屬性來聲明結(jié)果會被緩存多長時間。TTL的范圍通常在幾秒鐘到幾天之間,但通常為幾分鐘。非常低的TTL值,通常在一分鐘以下,被用在影響負載分配或者減少服務器替換或ISP故障轉(zhuǎn)移的時間。
刷新失敗
高可用系統(tǒng)通常依賴于他們IP機房的冗余基礎(chǔ)設(shè)施主機。TTL值較小的DNS條目可以減少客戶指向失敗主機的時間,但是同時會導致大量額外的DNS查詢。所以說,TTL的值應該是減少停機時間和客戶端性能最大化的一個折中。
通常降低客戶端性能是沒有意義的,但當服務器故障時是個例外。有一個簡單的方法來解決這個問題,也就是說不是嚴格的遵守TTL,而是僅僅當更高層協(xié)議如TCP或HTTP檢測到不可恢復錯誤時才刷新DNS緩存。這種技術(shù)在大多數(shù)場景下模擬TTL保持DNS緩存一致的行為,然而這幾乎消除了任何基于DNS高可用性解決方案中的性能損失。
但是需要注意的是這個技術(shù)方案和其他基于DNS的分布式負載方案不兼容。
異步刷新
異步刷新是一個DNS緩存方法,它遵守已經(jīng)設(shè)置TTL規(guī)則但是在很大程度上消除了頻繁查詢DNS的延遲。在這項技術(shù)中,需要一個異步DNS客戶端庫如c-ares來實現(xiàn)。
這個方法很簡單,一個過期的請求仍然返回的是老的結(jié)果,但是后臺有一個非阻塞的DNS查詢來定時刷新DNS緩存。這種方案如果采用阻塞式(如同步)回調(diào)來查詢每條老的DNS數(shù)據(jù),那么這個方案幾乎不受DNS查詢延遲的影響,但是仍然和很多基于DNS的故障轉(zhuǎn)移方案以及分布式負載方案兼容。
總結(jié)
要減少移動網(wǎng)絡高延遲的影響就需要通過減少使移動網(wǎng)絡延遲急劇增加的網(wǎng)絡往返次數(shù)來實現(xiàn)。而采用軟件優(yōu)化專注于最大限度地減少或消除往返協(xié)議的消息是克服這項艱巨的性能問題的關(guān)鍵。