天天看點

非常有意思的Flowlet

周五了解到一個叫做Flowlet的東西,比較有意思。大體上說來,它是由一個問題而引出的一個解決方案,由于了解還不夠深入,是以也暫時隻能這麼說。

  我先從問題說起。

ISP的動态負載均衡

由于公共骨幹網上流量的不确定性,每一條鍊路的負載不盡相同,為了保證總帶寬的最佳使用率,ISP往往會做一些動态負載均衡的政策。如下圖所示:

  • 時刻1:
    非常有意思的Flowlet
  • 時刻2:
    非常有意思的Flowlet

packet負載均衡和flow負載均衡

到底是按照packet做負載均衡呢還是按照一個五元組flow來做負載均衡呢?這是一個問題,很多人在做負載均衡的時候都會面臨這個選擇問題。

  具體來講,如果一條flow是強序的,那麼基于packet的負載均衡将會導緻亂序,這是裝置在做負載均衡時要避免的,比如TCP就不能基于packet來做負載均衡,而對于UDP這種協定便可以。

  目前從主機到中間裝置,幾乎所有的從闆卡,網卡隊列,到CPU中斷,到hash算法,均有機制保證TCP的強序性。

TCP的問題

正是由于TCP是強序的,是以TCP便無法基于packet做負載均衡,也就意味着,隻要一條TCP流已經發起了,它就幾乎不能再改變底層鍊路了,至少是最好不要改變底層鍊路,因為一旦底層鍊路改變,TCP将增加面臨亂序的機率。

幸虧TCP是burst發送

前面我的描述其實隐含了一個假設,即TCP flow的packet是平滑發送的:

非常有意思的Flowlet

然而實際上,不管是實際抓包(你可以觀測抓包的tcptrace圖)還是從具體實作(你可以看30年内任何TCP實作的源碼,比如cubic,vegas…)上看,你會發現TCP packet的發送其實是burst的:

非常有意思的Flowlet

哈哈,可乘之機!看到兩批次burst之間的時間間隙沒有,在這種間隙足夠大的時候切換下層鍊路是一個好的時機。這意味着舊鍊路上的packet均已經離開了鍊路或者至少将要離開鍊路,這個時候切換鍊路将不會造成亂序,不會破壞TCP的強序要求。

  嗯,這就是Flowlet。

Flowlet

一般而言,英文中的“-let”字尾代表“微小”的意思,比如booklet,houselet,以及Java applet…一個Flowlet代表的就是一條小流。如下圖所示:

非常有意思的Flowlet

在宏觀的觀感上,可以把一條flow看成是多個flowlet組成的,負載均衡現在就是基于flowlet來進行了,好吧,又引入了一個中間層,它既不是packet,也不是flow,而是大于packet小于flow的flowlet,哈哈,很有意思!

  那麼到底如何定量的去切分flowlet呢?這裡給出一個公式:

已知兩條鍊路的延遲分别為D1,D2,設一個數α,當α滿足下面的條件: 已 知 兩 條 鍊 路 的 延 遲 分 别 為 D 1 , D 2 , 設 一 個 數 α , 當 α 滿 足 下 面 的 條 件 :

α>|D1−D2| α > | D 1 − D 2 |

一條flow便可以通過α來切割為不同的flowlet 一 條 f l o w 便 可 以 通 過 α 來 切 割 為 不 同 的 f l o w l e t

α α 建議 50ms 50 m s 應該不錯了。

BBR之後一切将不再

我個人覺得flowlet的理念非常Q,perfect,通過一個新的層次解決了強序flow的負載均衡問題。然而它借用了一個TCP實作上的問題或者說是bug,即burst機制。所謂借着壞事幹好事。

  在TCP的AIMD模型下,pacing發送幾乎是不可能的,因為pacing的計算會破壞AIMD的盲決策,最終的控制模型将會畸變。然而近期Google的研究證明簡單的AIMD用于TCP傳輸其實是錯誤的,而引入了一中新的BBR pacing傳輸模型,這個BBR一下子把TCP擁塞控制算法引入了2.0時代!

  我想表達什麼?

  在BBR pacing模型下,我們假設BBR已經完美更新到了它應該成為的樣子,解決了一系列的失速,誤判等問題,屆時TCP packet的發送将會是下面的樣子:

非常有意思的Flowlet

你可能再也捕捉不到那個flowlet中的 α α 了,因為pacing rate的精确計算機制不會允許 α α 那麼久的空窗期存在!

  但BBR最終至多隻是終結了flowlet在TCP上的具體實作,它無法終結flowlet的理念。擁塞控制和負載均衡是兩個不同的領域,雖然有所關聯但卻井水不犯河水,擁塞控制說的是,當發現擁塞,要怎麼做,負載均衡說的是,它可以幫忙分擔擁塞。

Linux RFS中的影子

上周寫的那篇:

合并N個有序連結清單與FQ公平排程:https://blog.csdn.net/dog250/article/details/80234049

我找到了一道面試題或者說作業題在Linux中的影子,在我第一次聽聞flowlet的昨天,我想Linux RPS/RFS也有該理念的實作,具體看下面這段代碼:

/*
 * If the desired CPU (where last recvmsg was done) is
 * different from current CPU (one in the rx-queue flow
 * table entry), switch if one of the following holds:
 * ...
 *   - The current CPU's queue tail has advanced beyond the
 *     last packet that was enqueued using this table entry.
 *     This guarantees that all previous packets for the flow
 *     have been dequeued, thus preserving in order delivery.
 */
if (unlikely(tcpu != next_cpu) &&
    (...
     ((int)(per_cpu(softnet_data, tcpu).input_queue_head -
      rflow->last_qtail)) >= )) {
    tcpu = next_cpu;
    rflow = set_rps_cpu(dev, skb, rflow, next_cpu);
}
           

後記

非常感激總是有人幫助我讓我重新思考技術的本質!今天周六一覺睡到8點半,是以這篇文章到了10點多才分享出來,遲來了,但總比沒有好。

  對flowlet了解不深,不到一日而已,如果有需要讨論的,或者說發現我的說法有錯誤的,希望能及時指出,我會及時回複。

  上午積累了很多的家務事,申請了延後執行,這快到中午了,估計也幹不完了,瘋子大人和小小馬上也就回來了,等待接受批評,但能總結出這篇文章,也算欣慰了。