令人躁動一時且令人不安的TCP BBR演算法
阿新 • • 發佈:2018-12-18
雖然我在這個週六(2016/12/17)荒廢了一天而毫無意義的加班,我依然要在次日把上一週的點滴記錄下來。以下在2016/12/18下午19時之前的文章,全屬週六通宵之作。可以說,這個週末過得比較水。我已經30多個小時沒有睡覺了。週六加班大半天,晚上跟同事打了兩個多小時技術電話...你們能想象到跟同事電話裡帶著形而上的觀點聊兩個多小時技術,我們對工作該是多麼負責啊!我並無意表達我要表現出很忙的意思,事實上我幾乎從不加班,但這一次,不一樣。 BBR演算法是Google的大作,既然我一開始認同了它,那就不能在遇到它不適應的場景時隨意拋棄它,要想辦法優化它。要像幾年前搞VPN時的那種態度去對待BBR。本文的目的不是要把BBR演算法拉下神壇,本文算是一個總結性的,然而點點滴滴的字裡行間卻充斥著我的不滿。如果說誰能在30多個小時不睡覺之後還能寫一篇濃濃雞湯味的暖文,那是在把事情做成功之後的慶功宴會喝醉以後吧....現在,除了咒罵,別無其他。-------------------------Linux 4.9核心的釋出,最令人興奮的特性當屬BBR演算法了,這個演算法的釋出讓4.9核心簡直成了一個里程碑。在4.9核心釋出之前,我就詳細分析過BBR演算法的諸多細節,我的感情色彩從推崇到平淡直到冷漠和咒罵,我個人在剛剛刨去了對BBR的無知而黃袍加身給它的光環後,從頭來看,我應該在最初的“推崇”前面加上盲目兩個字,是為“盲目推崇”。在我盲目到一定程度而徹底迷失之前,我仔細研究了與TCP無關但與BBR相關的很多細節,包括骨幹網拓撲,時下流行的幾款Cisco裝置的轉發細節(比如Cisco Nexus 95xx系列),以及SDN網路的配置,發現BBR根本就不是一個獨立的模組,它完全做不到像CUBIC演算法那樣的獨立收斂,它依賴的外部環境太多太精細,以至於它完全不通用。如果你測試了BBR,並且結果還不錯,就像Google的報告中顯示的那般,那麼恭喜你,你的網路環境與Google的無異,如果你的測試結果不達預期,那麼同樣恭喜,因為根本沒人預期它的表現能有多好,庸人自擾之。Google的測試報告非常嚴謹,只是說“在....情況下,BBR針對傳統TCP的加速比達到...”。 你可知,這個加速比是路由器在擺脫了N加速比之後釋放給BBR的額外恩惠,你知道路由器出口排隊的N加速比是什麼意思嗎?我簡單點描述好了,路由器的每個出口佇列容量必須是其它所有N個入口處理頻寬之和的數量級與線速係數的乘積(理論上佇列容量要等於入口頻寬之和,但考慮到線速轉發,佇列容量要遠遠小於這個值),這就是N加速比問題。目前骨幹路由器交換機大多數是出口佇列,這樣N加速比問題就會帶來一個很嚴重的後果,即BufferBloat!我們知道,骨幹節點的頻寬都是很大的,且節點的出入度都很高,N加速比問題帶來的結果就是出口佇列非常大(這就是深佇列),然而如何保證每個入口只用其中的1/N呢?這就是各式各樣的佇列規則和排程演算法的範疇了,這就是說,在佇列規則和排程演算法不區分五元組的情況下,深佇列快取很容易被誤用!遺憾的是,骨幹路由節點很少會關注五元組資訊,雖然各種佇列管理機制,雜湊演算法等宗旨都是為了保證“公平”,但是公平的原則是建立在資訊量足夠的基礎之上的。大多數的佇列快取又是共享快取,所以誤用佇列快取的各種流量就非常多了。作為技術潔癖患者,我認為TCP褻瀆了IP,你覺得呢?這是我噁心TCP的理由之一。 比如說,由於運營商一般會對UDP流量進行策略化監管或者限速,目前,有一種非常簡單的UDP雙邊加速的方式就是,僅僅將協議號由UDP改成TCP即可!而UDP允許一定程度的丟包,所以說,路由器交換機的AQM對其而言,就是刑不上大夫! SDN網路或者大型企業骨幹網路相比上述的運營商骨幹(包括分層的接入,匯聚,核心,跨BGP...),就簡單多了。 在SDN網路或者新建的大型企業骨幹節點,N加速比問題幾乎被排除了。為什麼呢?因為這種網路的流量非常容易被排程到一個足夠均衡的平衡點。雖然在拓撲復雜性上,骨幹網是極其複雜的,然而在流量複雜性上,卻又是十分簡單,因此控制平面和管理平面可以設計的非常簡潔,用以指導資料平面在上述平衡點附近的高效轉發。 BBR演算法在此處發力!-------------------------可是在你身邊又有哪個聲稱搞TCP的人會去了解上面那些事情呢? 當然,我說這話有點偏見,因為我本身就喜歡上面說的那些路由器呀交換機呀等東西,並且持續了11年,我比較噁心TCP協議以及Socket之類的,並且噁心了11年,所以說在我的字裡行間充斥著不公平的詛咒!-------------------------Linux 4.9釋出了,大部分並不知所以然的所謂跟風者開始YY了,YY BBR多麼多麼神奇,多麼有望取代CUBIC成為預設擁塞演算法...YY它可以超過CUBIC幾十倍...那不過是一組你看到的資料,到我這裡測試說不定就是渣渣!我上半年也寫過一個演算法,在我的測試環境下可以秒CUBIC十幾倍,可為什麼它即便在我個人這裡也銷聲匿跡了呢? 我不喜歡把“場景”這個詞用在網路上,我覺得用“屬性”這個詞更好。場景是可變的,而屬性則是固有的。我來簡單描述一下BBR適用於什麼樣子屬性的網路。先說結論吧,BBR適用於穩定性比較好的網路。我以道路來做類比,就是兩類,一個是高速公路以及城市快速路,一個是展現城市視窗的道路,總之就是秩序感比較強的那種。然而在我們的生活中,80%的時間我們會花在“最後一公里”的道路上,這是一個80/20原則!正是那最後一公里拖慢了整體的效率! BBR比較適合跑在從一個收費站到另一個收費站之間,或者沿著長安街從東單跑到西單,要麼就是沿著世紀大道從陸家嘴跑到世紀公園,一旦拐到小衚衕裡,就分分鐘成渣了。這並沒有說BBR不好,就像從來都沒有人說蘭博基尼不好一樣,但是買輛蘭博基尼在衚衕裡開的,要麼是為了裝逼,要麼就是傻逼。 我來根據BBR的狀態機畫一幅其速度/時間的草圖:一旦發生頻寬抖動,BBR將不得不承受“頻寬無法充分利用”(頻寬向上抖動)或者“保守導致激進”(頻寬向下抖動)...不管怎麼說,基於內部伺服器的策略都是一廂情願的輪詢做法,它的高效性完全建立在其對整個場景的充分理解基礎之上,否則,BBR必須為自己的自行其是付出代價! 溫州皮鞋廠老闆非常鄙視這種不知其所以然的盲目測試,這一點的認同使得我和溫州老闆成了朋友。還記得當年評估Intel 82599網絡卡特性的時候,我告訴過他們,到底是輪詢還是中斷,完全取決於你對流量屬性的感知程度,你要充分測量流量的每一個細節!在這一點上,BBR看樣子是做對了!然而,我膩了! 溫州老闆這麼週末要研究下BBR演算法,另囑咐我寫一篇關於BBR的科普文章,我本來想答應,但是週末要加班,就只能拖延。我自認為我寫一篇可以讓任何人看懂的科普性的BBR文章並不太難,我相信自己有這個能力,但是最終,我還是隻能拖延,我對溫州老闆表示抱歉。 從上圖可以看到一個值得優化的地方,那就是下面的函式: 這一點可以看出,如果在ProbeMore週期,BBR的搶佔性是很強的,只要增加的傳送速率一直能帶來增加的反饋速率,這個過程就一直繼續,然而可能其它的資料流並不會等到BBR到達ProbeMore週期就把頻寬搶完了!在ProbeBW狀態下,ProbeMore只有一次機會,接下來會短暫的ProbeDrain,然後就是6個平穩的傳送週期,大約會持續6個RTT的時間,在這段時間內發生的任何事情,BBR都無法感知!會發生什麼事情呢?很簡單,要麼可用頻寬增加了,要麼可用頻寬減少了,如果是減少了,在時間視窗內,以最大的頻寬傳送資料將會造成丟包,BBR並不識別丟包而降速,所以此時BBR的行為就會比較激進,直到新的ProbeMore之後退出後的ProbeDrain週期!問題在於,收斂到已經減少的可用頻寬,需要多個RTT!這又是為什麼? 為什麼不能像ProbeMore一樣,一次性收斂到新的可用頻寬呢? 原因很簡單,因為不管什麼情況,只要你主動降速,ACK馬上就能把降速的效果反饋回來,這是100%確定的,沒有任何訊號告訴你“降速已經夠了”!所以說,每輪降速一次就是一個合理的選擇,因此可以說,對於BBR而言,實際上是乘性增,加性減的!這種策略用於補償其對頻寬變化的不敏感性! 因此,可以說,BBR的模型是一個探測並使用可用頻寬的模型,所依賴是其精細測量出來的rate_sample資料,這不是一個自動收斂的模型,和Reno/CUBIC完全不同。在Reno/CUBIC那裡,演算法對諸如rate_sample的細節是完全無知的,僅僅依靠ACK Clock就可以驅動演算法的運作,最終收斂到一個公平的平衡點。-------------------------我們再看本文中的唯一圖示,發現點什麼嗎?如果你開著一輛車在高速公路上,你會體會到這幅圖的深意。 如果你的前車不違規,那麼按照跟車距離200米來算,你的大部分時間都處在勻速狀態,在不考慮限速120的前提下,你可能會隔一段時間嘗試一下更快的速度,但是發現前車並沒有提速的情況下,你會慢下來回歸原來的速度,宗旨就是保持跟車200米。 然而,如果你在衚衕裡開車,BBR的原則將完全失效!請參考上圖來想象一下你在以下的道路上開車:上海嘉定區葉城路,倉場路,博樂路,深圳寶安區寶安大道,安陽市文峰中路...BBR並不是無級變速,它比較類似與換擋變速機制。加速快,剎車慢。.........如今,Linux 4.9核心的釋出,BBR騷動了全場,然而令人悲哀!太多的連什麼是TCP都不懂的人要求測試BBR,這讓我感到複雜並且悲傷! 座椅爆炸!經理爆炸! 我的措辭表達了溫州老闆的內心,我的措辭表達了王姐姐的內心,我在寫這些措辭的時候無時無刻不在想著跟小雨對罵的加班的2012年12月29日。 如今,BBR令人躁動不安,把Google的那幾個人推向神壇。我由於是國內首批接觸BBR的那幫人,在4.9核心釋出前就寫了幾篇文章,很多人給我發郵件,有大量要求加速網路的,有大量要求入職的(甚至包括我現在的公司同事...),有拉私活的,但唯獨很少有技術交流的(略有一二,已經加為好友並交談甚久),敢問你們懂BBR嗎?4.9核心一發布,一窩蜂的全出來了,典型的XX拿來主義,XX拿資料說話! 我敢說,BBR無法為你們大多數人帶來可觀的資料,因為Yuchung Cheng和Neal Cardwell也是揍一拳會趴下的凡人,也不是吃一口屎一口翔香的猛士!但是不可否認,Yuchung Cheng和Neal Cardwell比大多數人更瞭解自己的網路的屬性,更知道SDN的細節,而這些正是TCP程式設計師所忽略的!如果不瞭解這些,那就是劣根性帶來的悲哀!絕大多數搞TCP的程式設計師,喜歡掰扯,且根本不懂網路,然而顯得裝逼,我不屑為伍,以前我是據理力爭,然而現在不了。-------------------------Linux 4.9釋出了,讓很多人躁動了,這讓人不安! 對於TCP擁塞控制策略,任何人說任何話都可以自圓其說,就像任何人在任何地點可以評論股市一樣!不妄言,甚至閉口不言,寧願充當啞巴是最明智的,但這並不意味著其它所有人都是智者! 你可以拿BBR資料來炫耀,同時也可以來打臉,但這僅僅是你的資料,如果你不明白上圖中的細節,那麼,BBR對於你來講,和CUBIC有區別嗎?哈哈!我依然會保持沉默。---------就像何勇在紅磡演唱會時抹鼻屎到鏡頭後說的話一樣,我說:我不在乎得罪所有人!---------再次宣告,我不在乎高效,我只在乎公平,這是演化論決定的,推薦一本我正在看的書《盲眼鐘錶匠》。
static bool bbr_is_next_cycle_phase(struct sock *sk, const struct rate_sample *rs){ struct tcp_sock *tp = tcp_sk(sk); struct bbr *bbr = inet_csk_ca(sk); bool is_full_length = skb_mstamp_us_delta(&tp->delivered_mstamp, &bbr->cycle_mstamp) > bbr->min_rtt_us; u32 inflight, bw; /* The pacing_gain of 1.0 paces at the estimated bw to try to fully * use the pipe without increasing the queue. */ if (bbr->pacing_gain == BBR_UNIT) return is_full_length; /* just use wall clock time */ inflight = rs->prior_in_flight; /* what was in-flight before ACK? */ bw = bbr_max_bw(sk); /* A pacing_gain > 1.0 probes for bw by trying to raise inflight to at * least pacing_gain*BDP; this may take more than min_rtt if min_rtt is * small (e.g. on a LAN). We do not persist if packets are lost, since * a path with small buffers may not hold that much. */ if (bbr->pacing_gain > BBR_UNIT) return is_full_length && (rs->losses || /* perhaps pacing_gain*BDP won't fit */ inflight >= bbr_target_cwnd(sk, bw, bbr->pacing_gain)); /* A pacing_gain < 1.0 tries to drain extra queue we added if bw * probing didn't find more bw. If inflight falls to match BDP then we * estimate queue is drained; persisting would underutilize the pipe. */ // 如果這裡的“||”換成“&&”,將會如何呢? return is_full_length || inflight <= bbr_target_cwnd(sk, bw, BBR_UNIT);}
就著這個細節,我想來談一下BBR的搶佔性。
BBR的問題在於對頻寬變化的不敏感,所以可以看出在上圖中兩個紅色橢圓處會損失頻寬,一旦有新的空餘頻寬,BBR必須等到ProbeMore的增益週期才能發現,而在此之前,可能新的空餘頻寬已經被別的流搶佔了。我們來看一下為什麼BBR要在ProbeMore增益週期一次性達到最大的可用頻寬,因為BBR在1/4的增益後,新的實際頻寬馬上就可以在下一個RTT反饋回來,簡單點說,BBR的傳送速率多了1/4,在一個RTT後,它會發現根據ACK反饋回來的資料計算出的實際頻寬真的也增加了1/4,只要能持續收到“美夢成真”的反饋,BBR就會一直繼續增速,期待繼續可以“美夢成真”!