1. 程式人生 > >可以將TCP BBR演算法模組化到低版本核心取代銳速嗎

可以將TCP BBR演算法模組化到低版本核心取代銳速嗎

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                上週的文章引發了比較火爆的爭論並帶來了爭議,我比較滿意或者遺憾,儘管如此,如果有人真的能明白在文章的背後我真正想表達的意思,我也就深感欣慰了。還像往常一樣,我花週末的時間來總結技術,寫點技術散文,同時我希望能在技術上引發同樣的爭論。
        在跟溫州皮鞋廠老闆聊天時,老闆讓我從非技術角度重新思考了Google的BBR演算法。
        很多測試似乎表明BBR的表現非常不錯,雖不能保證包打天下,至少相比銳速而言,它是免費的啊,那麼疑問也就隨之而來了,既然BBR是免費的,且效果不錯,那麼那些搞單邊加速的產品還有意義嗎?畢竟單邊加速的空間有天花板,且賣的還真不便宜。
        從技術上講,商業化的單邊加速產品確實不再需要了,用BBR差不多就行了,但是單邊加速產品相比BBR有一個優勢,那就是不需要改動核心,基本都是模組化的,都可以online一鍵安裝,這是BBR做不到的。
        網上很多人都給出了BBR的升級方案,越做越簡單,基本都將下載核心,編譯核心,安裝核心,啟用BBR封裝成了一個指令碼,一鍵安裝。但是目前還沒有人能做到不改核心,僅僅用一個低版本核心的模組就能安裝BBR演算法,哪怕一個閹割版都沒有。我是不是又想做第一人?
        這個先不論。現在的問題是,難道企業使用者不能升級核心到4.9+嗎?溫州老闆分析了原因。
        其實很容易理解,企業使用者的OS都是依託某個發行版的,比如RH,Suse,至少也要是個Debian吧,無論是廠商的技術支援,還是足夠的社群資源,企業使用者都是需要的,企業使用者一般不會直接折騰核心,他們沒有精力或者沒有能力去升級核心,另外,升級到最新版的核心意味著你要獨自承擔技術風險,然而企業並不缺錢,所以寧願花錢買現成的產品,也不想自己折騰核心。
        題目中說的BBR是否可以代替銳速,非技術層面的已經說過了,答案很顯然,不能替代。那麼從技術上來講呢?先說答案吧,很難。我來簡單說一下難點在哪,然後才可以討論如何突破。
        要想在任意版本上使用BBR,就需要將BBR在任何核心版本上模組化,以2.6.32和3.10為例,其實這兩個版本對於TCP擁塞控制這個子系統而言差別並不大,但是為了支援BBR,Linux 4.9版本的核心對基礎設施做了比較大的改動。主要涉及三個方面。

1.罪與罰框架的重構

這個我就不再說了,已經說的夠多了。總結成一句話就是,4.9之前擁塞模組可以在cong_avoid回撥中犯罪,然後在PRR中被懲罰,在4.9之後,cong_control統一接管了罪與罰。

2.即時速率的測量和採集

問個問題,如何在一個TCP連線中,精確測量該連線的即時速率?

        要回答這個問題,先要知道什麼是即時速率。如果你用wget或者curl下載一個檔案,進度條後面不斷變化的那個速率是即時速率嗎?並不是。雖然那個速率看起來比較“即時”,但實際上只是把時間區間的一端不斷推進的平均速率而已,計算方法不外乎“當前已經傳輸的資料大小”除以“連線開始到當前的時間”此類。那麼到底什麼才是即時速率呢?

        即時速率的計算由ACK時鐘來驅動,它也是一個分子除以分母的商,分子依然是資料量,分母依然是時間段,其中分子的定義如下:從當前ACK確認的資料發出時到現在為止一共被確認的資料量;而分母的定義則是:從當前ACK確認的資料被髮出時到現在的時間差。具體的測量手段可以用下圖表示,為了清晰可見,下圖沒有包括SACK和重傳的情形,但實際上,重傳的資料段和被SACK的資料段也是要包含在內的:




在4.9之前,沒有任何辦法採集到這個即時速率的。換句話說,BBR需要在每收到一個ACK的時候都能計算此時的即時速率,但是在4.9核心之前是做不到的。為什麼這麼說呢?
即時速率和TCP的傳輸速率是有區別的,舉個例子來講,1秒內成功傳送10個一模一樣的大小為2000位元組資料包,那麼TCP的傳輸速率就是2000B/s,而即時速率則是20000B/s,差了10倍。
        也就是說,即時速率度量的是網路狀態,即網路管道有多大的傳輸能力,而TCP傳輸速率則度量的是TCP協議本身的傳輸能力,與網路無關。
        BBR計算即時速率,是把重傳的資料包也算進去的,只要是傳送出去的資料包,不管是新傳輸的,還是重傳的,都會納入計數。所以說,BBR會在傳送每一個數據包的時候,在該資料包上打上當時的傳輸快照,等到該資料包被ACK或者SACK的時候,用當前的快照與資料包傳送時的快照作差,即時速率就算出來了。因此,每收到一個ACK,都可以這麼計算一下,因此這個計算出來的速率是光滑前移的,計算完全靠ACK來驅動。BBR嚴重依賴連線中這個即時速率序列的全量值,而不是取樣值,所以說這個即時速率計算設施是非常重要的。
        每收到一個ACK時,都能拿到被這個ACK確認的資料包在發出的時候是什麼情形,然而在老版本的核心,以3.10為例,這是不可能做到的。之前的核心只能自己記錄時間點,然後在兩個時間點之間數被確認的資料包的數量,而不能僅僅靠ACK時鐘流來驅動計算。這種計算方式相比平滑的BBR計算方式粒度難免太粗了,以多長的時間間隔計算合適呢?100ms?200ms?1s?對於rtt是800ms的連線來講,100ms可能是合適的,但rtt是80ms的連線,100ms又顯得太長了...這就是問題。
        這個問題被我認為是移植BBR到低版本的大障礙。但我覺得既然認清了問題的根源,至少這是可以忍受的,遲到總比不來好。既然我不能修改核心為TCP增加任何基礎設施,我也只能用稍微蹩腳的方式來模擬了。模擬真的好嗎?

3.CA rtt與Seq rtt

既然在速率的計算上存在即時速率與TCP傳輸速率的差別,分別反應網路狀況和TCP本身的狀況,那麼對於RTT是不是也有類似的差別呢?答案顯然是肯定的。
        為了避免再次長篇大論,我先列一下3.10核心在採集RTT時採用的辦法。通過tcp_clean_rtx_queue函式,很容易看到以下的邏輯:
s32 seq_rtt = -1;s32 ca_seq_rtt = -1;while ((skb = tcp_write_queue_head(sk)) && skb != tcp_send_head(sk)) {    struct tcp_skb_cb *scb = TCP_SKB_CB(skb);    ca_seq_rtt = now - scb->when;    if (seq_rtt < 0) {        seq_rtt = ca_seq_rtt;    }}if (flag & FLAG_ACKED) {    tcp_ack_update_rtt(sk, flag, seq_rtt);        ca_ops->pkts_acked(sk, pkts_acked, ca_seq_rtt);}

你會看到,有兩個rtt,分別是seq_rtt和ca_seq_rtt,它們是什麼呢?其實它們分別的意義就是Seq TCP和CA rtt:
CA rtt:一個ACK確認的最後一個數據段的RTT。一般表示網路上真實測的的RTT,由以下三部分組成:傳輸延時,排隊延時,處理延時。
Seq rtt:一個ACK確認的第一個資料段的RTT。受到TCP Delay ACK的影響,因此會引入接收端的Delay延時,由以下四部分組成:傳輸延時,排隊延時,處理延時,Delay延時。
        你會發現,CA rtt主要注入給擁塞控制模組的pkts_acked回撥,供擁塞演算法來使用以及計算與RACK相關的定時器超時時間,,這也正合擁塞控制之本意,擁塞控制本來就是關注網路狀況的,它不會關心TCP協議本身。而Seq rtt則主要用於計算RTO。
        現在回到BBR向低版本移植的話題。
        以3.10為例,該版本的核心僅僅將CA rtt匯出給了pkts_acked回撥,而我們知道BBR是自己維護內部狀態,不受TCP本身的擁塞狀態轉換的影響,也就是說即便在Recovery狀態也會無條件按照自己的內部邏輯計算即時速率,該計算過程以CA rtt作為視窗,一旦越界便會進入PROBE RTT狀態,所以說CA rtt的採集是必須的,即便在一個ACK並沒有推進UNA的情況下(這意味著它只是攜帶了SACK或者在不支援SACK的情況下僅僅是一個重複的ACK),也是需要CA rtt的。
        可惜,3.10版本的核心並無次基礎設施無條件採集CA rtt。如果一個ACK沒有推進UNA,即沒有順序確認新資料,那麼CA rtt便不會被採集。這是移植的一大難點。我們來看一下4.9版本的核心是怎麼做到的,同樣是上述程式碼邏輯,4.9版本是這麼寫的:
s32 seq_rtt = -1;s32 ca_seq_rtt = -1;while ((skb = tcp_write_queue_head(sk)) && skb != tcp_send_head(sk)) {    struct tcp_skb_cb *scb = TCP_SKB_CB(skb);    ca_seq_rtt = now - scb->when;    if (seq_rtt < 0) {        seq_rtt = ca_seq_rtt;    }}// 我修改了命名,但無傷本質。if (likely(first_ackt.v64) && !(flag & FLAG_RETRANS_DATA_ACKED)) {    seq_rtt = skb_mstamp_us_delta(now, &first_ackt);    ca_rtt = skb_mstamp_us_delta(now, &last_ackt);}// 即便在只有SACK的情況下,也會更新CA rttif (sack->first_sackt.v64) {    sack_rtt = skb_mstamp_us_delta(now, &sack->first_sackt);    ca_rtt = skb_mstamp_us_delta(now, &sack->last_sackt);}// CA rtt匯出給呼叫者,進而注入擁塞控制模組sack->rate->rtt_us = ca_rtt; /* RTT of last (S)ACKed packet, or -1 */// 即便是沒有推進UNA,也會用seq_rtt作為測量的Seq rtt樣本進行移動指數平均。// 這就是說,即使在Recovery等“異常”狀態,Seq rtt(就是傳統意義上的rtt)也是會被更新的。rtt_update = tcp_ack_update_rtt(sk, flag, seq_rtt, sack_rtt, ca_rtt);if (flag & FLAG_ACKED) {    ca_ops->pkts_acked(sk, pkts_acked, ca_seq_rtt);}
......
由於以上3點涉及到針對TCP擁塞控制基礎設施比較大的重構和改進,所以說移植BBR演算法到低版本是一件幾乎不可能的事情,各CDN廠商又沒有足夠的動力(其實是因為擁有太多的阻力)去更新自己的核心到最新的4.9,4.10,所以說短時間內,BBR無法大規模用於國內CDN。
        當然,很多人聲稱自己已經在公網以及資料中心間測試了BBR,但這實屬小眾群體。要麼你是自己玩,這種情況怎麼折騰都行,要麼就是你有足夠的自由度定製自己產品的核心版本,這也無話可說。試想大多數使用系統廠商提供的RH企業版的使用者,怎麼可能隨意更新自己的核心並繼續獲得廠商的技術支援呢?以RH為例,它自身就有成百上千個Patch,即便你更新了核心到4.9,那你至少必須將這成百上千個Patch也一併打上,然而這幾乎是不可能的,RH公司不會為你做針對4.9核心的對應Patch。
         那該怎麼辦?如果非要移植,又不能改核心,那該怎麼辦?溫州老闆說可以使用Netfiter,我說太麻煩,工作量也不小。或者說使用jprobe機制鉤住tcp_v4_rcv,然後在其pre handler中接管整個處理邏輯,將其換成4.9核心的邏輯,涉及到資料結構的修改就需要自己malloc,在pre handler處理完畢之前,將skb的pkt_type改成PACKET_OTHERHOST,這樣就可以保證在jprobe_return之後進入原始tcp_v4_rcv後,在第一時間返回,從而做到完全接管TCP收包處理邏輯。
        看似不錯的選擇,但卻不能大規模使用,我一直覺得這只是一種Hack手段...
        另外,我認為,千萬不要移植一個實現了“一半”的BBR演算法。比如你會說,既然無法測量即時速率,那就自己設定timer自己測量平均速率來模擬,既然無法採到CA rtt,那就是用Seq rtt來近似...我之前也是這麼打算的,但是後來就認輸了,千萬不要這樣,因為BBR之所以可行,完全就是依賴與其精準的測量。

依賴於精準的測量

曾經的Reno時代,TCP依靠數學模型來收斂到平衡,現在BBR依靠精準的測量來收斂到平衡,孰好孰壞?
        來做一個類比,這兩者的差別不會大於推匯出的模型與資料訓練出的模型之間的差異。一個典型的例子就是吳軍在《 智慧時代》裡關於人工智慧的闡述。在上世紀50,60年代,人們普遍相信可以通過“演算法”來讓計算機獲得智慧。然後在經歷了持續的失敗後,人們把目光集中在資料上。
        既然已經有了足夠多的全量資料-而不是取樣資料,既然這些資料都是實實在在真實的,那麼這些資料表現出的模式一定就是真實的,問題是如何提煉出這些模式。在這個大資料的背景下,一開始沒有什麼公式,也沒有什麼演算法,有的只有資料,前提是資料必須是真實的。
        那麼如果資料是不真實的,會怎樣?後果會很嚴重,會把計算引入歧途,最終的模型也會與真實的情況大相徑庭。
        BBR如今也不再依賴既有的“數學公式”,比如它不是從一個AIMD控制論模型開始的,而是完全基於一系列的精確測量,測量即時頻寬,測量CA rtt,測量Seq rtt...然後根據一個非常簡單的網路管道模型,基於“最大實測頻寬”與“最小實測CA rtt”來計算“不排隊時”的速率和BDP,這個過程本身就是收斂的,關鍵點在於“不排隊”,只要不主動佔用佇列,連線就不會崩潰,所有的連線就能公平共享頻寬,這正是TCP收斂的重要目標。
        所以說,對於想優化BRR的來講,不要隨意去修改那些引數值。你會發現,稍微修改一點,結果就會謬之千里。

結論:

不修改核心,在低版本核心模組化BBR演算法沒有意義。

PS:這本《智慧時代》已經寄到了上海送給了溫州皮鞋廠老闆。我大致評價幾句,這本書前面的部分寫的比較精彩,後面就越來越拖沓冗餘了。

補遺:

胡老師(blog:http://blog.csdn.net/hu_zhenghui):-)看了本文後,覺得有些地方不妥,比較有道理,本來想直接改正文的,後來覺得這樣可能湮沒我們這次簡單交流的思路,所以就單獨寫一個小節了。
        在文中,我提到企業級使用者,然而如今企業級系統的瓶頸極少會出現在TCP這個環節,優化網路的往往都是網際網路公司。也就是說,企業級系統並非本文描述的TCP單邊加速的顯著應用場景。
        企業系統優化包含多方面的內容,直接瓶頸往往不在網路。那麼CDN服務呢?只能說之前的瓶頸在網路,但是隨著基礎設施的完善,目前對CDN服務要求ACL較多,ACL的瓶頸比較明顯。
        其實我覺得“隨著基礎設施的完善”的意思是說,各CDN服務提供商要相信底層提供的基礎設施,把軍備競賽的精力更多的放在排程,回源優化,路由(指的是應用層的概念,非IP路由)等方面,直接修改基礎設施的收益將會非常有限。另外,對於網際網路企業,所有的機器,OS,協議棧全部都是自己的,那麼優化基礎設施就顯得比較重要了。對於TCP而言,這裡還有一個單邊還是雙邊的問題,之前跟華為的人交流,華為這種公司認為單邊加速的收益非常有限,真正的紅利在雙邊優化,然而能像華為這樣做的了協議棧層級雙邊優化的公司(其端,管,雲戰略)又有幾家呢?
        不過對於非協議棧層面的雙邊優化,有客戶端或者瀏覽器的公司都可以做到,比如Google,BAT等公司都可以。以騰訊的QQ瀏覽器為例,如果你訪問非騰訊的資源,那麼它就是一個普通的瀏覽器,但是如果你訪問騰訊的資源,那麼是不是可以封裝成UDT流進行互動呢?當然,我不知道QQ瀏覽器的細節,但這幾乎是一個很常規的思路。

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述