我一再強調,BBR算法是個分界點,全部的TCP擁塞控制算法,被分爲BBR以前和BBR以後的(其實發現,這並非我我的的觀點,不少人都這麼認爲,全部想寫本文探個究竟)。固然這裏的」全部「並不包括封閉的那些算法,好比垃圾公司Appex的算法,或者偉大的垃圾微軟的算法。任何的算法都內含了一個進化的過程,CUBIC和Reno看起來很是不一樣,可是卻屬於同一個思想,所以能夠說,CUBIC是Reno的高級版本(將一種鋸齒換成另外一種鋸齒而已),事實上,TCP擁塞控制算法一直處在不斷的改進之中,起初,Reno算法,而後就是NewReno,再日後就是各類混戰,直到最終落實到了CUBIC,起碼在Linux平臺是這麼一個過程,其它的平臺,也是大同小異。
關於從Reno到CUBIC進化過程的細節,這裏不談,相信80%的人可能並不在意,另外20%在意的人可能比我懂得更多,因此,這裏不談。
...[可是我仍是要把篇幅留下來]
這裏要談的是BBR和Reno/CUBIC的一致性,這聽起來可能讓人以爲驚奇,它們不是有本質的區別嗎?爲何要說它們的一致性呢?
我特別感謝Van Jacobson這位大師。雖然他不認識我,我也沒見過他,可是他提出的問題致使了Reno,CUBIC直到BBR的誕生,我估計Appex和微軟這種只吃飯不拉屎的貔貅也是借鑑了他的思想。
首先要聲明,我並非Google的粉絲,因此我更能採用公正的眼光看待BBR算法,雖然我一再強調它的多麼多麼的創新,可是這並不意味着BBR就是最好的,若是把BBR做爲一個起點,相似Reno那樣的起點,BBR自己也會有一個進化的過程,我但願的是你們都參與這個過程,我能作的只是拋磚引玉。我只是一個宣傳者,一個鼓手。順便說一句,我比較崇拜寫出《大教堂與集市》而且會吹笛子的Eric S· Raymond,他自稱本身是一名鼓手(爲何不是笛子手呢?)。
咱們先看一下Van Jacobson提出的一些的問題。
To achieve network stability, TCP entity should obey a ‘packet conservation’ principle, which is borrowed from physics.
What is packet conservation principle?
--A new packet is not put into the network until an old packet leaves( i.e. ‘conservation’) while maintaining ‘equilibrium’ in which a connection runs stably
by effectively using the bandwidth available on the path.
OK,理解這個是簡單且直觀的,而後呢?VJ繼續說:
Three failures of packet conservation
1. The connection does not get to equilibrium
2. A sender injects a new packet before an old packet
has exited
3. The equilibrium can’t be reached because of
resource limits along the path
1, 3 : Equilibrium problem 2 : Conservation problem
To fix those failures, three algorithm are introduced
* Slow-start algorithm for 1.
* Retransmit timeout estimation algorithm for 2.
* Congestion avoidance algorithm for 3.
OK,問題提過了,方案也有了,接下來就是如何解決問題了,也就是說,如何把方案變成代碼。算法
咱們看一張圖,一張到目前爲止,BBR算法以前的TCP擁塞控制算法的圖解,假設你對TCP擁塞算法有所瞭解那麼這個圖就是直觀的:緩存
固然,這裏並不包括快速重傳/快速恢復算法。爲何?難道快速恢復不重要嗎?不是不重要,而是,快速恢復屬於上述經典圖解的一個優化!我不是說過嗎,算法是一個進化的過程,無論最終變得多麼複雜,其根本是不會變的。在BBR以前,其根本就是上圖所示。網絡
按照VJ的想法,TCP擁塞控制的根本就是實現上圖中紅色框框所框住的那些階段的處理邏輯。咱們先看看BBR以前是怎麼處理的,這雖然並不重要,但看看畢竟很少:優化
cwnd = 1; while(1) { send packets of min (cwnd, rwnd); // burst wait until receiving all ACKs for the previous sent packets slow-start if ( timeout occurs ) break; else cwnd = 2*cwnd; } threshold = cwnd/2; cwnd = 1; while(1){ if ( cwnd < threshold ){ send packets of min (cwnd, rwnd); //burst wait until receiving all ACKs for the previous sent packets slow-start if ( timeout occurs ){ threshold = cwnd/2; cwnd = 1;} else cwnd = 2*cwnd;} if ( cwnd == threshold || cwnd > threshold ) send packets of min (threshold, rwnd); //burst } else if ( cwnd > threshold ){ send a new packet whenever an ACK is received //self-clocking if ( Sender receives all ACKs for the previous sent packets ) {cwnd = cwnd + 1; send a new packet;} //to probe more bandwidth if ( timeout occurs) {threshold = cwnd/2; cwnd = 1;} //to avoid congestion } }
然而,仍是VJ的話:
「The way we’ve always done it」 is not necessarily the same as 「the right way to do it」
太TMD的經典了!
而後,咱們看下BBR是怎麼作的。ui
經過研究上面兩個圖,你能看出什麼呢?
Reno/CUBIC:
它們是事件驅動的!不管是丟包,仍是RTT增長,都被視爲一種嚴重的事件,這些嚴重的事件致使TCP擁塞控制系統在」To find current bandwidth「,」To avoid congestion「以及
」To probe more bandwidth「之間切換,最終落實下來的就是促使擁塞算法(不管Reno,Vegas仍是CUBIC)調整窗口的大小。
那麼誰來發現這些事件是否發生呢?答案是TCP擁塞控制狀態機。擁塞控制狀態機直接主導這些狀態的切換,只要它發現丟包,不論是不是真的,都會拉低窗口值。
因此說,Reno/CUBIC的窗口調整是被動的。
BBR:
bbr是反饋驅動的!bbr內置了自主的調速機制,不受TCP擁塞控制狀態機的控制,bbr算法是自閉的,它能夠本身完成VJ的全部狀態探測以及切換,無需外界干涉,且對外界的干涉視而不見。
bbr週期性的試圖探測是否有更多的帶寬,若是有,那麼就利用它,若是沒有,就退回到以前的狀態。
因此說,bbr的窗口調整是主動的。spa
當你看到Reno/CUBIC和bbr的區別的時候,可能會想起中斷和輪詢的區別,bbr和輪詢之間的不一樣點是,bbr有事實的反饋。code
好了,這就是Reno/CUBIC和bbr的區別,它們一樣完成了」To find current bandwidth「,」To avoid congestion「以及」To probe more bandwidth「的邏輯,只是一個是事件驅動的被動實現,一個是反饋驅動的主動實現。和同事聊天時,在聊到高血壓的時候,有一個很相似的事實:有一些人只有當以爲本身頭暈的時候纔會採起降血壓的措施,另外一些人則不斷喝水而且少菸酒讓本身血壓維持在正常水平...
本文的最後,咱們來看一下bbr算法自身的一些話題。
事實上,早在1981年的時候Gail & Kleinrock就發現了bbr採用的模型:
Rather than using events such as loss or buffer occupancy, which are only weakly correlated with congestion, BBR starts from Kleinrock’s formal model of
congestion and its associated optimal operating point.
然而,在當時和後續30年中,人們認爲這是一件比較困難的事情,你怎麼知道當前的帶寬是否是最大的且RTT是最小的,要知道,帶寬和RTT是相關的,它們的乘積就是BDP!直到最近,Google的bbr才意識到了一種很是簡單的方法:
While it is impossible to disambiguate any single bandwidth or RTT measurement, a connection's behavior over time tells a clearer story.
有多麼clearer呢?
bbr採用了一個時間窗口內的帶寬測量和RTT測量,關鍵是這句話裏面的」over time「!bbr算法之因此能夠完成最大帶寬和最小RTT的測量,關鍵在於如下的點:orm
當到達最大帶寬的時候,RTT開始增加(全部不以增長速率爲目的的緩存都是耍流氓!緩存不直接增長速率,緩存經過下降丟包來提升網絡利用率!),不管從RTT視圖仍是從帶寬視圖來看,兩個操做點都是一致的,這是基本!那麼bbr是怎麼測量的呢?
先穩定住帶寬,當帶寬再也不增長的時候,bbr認爲已經達到最大帶寬可,而後在此基礎上測量RTT,帶寬的RTT的操做點是重合的!就是這麼簡單且直接。
如今,你已經明白了,不管任何TCP擁塞控制算法,解決的都是一個問題(這個結論太簡單了,簡直就是廢話)。這是方法不一樣而已。three