常見 TCP 擁塞避免算法瀏覽(上)

BBR, the new kid on the TCP block | APNIC Blog 這篇文章挺好的,下面不少內容也基於這篇文章而來。html

擁塞避免用於避免由於發送者發送數據過快致使鏈路上由於擁塞而出現丟包。算法

TCP 鏈接創建後先通過 Slow Start 階段,每收到一個 ACK,CWND 翻倍,數據發送率以指數形式增加,等出現丟包,或達到 ssthresh,或到達接收方 RWND 限制後進入 Congestion Avoidance 階段。下面這個圖挺好的,描述了好幾個過程,找不到出處了,只是列一下圖吧。微信

一些基礎東西能夠看:TCP congestion control - Wikipedia網絡

BDP

BDP 是 Bandwidth and Delay Product. 就是帶寬 (單位 bps) 和延遲 (單位 s) 的乘積,單位是 bit,也是 Source 和 Destination 之間容許處在 Flying 狀態的最大數據量。Flying 也叫 Inflights,就是發送了但還未收到的 Ack 的數據。tcp


來自[3]。ide

實際發送速率乘以延遲獲得的值越接近 BDP 說明算法的效率越高。函數

Reno

Reno 這種叫作 ACK-Pacing,基於 Ack 來確認網絡情況。若是能持續收到 ACK,表示網絡能正常承載當前發送速率。性能

Reno maintains an estimate of the time to send a packet and receive the corresponding ACK (the 「round trip time,」 or RTT), and while the ACK stream is showing that no packets are being lost in transit, then Reno will increase the sending rate by one additional segment each RTT interval.

Reno 下,每收到一個 ACK,CWND 加一,等出現丟包以後發送者將發送速率減半。理想情況下,Reno 能走出以下曲線:大數據


來自[1]優化

Reno 有以下假設:

  • 丟包必定由於網絡出現擁塞,但實際可能網絡自己很差,可能有固有的丟包機率,因此假設並不嚴謹;
  • 網絡擁塞必定是由於網絡上某個 buffer overflow 了;
  • 網絡的 RTT 和帶寬穩定不容易變化;
  • 將速率減半之後,網絡上的 buffer 必定可以從 overflow 變爲 drain。也即對網絡上 Buffer 大小也有假設;

從上面假設能看出,Reno 下受鏈路上 Buffer 大小影響很大。當 Buffer 較小的時候,鏈路上實際處在發送狀態的數據量還未達到 BDP (Bandwidth delay product) 時候可能就會出現丟包,致使 Reno 馬上減半發送速率,從而沒法高效的利用網絡帶寬。若是 Buffer 很大,超過 BDP,可能會進入 「Buffer Bloat」 狀態,即延遲畸高,由於即便 Reno 速度降爲一半依然不能保證使鏈路 Buffer 清空,或者說可能大部分時間鏈路上 Buffer 都處在非空狀態,且每次 Reno 由於丟包而降速時,會作數據重傳,致使以前發送的可能還依舊在鏈路上排隊的數據空佔資源沒起到做用最終白白丟掉,因而讓整條鏈路上持續存在固有延遲。

Reno 的問題:

  • 上面提到了,受鏈路 Buffer 影響很大;
  • 對高帶寬網絡,好比帶寬是 10Gbps,即便假設延遲很是低只有 30ms,每一個 RTT 下 CWND 增長 1500 個八進制數,得好幾個小時才能真的利用起這個帶寬量,而且要求這幾個小時內數據都不能有丟包,否則 Reno 會降速;
  • Reno 每收到一個 Ack 就開始擴大 CWND,對鏈路上一塊兒共享鏈路的其它 RTT 較大的鏈接不友好。好比一個鏈接 RTT 很低,它的 CWND 會比別的共享鏈路的鏈接大,不公平的佔用更多帶寬;

BIC

BIC 叫 Binary Increase Congestion Control,是在 Reno 基礎上作改進,將 CWND 擴大過程分紅三個階段,第一階段是在遇到丟包後將 CWND 降爲 β × Wmax,(通常 β 是 0.5,Wmax 是丟包時 CWND)但記住以前 Wmax 最大值,以後以一個較快速度增長 CWND,在接近 Wmax 後 CWND 增長量是一個 Wmax 和 CWND 之間的二次函數,稱爲 Binary Increase,逐步去接近 Wmax,到達 Wmax 後又轉換爲二次曲線去探測下一個極限。具體內容能夠看 Wiki,在這裏:BIC TCP - Wikipedia

大概是這麼個圖,好處就是丟包後能快速恢復,並在穩按期盡力保持更長時間,並還能支持探測更高帶寬值。


來自[3]

Cubic

Cubic 在 BIC 的基礎上,一個是經過三次函數去平滑 CWND 增加曲線,讓它在接近上一個 CWND 最大值時保持的時間更長一些,再有就是讓 CWND 的增加和 RTT 長短無關,即不是每次 ACK 後就去增大 CWND,而是讓 CWND 增加的三次函數跟時間相關,無論 RTT 多大,必定時間後 CWND 必定增加到某個值,從而讓網絡更公平,RTT 小的鏈接不能擠佔 RTT 大的鏈接的資源。

平滑後的 CWND 和時間組成的曲線以下,能夠看到三次曲線保留了 BIC 的優勢,即在丟包後初期,CWND 能快速增加,減少丟包帶來的影響;而且在接近上一個 CWND 最大值時 CWND 增加速度又會很是慢,要經歷很長時間才能超越 Wmax,從而能盡力保持穩定,穩定在 Wmax 上;最後在超越 Wmax 後初期也是增加的很是慢,直到後來才快速的指數形式增加,用於探測下一個 Wmax。


來自[3]

Cubic 最終出來的曲線以下。黃色是 CWND,藍色線是網絡上隊列擁塞包數。藍色箭頭表示 queue fill,綠色箭頭表示 queue drain。看到有兩個淺綠色的線,高的是開始丟包,低的是隊列開始排隊。由於這裏畫的是說網絡上 bandwidth 是固定的,buffer 也是固定的,因此 Cubic 下沒有超越 Last Wmax 繼續探測下一個 Wmax 的曲線,都是還未到達第一次的 Wmax 時候就由於丟包而被迫下降了 CWND。第一次個黃色尖峯能那麼高是由於當時網絡上 buffer 是空的,後來每次丟包後 Buffer 還沒來得及徹底清空 CWND 又漲上來致使數據排隊了,因此後來的黃色尖峯都沒有第一個高。


來自[3]

Cubic 的優點:

  • 由於跟 RTT 無關因此更公平;
  • 更適合 BDP 大的網絡。Reno 不適合是由於它依賴 RTT,BDP 大的時候 RTT 若是很高,會好久才能將傳輸效率提上去;

Cubic 缺點:

  • 當 Bandwidth 變化時候,Cubic 須要很長時間才能從穩定點進入探測下一個 Wmax 的階段;
  • 更易致使 Bufferbloat。Reno 下若是鏈路上 Buffer 很大出現擁塞後 RTT 也會很長,好久纔會收到 ACK,纔會增長 CWND;但 Cubic 的 CWND 增加跟 RTT 無關,到時間就增加,從而更容易加重鏈路負擔。Bufferbloat 能夠看下節。

綜合來看 Cubic 適合 BDP 大的高性能網絡,性能意思是帶寬或者說發送速率,發送速率足夠大 Cubic 把 Buffer 佔滿後才能快速清空,纔能有較低的延遲。

一個挺好的講 Cubic 的 PPT:Cubic,還有論文。Linux 2.6 時候用的 CUBIC。

Bufferbloat

Bufferbloat 在 Bufferbloat - Wikipedia 講的挺好了。簡單說就是隨着內存愈來愈便宜,鏈路上有些設備的 Buffer 傾向於配置的特別大,而流行的 TCP Congestion Avoidance 算法又是基於丟包的。數據在隊列排隊說明鏈路已經出現擁塞,原本應該當即反饋給發送端讓發送端減少發送速度的,但由於 Buffer 很大,數據都在排隊,發送端根本不知道本身發出去的數據已經開始排隊,還在以某個速度 (Reno)甚至更高速度(Cubic,到時間就增長 CWND) 發送。等真的出現丟包時候,發送端依然不知道出現了丟包,還會快速發消息,直到丟的這個包被接收端感知到,回覆 ACK 後發送端才終於知道要下降發送速度。而重傳的包放在鏈路上還得等以前的數據包都送達接收端後才能被處理。若是接收端的 Buffer 不足夠大,不少數據送達接收端後都會丟棄,得等重傳的包到達(Head Of Line 問題)後才能送給上游,大大增長延遲。

Bufferbloat 一方面是會致使網絡上超長的延遲,再有就是致使網絡傳輸不穩定,有時候延遲很小,有的時候延遲又很大等。

能夠參考:Bufferbloat: Dark Buffers in the Internet - ACM Queue

PRR

在 CUBIC 之上又有個優化,叫作 Proportional Rate Reduction (PRR),用以讓 CUBIC 這種算法在遇到丟包時候能更快的恢復到當前 CWND 正常值,而不過度的下降到太低的水平。

參考:draft-mathis-tcpm-proportional-rate-reduction-01 - Proportional Rate Reduction for TCP。不進一步記錄了。Linux 3.X 的某個版本引入的,配合 CUBIC 一塊兒工做。

鏈路上的隊列模型

爲了進一步優化 Cubic,能夠先看看鏈路上隊列的模型。

  • 當鏈路上數據較少時,全部數據都在發送鏈路上進行發送,沒有排隊的數據。這種狀況下延遲最低;
  • 當傳輸的數據更多時候,數據開始排隊,延遲開始增大;
  • 當隊列滿的時候進入第三個狀態,隊列滿了出現丟包;


來自[1]

最優狀態是 State 1 和 State 2 之間,即沒有出現排隊致使延遲升高,又能徹底佔滿鏈路帶寬發送數據,又高效延遲又低。
而基於丟包的 Congestion Avoidance 策略都是保持在 State 2 的狀態。而 Cubic 是讓 CWND 儘量保持在上一個 Wmax 的狀態,也即 State 2 末尾和即將切換到 State 3 的狀態。因此 Cubic 至關於儘量去佔用鏈路資源,使勁發數據把下游鏈路佔滿但犧牲了延遲。

因而能夠看到,爲了保持在 State 1 和 State 2 狀態,咱們能夠監控每一個數據包的 RTT,先盡力增大 CWND 提升發送率若是發現發送速率提升後 RTT 沒有升高則能夠繼續提升發送速率,直到對 RTT 有影響時就減速,說明從 State 1 切換到了 State 2。

咱們但願讓 CWND 盡力保持在下面圖中標記爲 optimum operating point的點上,即 RTT 又小,鏈路上帶寬又被佔滿。在這個圖上,RTprop 叫作 round-trip propagation time,BtlBw 叫作 bottleneck bandwidth。橫軸是 inflights 數據量,縱軸有兩個一個是 RTT 一個是送達速度。看到圖中間有個很淡很淡的黃色的線把圖分紅了上下兩部分,上半部分是 Inflights 數據量和 Round trip time 的關係。下半部分是 Inflights 和 Delivery Rate 的關係。

這裏有四個東西須要說一下 CWND、發送速度、inflights,發送者帶寬。inflights 就是發送者發到鏈路中還未收到 ACK 的數據量,發送速度是發送者發送數據的速度,發送者帶寬是發送者能達到的最大發送速度,CWND 是根據擁塞算法獲得的當前容許的 inflights 最大數據量,它也影響着發送速度。好比即便有足夠大的帶寬,甚至有足夠多的數據要發,但 CWND 不夠,因而只能下降發送速度。這麼幾個東西會糾纏在一塊兒,有不少文章在描述擁塞算法的時候爲了方即可能會將這幾個東西中的某幾個混淆在一塊兒描述,看的時候須要盡力內心有數。

回到圖藍色的線表示 CWND 受制於 RTprop,綠色的線表示受制於 BltBw。灰色區域表示不可能出現的區域,至少會受到這兩個限制中的一個影響。白色區域正常來講也不會出現,只是說理論上有可能出現,好比在鏈路上還有其它發送者一塊兒在發送數據相互干擾等。灰色區域是不管如何不會出現。

先看上半部分,Inflights 和 Round-Trip Time 的關係,一開始 Inflights 數據量小,RTT 受制於鏈路固有的 RTprop 時間影響,即便鏈路上 Buffer 是空的 RTT 也至少是 RTprop。後來隨着 Inflights 增大到達 BDP 以後,RTT 開始逐步增大,斜率是 1/BtlBw,由於 BDP 點右側藍色虛線就是 Buffer 大小,每一個藍點對應的縱軸點就是消耗完這麼大 Buffer 須要的時間。消耗 Buffer 的時間 / Buffer 大小 就是 1/BltBw,由於消耗 Buffer 的時間乘以 BltBw 就是 Buffer 大小。感受很差描述,反正就這麼理解一下吧。主要是看到不可能到灰色部分,由於發送速率不可能比 BltBw 大。等到 Inflights 把 Buffer 佔滿後到達紅色區域開始丟包。

下半部分,Inflights 比較小,隨着 Inflights 增大 Delivery Rate 越大,由於此時還未達到帶寬上限,發的數據越多到達率也就越高。等 Inflights 超過 BDP 後,Delivery Rate 受制於 BtlBw 大小不會繼續增大,到達速度達到上限,Buffer 開始積累。當 Buffer 達到最大限度後,進入紅色區域開始丟包。

以前基於丟包的擁塞算法實際上就是和鏈路 Buffer 一塊兒配合控制 Inflights 數據量在 BDP 那條線後面與 BDP 線的距離。之前內存很貴,可能只比 BDP 大一點,基於丟包的算法延遲就不會很高,可能比最優 RTT 高一點。但後來 Buffer 愈來愈便宜,鏈路上 Buffer 就傾向於作的很大,此時基於丟包的擁塞算法就容易致使實際佔用的 Buffer 很大,就容易出現 BufferBloat。

爲了達到最優勢,發送速率必須達到 BltBw,從而佔滿鏈路帶寬,最高效的利用帶寬;再有須要控制 Inflight 數據量不能超過鏈路的 BDP = BtlBw * RTprop,從而有最優的延遲。發送速率能夠超過 BltBw,能夠有隊列暫時產生,但數據發送總量不能超 BDP,從而讓平均下來延遲仍是能在最優勢附近。


來自[4]

TCP Vegas

Vegas 就如上面說的理想中算法同樣,它會監控 RTT,在嘗試增長髮送速率時若是發現丟包或者 RTT 增長就下降發送速率,認爲網絡中出現擁塞。但它有這些問題:

  • CWND 增加是線性的,跟 Reno 同樣須要好久才能很好的利用網絡傳輸速率;
  • 最致命的是它不能很好的跟基於丟包的 Congestion Avoidance 共存,由於這些算法是讓隊列處在 State 2 和 3 之間,即儘量讓隊列排隊,也至關於儘量的增大延遲,但 Vegas 是儘量不排隊,一發現排隊就當即下降發送速率,因此 Vegas 和其它基於丟包算法共存時會逐步被擠出去;

參考文獻在下一篇統一提供。

更多內容歡迎關注 LeanCloud 官方微信號:「LeanCloud 通信」
相關文章
相關標籤/搜索