從TCP長肥管道的視窗開啟慢的問題看TCP中繼的意義
在經典的《TCP/IP詳解(卷1)》中,作者提到了長肥管道,請先細讀相關章節。
請注意,這本書成書已經二十年,當時的頻寬是很低的,假設只有16Mbps,RTT為1個單位,頻寬換算成位元組的話大概就是2MBps,簡單地按照十進位制換算,記為2000000Bps,我們假設TCP的段長為1000位元組,初始擁塞視窗為1個段,那麼按照慢啟動原理,需要經過多少個RTT才能填滿這2MBps的頻寬呢?
設需要RR個RTT填滿頻寬,簡單的公式如下:
2R=20000002R=2000000 則: R=log2000000R=log2000000 大致計算一下,約21個RTT的時間能將頻寬填滿,也就是說,在前21個RTT,TCP處於慢啟動頻寬探測階段,此時的頻寬利用率是非常低的。
設最大BDP為bdpbdp,頻寬為BB,RTT為tt,則低頻寬利用率持續的時間為:
T=log2bdp×t=log2(B×t)×tT=log2bdp×t=log2(B×t)×t
從這個公式中,我們發現頻寬BB越大,RTT越大,低頻寬利用率持續的時間就越久,檔案傳輸的總時間就會越長,這是TCP慢啟動的本質決定的,這就是探測的代價。
TCP慢啟動這種行為好還是不好,這是一個形而上的問題,本文主要分析一種從這種行為的問題想到的一種優化手段。
如果你想清晰地觀測TCP的行為,我想沒有什麼比tcptrace圖更加合適的了吧,想快速瞭解tcptrace,就實際在wireshark中看一下,然後看看我的一篇文章: 在Wireshark的tcptrace圖中看清TCP擁塞控制演算法的細節(CUBIC/BBR演算法為例)
接下來我將直接上TCP慢啟動的tcptrace圖:
先仔細看明白上面的tcptrace圖,保證看明白後,故事就要開始了。
週六寫了一篇文章: 知乎上的一個問題,TCP in TCP隧道為什麼不好:https://blog.csdn.net/dog250/article/details/81257271 順便噴了一下知乎,緊接著第二天,有網友提出了一個非常典型的超級好的問題貼在了文章的評論裡,我原文摘過來,如下:
遇到一個問題,希望作者能指點迷經,不然我也要上知乎提問了。 A -> C 傳送100M大檔案,tcp連線,用時2.4s。 A -> B -> C 相同檔案相同tcp,用時確是1.6s 。經過中間一個節點轉發速度是增加的,效能為什麼會提升?ABC三個點在物理位置上大約呈直線(重慶,西安,大連)。 相同測試我擴充套件到4個節點,測試結果用時也是減少的。哪一個環節對是效能提升的關鍵?測試節點為機房,丟包率幾乎沒有。
我最初的回答如下:
TCP連線中,物理距離越遠,傳播延時越大,RTT越大,這意味著視窗開啟的越慢,慢啟動過程越久。你的物理頻寬越大,你所說的問題越明顯。中間加一個節點,每一段的RTT就變小了,擁塞視窗在每一段都可以更快的滿載。 PS:現在很多跨國SDWAN都採用這種中間終結TCP的方式以減少傳輸時延。
後來我琢磨著,這麼好的一個問題,還是單獨總結一篇隨筆吧,於是就決定寫下本文。
針對這位網友的問題,我覺得沒有什麼比一張tcptrace圖更能解釋問題的了,在給出tcptrace圖之前,先給出實驗拓撲:
然後我們看一下兩次實驗的tcptrace的對比圖。下圖分上下兩個部分,上半部分表示A到B直接建立TCP連線的慢啟動情形,下半部分表示加入了中間節點C後的兩個TCP連線接力的情形,A發起的TCP連線在C處終結:
很顯然,我們看到了最關鍵的一環,那就是,加入中間節點B後,A到B的RTT比A直接到C的RTT更小,RTT減小後,包絡線往下凸的更加狠了些,也就是說,擁塞視窗的增大速率加快了,視窗開啟的速度加快了*,這非常符合文初那簡單的公式:
T=log2bdp×t=log2(B×t)×tT=log2bdp×t=log2(B×t)×t
作為理論推導,我們假設沒有TCP慢啟動過程,節點之間直接用頻寬指定的固定的速率傳送資料(傳送線表現為一條直線而不是慢啟動時下凸曲線),會不會節省時間呢?為此,我做一幅圖來看個究竟:
並沒有!!
看來,通過中間加一個或者幾個節點進行TCP中繼僅僅在頻寬探測過程中可以提高效率。減少RTT可以加快慢啟動的過程,進而儘快到達頻寬滿載的狀態,提高總的頻寬利用率。
換句話說,為什麼大的RTT會導致頻寬利用率低,是因為TCP探測頻寬是靠ACK反饋計算的,而ACK的取樣頻率又是和RTT正相關的。RTT越大,ACK取樣頻率就會越低。
不過,還有trick。我們知道,TCP一旦超時或者應用資料空窗期超過一定的閾值,會從慢啟動重新開始,比如TCP的Linux實現中,有下面的引數:
tcp_slow_start_after_idle - BOOLEAN If set, provide RFC2861 behavior and time out the congestion window after an idle period. An idle period is defined at the current RTO. If unset, the congestion window will not be timed out after an idle period. Default: 1
一旦發生慢啟動的情形,頻寬探測將會重新開始,擁塞視窗將會從非常低的值根據ACK反饋增加(tcptrace圖上表現為傳送線的斜率增加,曲線下凸),此時,加入TCP中繼可以促進整個過程快速收斂(tcptrace圖上的表現為曲線下凸程度加劇,斜率快速增加),在總體效能上獲得良好收益。
顯而易見的是,對於那些短連線,比如慢啟動尚未結束,傳輸就終止的連線,TCP中繼的加入會提高效能,這是必然的。
非常感謝提出這個典型問題的網友。這個問題不僅僅揭示了一個關於TCP慢啟動的原則,更多的是提供了一種TCP加速的方案。
如果一個TCP跨越廣闊的物理距離,那麼使用傳統的TCP擁塞控制方案去優化它的傳輸效能將會面臨巨大的挑戰,我們知道TCP是一個端到端的協議,端和端之間越遠,物理上的傳輸時延就是越大,傳輸的控制就會越難,所以,我們儘可能要減少端和端之間的距離。
沒錯,CDN就是基於這樣的思路,將資源提前運送到裡客戶端儘可能近的地方,這樣就做到了確保資料包的傳播延遲儘可能短,而不至於將大量的時間浪費在頻寬探測這種慢啟動行為上。
然而有個問題,那就是動態資源,並不適合提前運送,那麼怎麼辦?試想下面的拓撲:
Client和Server之間要跨越廣袤的地理空間,在TCP/IP網路中觀測,則體現為要跨越L1,L2,L3,L4這四段完全不同的鏈路,RTT非常之長。TCP的端到端特徵將會給這種拓撲的擁塞控制帶來巨大的挑戰,可能L1鏈路丟包率很低,但是排隊延遲大,而L3鏈路丟包率非常高,L2又是一條限速鏈路…這將很難實施擁塞控制,鏈路之間不同的特徵會讓特定擁塞控制演算法的不同策略之間相互掣肘!任何一個地方出現丟包,都有可能將整個TCP連線拉回慢啟動狀態,頻寬利用率非常低。
The fxxking long-RTT,the fxxking end-to-end!
遇到這種問題怎麼辦?有辦法,就是讓TCP連線終結於鏈路的終點處,以鏈路為單位做中繼:
這是一個違背原則的設計,不過這也是一個解決問題的設計,和給人帶來問題的原始TCP設計不同,我覺得這個設計可以給人帶來收益。
獨立的鏈路獨立的擁塞控制,基於每一段鏈路建立TCP連線,有效縮短總體上浪費在慢啟動階段的時間。但是有個前提,你必須有能力在上圖中的特殊位置部署你自己的TCP中繼,這顯然是個生意,商業噱頭….
此外,上述討論均是在保證中繼節點的轉發延遲為0的前提下的,顯然TCP先終結再開啟,這顯然是要花費一定的時間的,不過不必擔心,主機轉發的時間和跨大範圍的RTT相比不是一個量級,中國和美國之間的RTT是百毫秒級別,而主機內處理和傳送一個數據包則是微秒級別的。唯一值得權衡的就是TCP握手的時延。
接下來我們來看一個細節吧。
還是那個公式:
T=log2bdp×t=log2(B×t)×tT=log2bdp×t=log2(B×t)×t
可以預見,在端到端的TCP被分割的無限小時,RTT,即tt無限接近於2B2B的時候,則上述的TT趨向於0,而RTT增加的時候,不光RTT增加的因素,顯然log2(B×t)log2(B×t)這個因子也在增加,二者相乘,增加的更加猛烈!這對端到端的慢啟動是多麼莫大的諷刺啊!
再看在最初針對本文的實驗描述問題的時候,我給出的那個對比圖: 這幅對比圖中是假設傳輸的資料總量是一個定值,顯然這是一個慢啟動尚未結束就結束傳輸的案例,要麼就是BDP很大,要麼就是檔案很小。如果檔案比較大的情況,假設檔案無限大,那麼如何在tcptrace圖裡清晰看出慢啟動時間對總下載時間的影響呢?假設RTT為tt,鏈路固有的頻寬為BB,我們只需要考慮下面的式子:
bdp=B×tbdp=B×t
可見,tt越小,bdpbdp就越小,一般我們可以將bdpbdp看作是最大的in-flight資料包,也就是擁塞視窗的最大值,那麼按照慢啟動的規則:
cwnd=2R=B×tcwnd=2R=B×t
可以看出,tt越小,nn就越小,即達到越大頻寬所需的時間就越短。我們也能從下面的圖示中看出:
超級顯然的一個事實,不是嗎?
轉自dog250的CSDN部落格,如有侵權,請聯絡刪除!